/********************************************/
/*USB Code                                  */
/*PIC32MX795F512L-80I/PF                    */
/*PIC32MX695F512L-80I/PF                    */
/********************************************/
/*XC32 version                              */
/********************************************/

#include <sys/kmem.h>
#include <sys/attribs.h>
#include <string.h>

#include "MainBrain.h"

#define _DTS_CHECKING_ENABLED 0x08
#define USBAdvancePingPongBuffer(buffer) ((BYTE_VAL*)buffer)->Val ^= 8;
#define USBHALPingPongSetToOdd(buffer)   {((BYTE_VAL*)buffer)->Val |= 8;}
#define USBHALPingPongSetToEven(buffer)  {((BYTE_VAL*)buffer)->Val &= ~8;}

#define USB_MAJOR_VER   1       // Firmware version, major release number.
#define USB_MINOR_VER   8       // Firmware version, minor release number.
#define USB_DOT_VER     0       // Firmware version, dot release number.

volatile int USBDeviceState;
volatile uint8_t USBActiveConfiguration;
volatile uint8_t USBAlternateInterface[1];
volatile BDT_ENTRY *pBDTEntryEP0OutCurrent;
volatile BDT_ENTRY *pBDTEntryEP0OutNext;
volatile BDT_ENTRY *pBDTEntryOut[2];
volatile BDT_ENTRY *pBDTEntryIn[2];
volatile uint8_t shortPacketStatus;
volatile uint8_t controlTransferState;
volatile IN_PIPE inPipes[1];
volatile OUT_PIPE outPipes[1];
volatile uint8_t *pDst;
volatile bool RemoteWakeup;
volatile bool USBBusIsSuspended;
volatile USTAT_FIELDS USTATcopy;
volatile uint8_t endpoint_number;
volatile bool BothEP0OutUOWNsSet;
volatile EP_STATUS ep_data_in[2];
volatile EP_STATUS ep_data_out[2];
volatile bool USBDeferStatusStagePacket;
volatile bool USBStatusStageEnabledFlag1;
volatile bool USBStatusStageEnabledFlag2;
volatile bool USBDeferINDataStagePackets;
volatile bool USBDeferOUTDataStagePackets;
volatile BDT_ENTRY BDT[8] __attribute__ ((coherent, aligned (512)));
volatile CTRL_TRF_SETUP SetupPkt CTRL_TRF_SETUP_ADDR_TAG;
volatile uint8_t CtrlTrfData[8] CTRL_TRF_DATA_ADDR_TAG;

extern const USB_DEVICE_DESCRIPTOR device_dsc;
extern const uint8_t *const USB_CD_Ptr[];
extern const uint8_t *const USB_SD_Ptr[];

//event handler
bool USER_USB_CALLBACK_EVENT_HANDLER(int event, void *pdata, uint16_t size);

//Internal Functions
static void USBCtrlEPService(void);
static void USBCtrlTrfSetupHandler(void);
static void USBCtrlTrfInHandler(void);
static void USBCheckStdRequest(void);
static void USBStdGetDscHandler(void);
static void USBCtrlEPServiceComplete(void);
static void USBCtrlTrfTxService(void);
static void USBCtrlTrfRxService(void);
static void USBStdSetCfgHandler(void);
static void USBStdGetStatusHandler(void);
static void USBStdFeatureReqHandler(void);
static void USBCtrlTrfOutHandler(void);
static void USBConfigureEndpoint(uint8_t EPNum, uint8_t direction);

void USB_Init(void)
{
    uint8_t i;

    //Clear USB interrupt
    IEC1CLR = 0x02000000;

    // Clear USB error flags
    U1EIR = 0xff;  
       
    // Clear USB interrupts          
    U1IR = 0xff;

    //Clear the endpoint control registers
    U1EP0 = 0;
    
    U1EP1 = 0;

    U1CNFG1 = 0;
    U1EIE = 0x9F;
    U1IE = 0x9f;
    U1OTGCON &= 0x000F;
    U1OTGCON |= 0;

    //turn on the module
    U1PWRCbits.USBPWR = 1;
    
    //set the base address of the BDT
    int addr = (int)&BDT[0];
    U1BDTP3 = (((uint32_t)KVA_TO_PA(addr)) >> 24);
    U1BDTP2 = (((uint32_t)KVA_TO_PA(addr)) >> 16);
    U1BDTP1 = (((uint32_t)KVA_TO_PA(addr)) >> 8);

    //Clear the BDT
    for(i=0;i<8;i++)
    {
        BDT[i].Val = 0x00;
    }
    
    // Assert reset request to all of the Ping Pong buffer pointers
    U1CONbits.PPBRST = 1;                    

    //Reset address
    U1ADDR = 0x00;                   

    //Enable packet processing
    U1CONbits.PKTDIS = 0;           

    //Stop trying to reset ping pong buffer pointers
    U1CONbits.PPBRST = 0; 

    //Flush any pending transactions
    while(U1IRbits.TRNIF == 1)      
    {
        //clear TRNIF interrupt
        U1IR = 1 << 3;

        //Initialize USB stack software state variables
        inPipes[0].info.Val = 0;
        outPipes[0].info.Val = 0;
        outPipes[0].wCount.Val = 0;
    }

    //Set flags to true, so the USBCtrlEPAllowStatusStage() function knows not to
    //try and arm a status stage, even before the first control transfer starts.
    USBStatusStageEnabledFlag1 = true;  
    USBStatusStageEnabledFlag2 = true;
    
    //Initialize other flags
    USBDeferINDataStagePackets = false;
    USBDeferOUTDataStagePackets = false;
    USBBusIsSuspended = false;

	//Initialize all pBDTEntryIn[] and pBDTEntryOut[]
	//pointers to NULL, so they don't get used inadvertently.  
	for(i = 0; i < 2; i++)
	{
		pBDTEntryIn[i] = 0u;
		pBDTEntryOut[i] = 0u;		
		ep_data_in[i].Val = 0u;
        ep_data_out[i].Val = 0u;
	}

    //Get ready for the first packet
    pBDTEntryIn[0] = (volatile BDT_ENTRY*)&BDT[2];
    
    // Initialize EP0 RX, TX, Handshake
    U1EP0 = 0xd;        
    
	//Load BDT with the address of EP0 OUT buffer - SETUP
    BDT[0].ADR = ((uint32_t)KVA_TO_PA(&SetupPkt));
    BDT[0].CNT = 8;
    BDT[0].STAT.Val = 0x84;

    // Clear active configuration
    USBActiveConfiguration = 0;     

    //Indicate that we are now in the detached state        
    USBDeviceState = DETACHED_STATE;
    
}

void USBDeviceAttach(void)
{
    //if we are in the detached state
    if(USBDeviceState == DETACHED_STATE)
    {
        //Initialize registers to known states.
        U1CON = 0;          

        // Mask all USB interrupts
        U1IE = 0;                                

        //Configure things like: pull ups, full/low-speed mode, 
        //set the ping pong mode, and set internal transceiver
        //SetConfigurationOptions();
        U1CNFG1 = 0;
        U1EIE = 0x9F;
        U1IE = 0x9f;
        U1OTGCON &= 0x000F;
        U1OTGCON |= 0;

        //Enable the USB interrupt 
        //but leave existing interrupts unchanged
        IEC1 = IEC1 | (1 << 25);
       
        // Enable module & attach to bus
        while(!U1CONbits.USBEN)
        {
            U1CONbits.USBEN = 1;
        }

        //moved to the attached state
        USBDeviceState = ATTACHED_STATE;
    }
}

//USB Interrupt Handler
void __ISR(45, IPL7SRS) USBInterrupt(void) 
{
    uint8_t i;
    
    if(USBDeviceState == ATTACHED_STATE)
    {
        /*
         * After enabling the USB module, it takes some time for the
         * voltage on the D+ or D- line to rise high enough to get out
         * of the SE0 condition. The USB Reset interrupt should not be
         * unmasked until the SE0 condition is cleared. This helps
         * prevent the firmware from misinterpreting this unique event
         * as a USB bus reset from the USB host.
         */
        
        // Clear all USB interrupts
        U1IR = 0xff;
        
        // Unmask RESET interrupt
        U1IEbits.URSTIE = 1;
        
        // Unmask IDLE interrupt
        U1IEbits.IDLEIE = 1;  
        
        USBDeviceState = POWERED_STATE;
    }

    /*
     * Task A: Service USB Activity Interrupt
     */
    if(U1OTGIRbits.ACTVIF && U1OTGIEbits.ACTVIE)
    {
        //USBClearInterruptFlag(USBActivityIFReg,USBActivityIFBitNum);
        U1OTGIR = 1 << 4;
        
        //USBWakeFromSuspend();
        USBBusIsSuspended = false;
        U1OTGIEbits.ACTVIE = 0;
        //Bug fix per Microchip must do again!
        U1OTGIR = 1 << 4;

    }

    /*
     * Pointless to continue servicing if the device is in suspend mode.
     */
    if(U1PWRCbits.USUSPEND == 1)
    {
        IFS1 = 0 << 25;
        
        return;
    }

    /*
     * Task B: Service USB Bus Reset Interrupt.
     * When bus reset is received during suspend, ACTVIF will be set first,
     * once the UCONbits.SUSPND is clear, then the URSTIF bit will be asserted.
     * This is why URSTIF is checked after ACTVIF.
     *
     * The USB reset flag is masked when the USB state is in
     * DETACHED_STATE or ATTACHED_STATE, and therefore cannot
     * cause a USB reset event during these two states.
     */
    if(U1IRbits.URSTIF && U1IEbits.URSTIE)
    {
        USB_Init();

        //Re-enable the interrupts since the USB_Init() function will
        //  disable them.  This will do nothing in a polling setup
        //USBUnmaskInterrupts();
        IEC1SET = 0x02000000;
        
        USBDeviceState = DEFAULT_STATE;
        
        U1IR = 1 << 0;
    }

    /*
     * Task C: Service other USB interrupts
     */
    if(U1IRbits.IDLEIF && U1IEbits.IDLEIE)
    { 
        //USBSuspend();
        U1OTGIEbits.ACTVIE = 1; 
        U1IR =  1 << 4;
        USBBusIsSuspended = true;
        U1IR =  1 << 4;
    }

    if(U1IRbits.SOFIF)
    {
        if(U1IEbits.SOFIE)
        {
            
        }    
        U1IR= 1 << 2;
    }

    if(U1IRbits.STALLIF && U1IEbits.STALLIE)
    {
        //USBStallHandler();
        /* v2b fix */
        if(U1EP0bits.EPSTALL == 1)
        {
            // UOWN - if 0, owned by CPU, if 1, owned by SIE
            if((pBDTEntryEP0OutCurrent->STAT.Val == 0x80) && (pBDTEntryIn[0]->STAT.Val == 0x84))
            {
                // Set ep0Bo to stall also
                pBDTEntryEP0OutCurrent->STAT.Val = 0x0c;
            }//end if
            U1EP0bits.EPSTALL = 0;               // Clear stall status
        }

        U1IR = 1 << 7;
    }

    if(U1IRbits.UERRIF && U1IEbits.UERRIE)
    {        
        U1EIR = 0xff;               // This clears UERRIF

        U1IR = 1 << 1;
    }

    /*
     * Pointless to continue servicing if the host has not sent a bus reset.
     * Once bus reset is received, the device transitions into the DEFAULT
     * state and is ready for communication.
     */
    if(USBDeviceState < DEFAULT_STATE)
    {
	    IFS1 = 0 << 25;
        
	    return; 
	}  

    /*
     * Task D:TRNIF
     */
    if(U1IEbits.TRNIE)
    {
        //Drain or deplete the USAT FIFO entries.  If the USB FIFO ever gets full, USB bandwidth
        //utilization can be compromised, and the device won't be able to receive SETUP packets.
	    for(i = 0; i < 4u; i++)	
		{			
		    if(U1IRbits.TRNIF)
		    {
    		    //Save and extract USTAT register info.  Will use this info later.
                USTATcopy.Val = U1STAT;

                endpoint_number = USTATcopy.endpoint_number;
                
                //Clear the TRNIF interrupt by setting the flag bit
                U1IR = 1 << 3;
                
                //Keep track of the hardware ping pong state for endpoints other than EP0
                if(!USTATcopy.direction)
                {
                    ep_data_out[endpoint_number].bits.ping_pong_state ^= 1;
                }   
                else
                {
                    ep_data_in[endpoint_number].bits.ping_pong_state ^= 1;
                }         
                
                //USBCtrlEPService only services transactions over EP0.
                //It ignores all other EP transactions.
                if(!endpoint_number)
                {
                    USBCtrlEPService();
                }
		    }
		    else
            {
                //USTAT FIFO must be empty
		    	break;	
            }
		}
	} 

    IFS1 = 0 << 25;
}

void USBEnableEndpoint(uint8_t ep, uint8_t options)
{
    unsigned char* p;
        
    //Use USBConfigureEndpoint() to set up the pBDTEntryIn/Out[ep] pointer and 
    //starting DTS state in the BDT entry.
    if(options & 0x08)
    {
        USBConfigureEndpoint(ep, OUT_FROM_HOST);
    }
    if(options & 0x04)
    {
        USBConfigureEndpoint(ep, IN_TO_HOST);
    }

    //Update the relevant UEPx register to actually enable the endpoint with
    //the specified options (ex: handshaking enabled, control transfers allowed,
    //etc.)
    p = (unsigned char*)(&U1EP0+(4*ep));
    *p = options;
}

void* USBTransferOnePacket(uint8_t ep,uint8_t dir,uint8_t* data,uint8_t len)
{
    volatile BDT_ENTRY* handle;

    //If the direction is IN
    if(dir != 0)
    {
        //point to the IN BDT of the specified endpoint
        handle = pBDTEntryIn[ep];
    }
    else
    {
        //else point to the OUT BDT of the specified endpoint
        handle = pBDTEntryOut[ep];
    }
    
    //Error checking code.  Make sure the handle (pBDTEntryIn[ep] or
    //pBDTEntryOut[ep]) is initialized before using it.
    if(handle == 0)
    {
	    return 0;
	}

    //Set the data pointer, data length, and enable the endpoint
    handle->ADR = ConvertToPhysicalAddress(data);
    handle->CNT = len;
    handle->STAT.Val &= 0x40;
    handle->STAT.Val |= 0x88;

    //Point to the next buffer for ping pong purposes.
    if(dir != OUT_FROM_HOST)
    {
        //toggle over the to the next buffer for an IN endpoint
        USBAdvancePingPongBuffer(&pBDTEntryIn[ep]);      
    }
    else
    {
        //toggle over the to the next buffer for an OUT endpoint
        USBAdvancePingPongBuffer(&pBDTEntryOut[ep]);     
    }
    return (void*)handle;
}

void USBStallEndpoint(uint8_t ep, uint8_t dir)
{
    BDT_ENTRY *p;

    if(ep == 0)
    {
        //For control endpoints (ex: EP0), we need to STALL both IN and OUT
        //endpoints.  EP0 OUT must also be prepared to receive the next SETUP 
        //packet that will arrrive.
        pBDTEntryEP0OutNext->CNT = 8;
        pBDTEntryEP0OutNext->ADR = ConvertToPhysicalAddress(&SetupPkt);
        pBDTEntryEP0OutNext->STAT.Val = 0x8c;
        pBDTEntryIn[0]->STAT.Val = 0x84; 
               
    }
    else
    {
        p = (BDT_ENTRY*)(&BDT[(4*ep+2*dir+0)]);
        p->STAT.Val |= 0x04 | 0x80;
    
        //If the device is in FULL or ALL_BUT_EP0 ping pong modes
        //then stall that entry as well
        #if (USB_PING_PONG_MODE == USB_PING_PONG__FULL_PING_PONG) || (USB_PING_PONG_MODE == USB_PING_PONG__ALL_BUT_EP0)
    
        p = (BDT_ENTRY*)(&BDT[(4*ep+2*dir+1)]);
        p->STAT.Val |= 0x04 | 0x80;
        #endif
    }
}

void USBCancelIO(uint8_t endpoint)
{
    if(U1CONbits.PKTDIS == 1)
    {
    	//The PKTDIS bit is currently set right now.  It is therefore "safe"
    	//to mess with the BDT right now.
    	pBDTEntryIn[endpoint]->Val &= 0x40;	//Makes UOWN = 0 (_UCPU mode).  Deactivates endpoint.  Only sends NAKs.
    	pBDTEntryIn[endpoint]->Val ^= 0x40;	//Toggle the DTS bit.  This packet didn't get sent yet, and the next call to USBTransferOnePacket() will re-toggle the DTS bit back to the original (correct) value.
    	
    	//Need to do additional handling if ping-pong buffering is being used
        #if ((USB_PING_PONG_MODE == USB_PING_PONG__FULL_PING_PONG) || (USB_PING_PONG_MODE == USB_PING_PONG__ALL_BUT_EP0))
        //Point to the next buffer for ping pong purposes.  UOWN getting cleared
        //(either due to SIE clearing it after a transaction, or the firmware
        //clearing it) makes hardware ping pong pointer advance.
        USBAdvancePingPongBuffer(&pBDTEntryIn[endpoint]);       
    
    	pBDTEntryIn[endpoint]->STAT.Val &= 0x40;
    	pBDTEntryIn[endpoint]->STAT.Val ^= 0x40;
        #endif
    }
}

void USBDeviceDetach(void)
{
    //If the interrupt option is selected then the customer is required
    //  to notify the stack when the device is attached or removed from the
    //  bus by calling the USBDeviceAttach() and USBDeviceDetach() functions.
    {
         // Disable module & detach from bus
         U1CON = 0;             

         // Mask all USB interrupts              
         U1IE = 0;          

         //Move to the detached state                  
         USBDeviceState = DETACHED_STATE;
            //return so that we don't go through the rest of 
            //the state machine
         return;
    }
}

void USBCtrlEPAllowStatusStage(void)
{
    //Check and set two flags, prior to actually modifying any BDT entries.
    //This double checking is necessary to make certain that 
    //USBCtrlEPAllowStatusStage() can be called twice simultaneously (ex: once 
    //in main loop context, while simultaneously getting an interrupt which 
    //tries to call USBCtrlEPAllowStatusStage() again, at the same time).
    if(USBStatusStageEnabledFlag1 == false)
    {
        USBStatusStageEnabledFlag1 = true;  
        if(USBStatusStageEnabledFlag2 == false)
        {
            USBStatusStageEnabledFlag2 = true;
        
            //Determine which endpoints (EP0 IN or OUT needs arming for the status
            //stage), based on the type of control transfer currently pending.
            if(controlTransferState == 2)
            {
                pBDTEntryIn[0]->CNT = 0;
                pBDTEntryIn[0]->STAT.Val = 0xc8;        
            }
            else if(controlTransferState == 1)
            {
        		BothEP0OutUOWNsSet = false;	//Indicator flag used in USBCtrlTrfOutHandler()
        
                //This buffer (when ping pong buffering is enabled on EP0 OUT) receives the
                //next SETUP packet.
        		#if((USB_PING_PONG_MODE == USB_PING_PONG__EP0_OUT_ONLY) || (USB_PING_PONG_MODE == USB_PING_PONG__FULL_PING_PONG))
        		pBDTEntryEP0OutCurrent->CNT = 8;
        		pBDTEntryEP0OutCurrent->ADR = ConvertToPhysicalAddress(&SetupPkt);
        		pBDTEntryEP0OutCurrent->STAT.Val = 0x80|0x04; //Prepare endpoint to accept a SETUP transaction
        		BothEP0OutUOWNsSet = true;	//Indicator flag used in USBCtrlTrfOutHandler()
        		#endif
        
                //This EP0 OUT buffer receives the 0-byte OUT status stage packet.
        		pBDTEntryEP0OutNext->CNT = 8;
        		pBDTEntryEP0OutNext->ADR = ConvertToPhysicalAddress(&SetupPkt);
        		pBDTEntryEP0OutNext->STAT.Val = 0x80;           // Note: DTSEN is 0
            }
        }    
    }
}   

void USBCtrlEPAllowDataStage(void)
{
    USBDeferINDataStagePackets = false;
    USBDeferOUTDataStagePackets = false;

    if(controlTransferState == 2) //(<setup><out><out>...<out><in>)
    {
        //Prepare EP0 OUT to receive the first OUT data packet in the data stage sequence.
        pBDTEntryEP0OutNext->CNT = 8;
        pBDTEntryEP0OutNext->ADR = ConvertToPhysicalAddress(&CtrlTrfData);
        pBDTEntryEP0OutNext->STAT.Val = 0xc8;
    }   
    else    //else must be controlTransferState == 1 (<setup><in><in>...<in><out>)
    {
        //Error check the data stage byte count.  Make sure the user specified
        //value was no greater than the number of bytes the host requested.
		if(SetupPkt.wLength < inPipes[0].wCount.Val)
		{
			inPipes[0].wCount.Val = SetupPkt.wLength;
		}
		USBCtrlTrfTxService();  //Copies one IN data packet worth of data from application buffer
		                        //to CtrlTrfData buffer.  Also keeps track of how many bytes remaining.

	    //Cnt should have been initialized by responsible request owner (ex: by
	    //using the USBEP0SendRAMPtr() or USBEP0SendROMPtr() API function).
		pBDTEntryIn[0]->ADR = ConvertToPhysicalAddress(&CtrlTrfData);
		pBDTEntryIn[0]->STAT.Val = 0xc8;
    }     
}    

static void USBConfigureEndpoint(uint8_t EPNum, uint8_t direction)
{
    volatile BDT_ENTRY* handle;

    //Compute a pointer to the even BDT entry corresponding to the
    //EPNum and direction values passed to this function.
    handle = (volatile BDT_ENTRY*)&BDT[0]; //Get address of start of BDT
    handle += (4*EPNum+2*direction+0u);  //Add in offset to the BDT of interest
    
    handle->STAT.UOWN = 0;  //mostly redundant, since USBStdSetCfgHandler() 
    //already cleared the entire BDT table

    //Make sure our pBDTEntryIn/Out[] pointer is initialized.  Needed later
    //for USBTransferOnePacket() API calls.
    if(direction == OUT_FROM_HOST)
    {
        pBDTEntryOut[EPNum] = handle;
    }
    else
    {
        pBDTEntryIn[EPNum] = handle;
    }

        handle->STAT.DTS = 0;
        (handle+1)->STAT.DTS = 1;
}

static void USBCtrlEPServiceComplete(void)
{
    /*
     * PKTDIS bit is set when a Setup Transaction is received.
     * Clear to resume packet processing.
     */
    U1CONbits.PKTDIS = 0;

	//Check the busy bits and the SetupPtk.DataDir variables to determine what type of
	//control transfer is currently in progress.  We need to know the type of control
	//transfer that is currently pending, in order to know how to properly arm the 
	//EP0 IN and EP0 OUT endpoints.
    if(inPipes[0].info.bits.busy == 0)
    {
        if(outPipes[0].info.bits.busy == 1)
        {
            controlTransferState = 2;
            /*
             * Control Write:
             * <SETUP[0]><OUT[1]><OUT[0]>...<IN[1]> | <SETUP[0]>
             */

            //1. Prepare OUT EP to receive data, unless a USB class request handler
            //   function decided to defer the data stage (ex: because the intended
            //   RAM buffer wasn't available yet) by calling USBDeferDataStage().
            //   If it did so, it is then responsible for calling USBCtrlEPAllowDataStage(),
            //   once it is ready to begin receiving the data.
            if(USBDeferOUTDataStagePackets == false)
            {
                USBCtrlEPAllowDataStage();
            }
            
            //2.  IN endpoint 0 status stage will be armed by USBCtrlEPAllowStatusStage() 
            //after all of the OUT data has been received and consumed, or if a timeout occurs.
            USBStatusStageEnabledFlag2 = false;
            USBStatusStageEnabledFlag1 = false;
        }
        else
        {
            /*
             * If no one knows how to service this request then stall.
             * Must also prepare EP0 to receive the next SETUP transaction.
             */
            pBDTEntryEP0OutNext->CNT = 8;
            pBDTEntryEP0OutNext->ADR = ConvertToPhysicalAddress(&SetupPkt);
            pBDTEntryEP0OutNext->STAT.Val = 0x8c;
            pBDTEntryIn[0]->STAT.Val = 0x84; 
        }
    }
    else    // A module has claimed ownership of the control transfer session.
    {
		if(SetupPkt.DataDir == USB_SETUP_DEVICE_TO_HOST_BITFIELD)
		{
			controlTransferState = 1;
			/*
			 * Control Read:
			 * <SETUP[0]><IN[1]><IN[0]>...<OUT[1]> | <SETUP[0]>
			 *
			 * 1. Prepare IN EP to transfer data to the host.  If however the data
			 *    wasn't ready yet (ex: because the firmware needs to go and read it from
			 *    some slow/currently unavailable resource, such as an external I2C EEPROM),
			 *    Then the class request handler reponsible should call the USBDeferDataStage()
			 *    macro.  In this case, the firmware may wait up to 500ms, before it is required
			 *    to transmit the first IN data packet.  Once the data is ready, and the firmware
			 *    is ready to begin sending the data, it should then call the 
			 *    USBCtrlEPAllowDataStage() function to start the data stage.
			 */
			if(USBDeferINDataStagePackets == false)
            {
                USBCtrlEPAllowDataStage();
			}

            // 2. (Optionally) allow the status stage now, to prepare for early termination.
            //    Note: If a class request handler decided to set USBDeferStatusStagePacket == true,
            //    then it is responsible for eventually calling USBCtrlEPAllowStatusStage() once it
            //    is ready.  If the class request handler does this, it needs to be careful to
            //    be written so that it can handle the early termination scenario.
            //    Ex: It should call USBCtrlEPAllowStatusStage() when any of the following occurs:
            //    1.  The desired total number of bytes were sent to the host.
            //    2.  The number of bytes that the host originally requested (in the SETUP packet that 
            //        started the control transfer) has been reached.
            //    3.  Or, if a timeout occurs (ex: <50ms since the last successful EP0 IN transaction), regardless 
            //        of how many bytes have actually been sent.  This is necessary to prevent a deadlock situation
            //        (where the control transfer can't complete, due to continuous NAK on status stage) if the
            //        host performs early termination.  If enabled, the USB_ENABLE_STATUS_STAGE_TIMEOUTS usb_config.h
            //        option can take care of this for you.
            //    Note: For this type of control transfer, there is normally no harm in simply arming the
            //    status stage packet right now, even if the IN data is not ready yet.  This allows for
            //    immediate early termination, without adding unecessary delay.  Therefore, it is generally not
            //    recommended for the USB class handler firmware to call USBDeferStatusStage(), for this 
            //    type of control transfer.  If the USB class handler firmware needs more time to fetch the IN
            //    data that needs to be sent to the host, it should instead use the USBDeferDataStage() function.
            USBStatusStageEnabledFlag2 = false;
            USBStatusStageEnabledFlag1 = false;
            if(USBDeferStatusStagePacket == false)
            {
                USBCtrlEPAllowStatusStage();
            } 
		}
		else   // (SetupPkt.DataDir == USB_SETUP_DIRECTION_HOST_TO_DEVICE)
		{
			//This situation occurs for special types of control transfers,
			//such as that which occurs when the host sends a SET_ADDRESS
			//control transfer.  Ex:
			//
			//<SETUP[0]><IN[1]> | <SETUP[0]>
				
			//Although the data direction is HOST_TO_DEVICE, there is no data stage
			//(hence: outPipes[0].info.bits.busy == 0).  There is however still
			//an IN status stage.

			controlTransferState = 2;     //Since this is a HOST_TO_DEVICE control transfer
			
			//1. Prepare OUT EP to receive the next SETUP packet.
			pBDTEntryEP0OutNext->CNT = 8;
			pBDTEntryEP0OutNext->ADR = ConvertToPhysicalAddress(&SetupPkt);
			pBDTEntryEP0OutNext->STAT.Val = 0x80|0x04;
				
			//2. Prepare for IN status stage of the control transfer
            USBStatusStageEnabledFlag2 = false;
            USBStatusStageEnabledFlag1 = false;
			if(USBDeferStatusStagePacket == false)
            {
                USBCtrlEPAllowStatusStage();
            } 
		}
    }
}

static void USBCtrlTrfTxService(void)
{
    uint8_t byteToSend;

    //Figure out how many bytes of data to send in the next IN transaction.
    //Assume a full size packet, unless otherwise determined below.
    byteToSend = 8;         
    if(inPipes[0].wCount.Val < (uint8_t)8)
    {
        byteToSend = inPipes[0].wCount.Val;

        //Keep track of whether or not we have sent a "short packet" yet.
        //This is useful so that later on, we can configure EP0 IN to STALL,
        //after we have sent all of the intended data.  This makes sure the
        //hardware STALLs if the host erroneously tries to send more IN token 
        //packets, requesting more data than intended in the control transfer.
        if(shortPacketStatus == 0)
        {
            shortPacketStatus = 1;
        }
        else if(shortPacketStatus == 1)
        {
            shortPacketStatus = 2;
        }
    }

    //Keep track of how many bytes remain to be sent in the transfer, by
    //subtracting the number of bytes about to be sent from the total.
    inPipes[0].wCount.Val = inPipes[0].wCount.Val - byteToSend;
    
    //Next, load the number of bytes to send to BC7..0 in buffer descriptor.
    //Note: Control endpoints may never have a max packet size of > 64 bytes.
    //Therefore, the BC8 and BC9 bits should always be maintained clear.
    pBDTEntryIn[0]->CNT = byteToSend;

    //Now copy the data from the source location, to the CtrlTrfData[] buffer,
    //which we will send to the host.
    pDst = (volatile uint8_t*)CtrlTrfData;                // Set destination pointer
    if(inPipes[0].info.bits.ctrl_trf_mem == USB_EP0_ROM)   // Determine type of memory source
    {
        while(byteToSend)
        {
            *pDst++ = *inPipes[0].pSrc.bRom++;
            byteToSend--;
        }//end while(byte_to_send.Val)
    }
    else  // RAM
    {
        while(byteToSend)
        {
            *pDst++ = *inPipes[0].pSrc.bRam++;
            byteToSend--;
        }//end while(byte_to_send.Val)
    }//end if(usb_stat.ctrl_trf_mem == _ROM)
}

static void USBCtrlTrfRxService(void)
{
    uint8_t byteToRead;
    uint8_t i;

    //Load byteToRead with the number of bytes the host just sent us in the 
    //last OUT transaction.
    byteToRead = pBDTEntryEP0OutCurrent->CNT;   

    //Update the "outPipes[0].wCount.Val", which keeps track of the total number
    //of remaining bytes expected to be received from the host, in the control
    //transfer.  First check to see if the host sent us more bytes than the
    //application firmware was expecting to receive.
    if(byteToRead > outPipes[0].wCount.Val)
    {
        byteToRead = outPipes[0].wCount.Val;
    }	
    //Reduce the number of remaining bytes by the number we just received.
	outPipes[0].wCount.Val = outPipes[0].wCount.Val - byteToRead;   

    //Copy the OUT DATAx packet bytes that we just received from the host,
    //into the user application buffer space.
    for(i=0;i<byteToRead;i++)
    {
        *outPipes[0].pDst.bRam++ = CtrlTrfData[i];
    }//end while(byteToRead.Val)

    //If there is more data to receive, prepare EP0 OUT so that it can receive 
	//the next packet in the sequence.
    if(outPipes[0].wCount.Val > 0)
    {
        pBDTEntryEP0OutNext->CNT = 8;
        pBDTEntryEP0OutNext->ADR = ConvertToPhysicalAddress(&CtrlTrfData);
        if(pBDTEntryEP0OutCurrent->STAT.DTS == 0)
        {
            pBDTEntryEP0OutNext->STAT.Val = 0xc8;
        }
        else
        {
            pBDTEntryEP0OutNext->STAT.Val = 0x88;
        }
    }
    else
    {
	    //We have received all OUT packets that we were expecting to
	    //receive for the control transfer.  Prepare EP0 OUT to receive
		//the next SETUP transaction that may arrive.
        pBDTEntryEP0OutNext->CNT = 8;
        pBDTEntryEP0OutNext->ADR = ConvertToPhysicalAddress(&SetupPkt);
        //Configure EP0 OUT to receive the next SETUP transaction for any future
        //control transfers.  However, set BSTALL in case the host tries to send
        //more data than it claims it was going to send.
        pBDTEntryEP0OutNext->STAT.Val = 0x80|0x04;

		//All data bytes for the host to device control write (OUT) have now been
		//received successfully.
		//Go ahead and call the user specified callback function, to use/consume
		//the control transfer data (ex: if the "void (*function)" parameter 
		//was non-NULL when USBEP0Receive() was called).
        if(outPipes[0].pFunc != NULL)
        {
            outPipes[0].pFunc();    //Call the user's callback function
        }
        outPipes[0].info.bits.busy = 0;    

        //Ready to arm status stage IN transaction now, if the application
        //firmware has completed processing the request.  If it is still busy
        //and needs more time to finish handling the request, then the user
        //callback (the one called by the outPipes[0].pFunc();) should set the
        //USBDeferStatusStagePacket to true (by calling USBDeferStatusStage()).  In 
        //this case, it is the application's firmware responsibility to call 
        //the USBCtrlEPAllowStatusStage() function, once it is fully done handling the request.
        //Note: The application firmware must process the request and call
        //USBCtrlEPAllowStatusStage() in a semi-timely fashion.  "Semi-timely"
        //means either 50ms, 500ms, or 5 seconds, depending on the type of
        //control transfer.  See the USB 2.0 specification section 9.2.6 for
        //more details.
        if(USBDeferStatusStagePacket == false)
        {
            USBCtrlEPAllowStatusStage();
        }            
    }    

}

static void USBStdSetCfgHandler(void)
{
    // This will generate a zero length packet
    inPipes[0].info.bits.busy = 1;            

    //Clear all of the endpoint control registers
    //DisableNonZeroEndpoints(1);

    uint8_t i;
    uint32_t *p = (uint32_t*)&U1EP1;
    for(i=0;i<1;i++)
    {
        *p = 0;
        p += 4;
    }
    
    //Clear all of the BDT entries
    memset((void*)&BDT[0], 0x00, sizeof(BDT));

    // Assert reset request to all of the Ping Pong buffer pointers
    U1CONbits.PPBRST = 1;                                   

	//Re-Initialize all ping pong software state bits to 0 (which corresponds to
	//the EVEN buffer being the next one that will be used), since we are also 
	//doing a hardware ping pong pointer reset above.
	for(i = 0; i < (uint8_t)(2u); i++)
	{
		ep_data_in[i].Val = 0u;
        ep_data_out[i].Val = 0u;
	}

    //clear the alternate interface settings
    memset((void*)&USBAlternateInterface,0x00,1);

    //Stop trying to reset ping pong buffer pointers
    U1CONbits.PPBRST = 0;

    pBDTEntryIn[0] = (volatile BDT_ENTRY*)&BDT[2];

	//Set the next out to the current out packet
    pBDTEntryEP0OutCurrent = (volatile BDT_ENTRY*)&BDT[0];
    pBDTEntryEP0OutNext = pBDTEntryEP0OutCurrent;

    //set the current configuration
    USBActiveConfiguration = SetupPkt.bConfigurationValue;

    //if the configuration value == 0
    if(USBActiveConfiguration == 0)
    {
        //Go back to the addressed state
        USBDeviceState = ADDRESS_STATE;
    }
    else
    {
        //initialize the required endpoints
        USER_USB_CALLBACK_EVENT_HANDLER(EVENT_CONFIGURED,(void*)&USBActiveConfiguration,1);
        
        //Otherwise go to the configured state.  Update the state variable last,
        //after performing all of the set configuration related initialization
        //tasks.
        USBDeviceState = CONFIGURED_STATE;		
        
    }//end if(SetupPkt.bConfigurationValue == 0)
}

static void USBStdGetDscHandler(void)
{
    if(SetupPkt.bmRequestType == 0x80)
    {
        inPipes[0].info.Val = USB_EP0_ROM | USB_EP0_BUSY | USB_EP0_INCLUDE_ZERO;

        switch(SetupPkt.bDescriptorType)
        {
            case USB_DESCRIPTOR_DEVICE:
                inPipes[0].pSrc.bRom = (const uint8_t*)&device_dsc;
                inPipes[0].wCount.Val = sizeof(device_dsc);
                break;
            case USB_DESCRIPTOR_CONFIGURATION:
                    inPipes[0].pSrc.bRom = *(USB_CD_Ptr+SetupPkt.bDscIndex);

                //This must be loaded using byte addressing.  The source pointer
                //  may not be word aligned for the 16 or 32 bit machines resulting
                //  in an address error on the dereference.
                inPipes[0].wCount.byte.LB = *(inPipes[0].pSrc.bRom+2);
                inPipes[0].wCount.byte.HB = *(inPipes[0].pSrc.bRom+3);
                break;
            case USB_DESCRIPTOR_STRING:
                //USB_NUM_STRING_DESCRIPTORS was introduced as optional in release v2.3.  In v2.4 and
                //  later it is now manditory.  This should be defined in usb_config.h and should
                //  indicate the number of string descriptors.
                if(SetupPkt.bDscIndex<4)
                {
                    //Get a pointer to the String descriptor requested
                    inPipes[0].pSrc.bRom = *(USB_SD_Ptr+SetupPkt.bDscIndex);
                    // Set data count
                    inPipes[0].wCount.Val = *inPipes[0].pSrc.bRom;                    
                }
                else if(SetupPkt.bDscIndex == MICROSOFT_OS_DESCRIPTOR_INDEX)
                {
                    //Get a pointer to the special MS OS string descriptor requested
                    inPipes[0].pSrc.bRom = (const uint8_t*)&MSOSDescriptor;
                    // Set data count
                    inPipes[0].wCount.Val = *inPipes[0].pSrc.bRom;                    
                }    
                else
                {
                    inPipes[0].info.Val = 0;
                }
                break;
            default:
                inPipes[0].info.Val = 0;
                break;
        }
    }
}

static void USBStdGetStatusHandler(void)
{
    CtrlTrfData[0] = 0;                 // Initialize content
    CtrlTrfData[1] = 0;

    switch(SetupPkt.Recipient)
    {
        case USB_SETUP_RECIPIENT_DEVICE_BITFIELD:
            inPipes[0].info.bits.busy = 1;
            /*
             * [0]: bit0: Self-Powered Status [0] Bus-Powered [1] Self-Powered
             *      bit1: RemoteWakeup        [0] Disabled    [1] Enabled
             */
            //Device is Self Powered
            CtrlTrfData[0]|=0x01;

            if(RemoteWakeup == true)
            {
                CtrlTrfData[0]|=0x02;
            }
            break;
        case USB_SETUP_RECIPIENT_INTERFACE_BITFIELD:
            inPipes[0].info.bits.busy = 1;     // No data to update
            break;
        case USB_SETUP_RECIPIENT_ENDPOINT_BITFIELD:
            inPipes[0].info.bits.busy = 1;
            /*
             * [0]: bit0: Halt Status [0] Not Halted [1] Halted
             */
            {
                BDT_ENTRY *p;

                if(SetupPkt.EPDir == 0)
                {
                    p = (BDT_ENTRY*)pBDTEntryOut[SetupPkt.EPNum];
                }
                else
                {
                    p = (BDT_ENTRY*)pBDTEntryIn[SetupPkt.EPNum];
                }

                if((p->STAT.UOWN == 1) && (p->STAT.BSTALL == 1))
                    CtrlTrfData[0]=0x01;    // Set bit0
                break;
            }
    }//end switch

    if(inPipes[0].info.bits.busy == 1)
    {
        inPipes[0].pSrc.bRam = (uint8_t*)&CtrlTrfData;            // Set Source
        inPipes[0].info.bits.ctrl_trf_mem = USB_EP0_RAM;               // Set memory type
        inPipes[0].wCount.v[0] = 2;                         // Set data count
    }//end if(...)
}//end USBStdGetStatusHandler

static void USBCtrlEPService(void)
{
	//Check if the last transaction was on EP0 OUT endpoint (of any kind, to either the even or odd buffer if ping pong buffers used)
    if((USTATcopy.Val & ~0x04) == 0)
    {
		//Point to the EP0 OUT buffer of the buffer that arrived
        pBDTEntryEP0OutCurrent = (volatile BDT_ENTRY*)&BDT[(USTATcopy.Val & 0xfc) >> 2];
        
		//Set the next out to the current out packet
        pBDTEntryEP0OutNext = pBDTEntryEP0OutCurrent;
		//Toggle it to the next ping pong buffer (if applicable)
        ((BYTE_VAL*)&pBDTEntryEP0OutNext)->Val ^= 0x8;

		//If the current EP0 OUT buffer has a SETUP packet
        if(pBDTEntryEP0OutCurrent->STAT.PID == 0xd)
        {
            unsigned char setup_cnt;

	        //The SETUP transaction data may have gone into the the CtrlTrfData 
	        //buffer, or elsewhere, depending upon how the BDT was prepared
	        //before the transaction.  Therefore, we should copy the data to the 
	        //SetupPkt buffer so it can be processed correctly by USBCtrlTrfSetupHandler().		    
            for(setup_cnt = 0; setup_cnt < 8u; setup_cnt++) //SETUP data packets always contain exactly 8 bytes.
            {
                *(uint8_t*)((uint8_t*)&SetupPkt + setup_cnt) = *(uint8_t*)PA_TO_KVA1(pBDTEntryEP0OutCurrent->ADR);
                pBDTEntryEP0OutCurrent->ADR++;
            }    
            pBDTEntryEP0OutCurrent->ADR = ((uint32_t)KVA_TO_PA(&SetupPkt));

			//Handle the control transfer (parse the 8-byte SETUP command and figure out what to do)
            USBCtrlTrfSetupHandler();
        }
        else
        {
			//Handle the DATA transfer
            USBCtrlTrfOutHandler();
        }
    }
    else if((USTATcopy.Val & ~0x04) == 0x8)
    {
		//Otherwise the transmission was and EP0 IN
		//  so take care of the IN transfer
        USBCtrlTrfInHandler();
    }

}

static void USBCtrlTrfSetupHandler(void)
{
    //--------------------------------------------------------------------------
    //1. Re-initialize state tracking variables related to control transfers.
    //--------------------------------------------------------------------------
    shortPacketStatus = 0;  
    USBDeferStatusStagePacket = false;
    USBDeferINDataStagePackets = false;
    USBDeferOUTDataStagePackets = false;
    BothEP0OutUOWNsSet = false;    
    controlTransferState = 0;

    //Abandon any previous control transfers that might have been using EP0.
    //Ordinarily, nothing actually needs abandoning, since the previous control
    //transfer would have completed successfully prior to the host sending the next
    //SETUP packet.  However, in a timeout error case, or after an EP0 STALL event,
    //one or more UOWN bits might still be set.  If so, we should clear the UOWN bits,
    //so the EP0 IN/OUT endpoints are in a known inactive state, ready for re-arming
    //by the class request handler that will be called next.
    pBDTEntryIn[0]->STAT.Val &= ~(0x80);     
    ((BYTE_VAL*)&pBDTEntryIn[0])->Val ^= 0x8;
    pBDTEntryIn[0]->STAT.Val &= ~(0x80);      
    ((BYTE_VAL*)&pBDTEntryIn[0])->Val ^= 0x8;
    pBDTEntryEP0OutNext->STAT.Val &= ~(0x80);         

    inPipes[0].info.Val = 0;
    inPipes[0].wCount.Val = 0;
    outPipes[0].info.Val = 0;
    outPipes[0].wCount.Val = 0;
    

    //--------------------------------------------------------------------------
    //2. Now find out what was in the SETUP packet, and begin handling the request.
    //--------------------------------------------------------------------------
    USBCheckStdRequest();   //Check for standard USB "Chapter 9" requests.
    USER_USB_CALLBACK_EVENT_HANDLER(EVENT_EP0_REQUEST,0,0); //Check for USB device class specific requests


    //--------------------------------------------------------------------------
    //3. Re-arm EP0 IN and EP0 OUT endpoints, based on the control transfer in 
    //   progress.  If one of the above handlers (in step 2) knew how to process
    //   the request, it will have set one of the inPipes[0].info.bits.busy or
    //   outPipes[0].info.bits.busy flags = 1.  This lets the
    //   USBCtrlEPServiceComplete() function know how and which endpoints to 
    //   arm.  If both info.bits.busy flags are = 0, then no one knew how to
    //   process the request.  In this case, the default behavior will be to
    //   perform protocol STALL on EP0.
    //-------------------------------------------------------------------------- 
    USBCtrlEPServiceComplete();
}

static void USBCtrlTrfOutHandler(void)
{
    if(controlTransferState == 2)
    {
        USBCtrlTrfRxService();	//Copies the newly received data into the appropriate buffer and configures EP0 OUT for next transaction.
    }
    else //In this case the last OUT transaction must have been a status stage of a 1 (<setup><in><in>...<OUT>  <-- this last OUT just occurred as the status stage)
    {
        //If the status stage is complete, this means we are done with the 
        //control transfer.  Go back to the idle "WAIT_SETUP" state.
        controlTransferState = 0;

	    //Prepare EP0 OUT for the next SETUP transaction, however, it may have
	    //already been prepared if ping-pong buffering was enabled on EP0 OUT,
	    //and the last control transfer was of direction: device to host, see
	    //USBCtrlEPServiceComplete().  If it was already prepared, do not want
	    //to do anything to the BDT.
		if(BothEP0OutUOWNsSet == false)
		{
	        pBDTEntryEP0OutNext->CNT = 8;
	        pBDTEntryEP0OutNext->ADR = ConvertToPhysicalAddress(&SetupPkt);
	        pBDTEntryEP0OutNext->STAT.Val = 0x8c;			
		}
		else
		{
			BothEP0OutUOWNsSet = false;
		}
    }
}

static void USBCtrlTrfInHandler(void)
{
    uint8_t lastDTS;

    lastDTS = pBDTEntryIn[0]->STAT.DTS;

    //switch to the next ping pong buffer
    ((BYTE_VAL*)&pBDTEntryIn[0])->Val ^= 8;

    //Must check if in ADR_PENDING_STATE.  If so, we need to update the address
    //now, since the IN status stage of the (set address) control transfer has 
    //evidently completed successfully.
    if(USBDeviceState == ADR_PENDING_STATE)
    {
        U1ADDR = SetupPkt.bDevADR.Val;
        if(U1ADDR != 0u)
        {
            USBDeviceState=ADDRESS_STATE;
        }
        else
        {
            USBDeviceState=DEFAULT_STATE;
        }
    }//end if


    if(controlTransferState == 1)
    {
        pBDTEntryIn[0]->ADR = ConvertToPhysicalAddress(CtrlTrfData);
        USBCtrlTrfTxService();

        //Check if we have already sent a short packet.  If so, configure
        //the endpoint to STALL in response to any further IN tokens (in the
        //case that the host erroneously tries to receive more data than it
        //should).
        if(shortPacketStatus == 2)
        {
            // If a short packet has been sent, don't want to send any more,
            // stall next time if host is still trying to read.
            pBDTEntryIn[0]->STAT.Val = 0x80|0x04;
        }
        else
        {
            if(lastDTS == 0)
            {
                pBDTEntryIn[0]->STAT.Val = 0xc8;
            }
            else
            {
                pBDTEntryIn[0]->STAT.Val = 0x88;
            }
        }//end if(...)else
    }
	else // must have been a 2 status stage IN packet (<setup><out><out>...<IN>  <-- this last IN just occurred as the status stage)
	{
        //if someone is still expecting data from the control transfer
        //  then make sure to terminate that request and let them know that
        //  they are done
        if(outPipes[0].info.bits.busy == 1)
        {
            if(outPipes[0].pFunc != NULL)
            {
                outPipes[0].pFunc();
            }
            outPipes[0].info.bits.busy = 0;
        }
    	
        controlTransferState = 0;
        //Don't need to arm EP0 OUT here.  It was already armed by the last <out> that
        //got processed by the USBCtrlTrfRxService() handler.
	}	

}

static void USBCheckStdRequest(void)
{
    if(SetupPkt.RequestType != 0)
    {
        return;
    }

    switch(SetupPkt.bRequest)
    {
        case USB_REQUEST_SET_ADDRESS:
            inPipes[0].info.bits.busy = 1;            // This will generate a zero length packet
            USBDeviceState = ADR_PENDING_STATE;       // Update state only
            /* See USBCtrlTrfInHandler() for the next step */
            break;
        case USB_REQUEST_GET_DESCRIPTOR:
            USBStdGetDscHandler();
            break;
        case USB_REQUEST_SET_CONFIGURATION:
            USBStdSetCfgHandler();
            break;
        case USB_REQUEST_GET_CONFIGURATION:
            inPipes[0].pSrc.bRam = (uint8_t*)&USBActiveConfiguration;         // Set Source
            inPipes[0].info.bits.ctrl_trf_mem = 0x01;               // Set memory type
            inPipes[0].wCount.v[0] = 1;                         // Set data count
            inPipes[0].info.bits.busy = 1;
            break;
        case USB_REQUEST_GET_STATUS:
            USBStdGetStatusHandler();
            break;
        case USB_REQUEST_CLEAR_FEATURE:
        case USB_REQUEST_SET_FEATURE:
            USBStdFeatureReqHandler();
            break;
        case USB_REQUEST_GET_INTERFACE:
            inPipes[0].pSrc.bRam = (uint8_t*)&USBAlternateInterface[SetupPkt.bIntfID];  // Set source
            inPipes[0].info.bits.ctrl_trf_mem = 0x01;               // Set memory type
            inPipes[0].wCount.v[0] = 1;                         // Set data count
            inPipes[0].info.bits.busy = 1;
            break;
        case USB_REQUEST_SET_INTERFACE:
            inPipes[0].info.bits.busy = 1;
            USBAlternateInterface[SetupPkt.bIntfID] = SetupPkt.bAltID;
            break;
        case USB_REQUEST_SET_DESCRIPTOR:
            USER_USB_CALLBACK_EVENT_HANDLER(EVENT_SET_DESCRIPTOR,0,0);
            break;
        case USB_REQUEST_SYNCH_FRAME:
        default:
            break;
    }
}

static void USBStdFeatureReqHandler(void)
{
    BDT_ENTRY *p;
    EP_STATUS current_ep_data;
    uint32_t* pUEP;

    //Check if the host sent a valid SET or CLEAR feature (remote wakeup) request.
    if((SetupPkt.bFeature == 1) && (!SetupPkt.Recipient))
    {
        inPipes[0].info.bits.busy = 1;
        if(SetupPkt.bRequest == 3)
            RemoteWakeup = true;
        else
            RemoteWakeup = false;
    }

    //Check if the host sent a valid SET or CLEAR endpoint halt request.
    if((!SetupPkt.bFeature) && (SetupPkt.Recipient == 2)&&
       (SetupPkt.EPNum != 0) && (SetupPkt.EPNum <= 1) && (USBDeviceState == CONFIGURED_STATE))
    {
		//The request was valid.  Take control of the control transfer and
		//perform the host requested action.
		inPipes[0].info.bits.busy = 1;

        //Fetch a pointer to the BDT that the host wants to SET/CLEAR halt on.
        if(!SetupPkt.EPDir)
        {
            p = (BDT_ENTRY*)pBDTEntryOut[SetupPkt.EPNum];
            current_ep_data.Val = ep_data_out[SetupPkt.EPNum].Val;
        }
        else
        {
            p = (BDT_ENTRY*)pBDTEntryIn[SetupPkt.EPNum];
            current_ep_data.Val = ep_data_in[SetupPkt.EPNum].Val;
        }

        //If ping pong buffering is enabled on the requested endpoint, need 
        //to point to the one that is the active BDT entry which the SIE will 
        //use for the next attempted transaction on that EP number.
            if(current_ep_data.bits.ping_pong_state == 0) //Check if even
            {
                USBHALPingPongSetToEven(&p);
            }
            else //else must have been odd
            {
                USBHALPingPongSetToOdd(&p);
            }
       
        //Update the BDT pointers with the new, next entry based on the feature
        //  request
        if(!SetupPkt.EPDir)
        {
            pBDTEntryOut[SetupPkt.EPNum] = (volatile BDT_ENTRY *)p;
        }
        else
        {
            pBDTEntryIn[SetupPkt.EPNum] = (volatile BDT_ENTRY *)p;
        }

		//Check if it was a SET_FEATURE endpoint halt request
        if(SetupPkt.bRequest == 3)
        {
            if(p->STAT.UOWN == 1)
            {
                //Mark that we are terminating this transfer and that the user
                //  needs to be notified later
                if(!SetupPkt.EPDir)
                {
                    ep_data_out[SetupPkt.EPNum].bits.transfer_terminated = 1;
                }
                else
                {
                    ep_data_in[SetupPkt.EPNum].bits.transfer_terminated = 1;
                }
            }

			//Then STALL the endpoint
            p->STAT.Val |= 0x84;
        }//if(SetupPkt.bRequest == USB_REQUEST_SET_FEATURE)
        else
        {
			//Else the request must have been a CLEAR_FEATURE endpoint halt.
                //toggle over the to the non-active BDT
                USBAdvancePingPongBuffer(&p);  

                if(p->STAT.UOWN == 1)
                {
                    //Clear UOWN and set DTS state so it will be correct the next time
                    //the application firmware uses USBTransferOnePacket() on the EP.
                    p->STAT.Val &= (~0x80);    //Clear UOWN bit
                    p->STAT.Val |= 0x40;       //Set DTS to DATA1
                    USER_USB_CALLBACK_EVENT_HANDLER(EVENT_TRANSFER_TERMINATED,p,sizeof(p));
                }
                else
                {
                    //UOWN already clear, but still need to set DTS to DATA1     
					p->STAT.Val |= 0x40;
                }

                //toggle back to the active BDT (the one the SIE is currently looking at
                //and will use for the next successful transaction to take place on the EP
                USBAdvancePingPongBuffer(&p);    
                
                //Check if we are currently terminating, or have previously terminated
                //a transaction on the given endpoint.  If so, need to clear UOWN,
                //set DTS to the proper state, and call the application callback
                //function.
                if((current_ep_data.bits.transfer_terminated != 0) || (p->STAT.UOWN == 1))
                {
                    if(!SetupPkt.EPDir)
                    {
                        ep_data_out[SetupPkt.EPNum].bits.transfer_terminated = 0;
                    }
                    else
                    {
                        ep_data_in[SetupPkt.EPNum].bits.transfer_terminated = 0;
                    }
                    //clear UOWN, clear DTS to DATA0, and finally remove the STALL condition     
                    p->STAT.Val &= ~(0xc4);  
                    //Call the application event handler callback function, so it can 
					//decide if the endpoint should get re-armed again or not.
                    USER_USB_CALLBACK_EVENT_HANDLER(EVENT_TRANSFER_TERMINATED,p,sizeof(p));
                }
                else
                {
                    //clear UOWN, clear DTS to DATA0, and finally remove the STALL condition     
                    p->STAT.Val &= ~(0xc4); 
                } 
                //else we must not be using ping-pong buffering on the requested endpoint
                //Check if we need to call the user transfer terminated event callback function.
                //We should call the callback, if the endpoint was previously terminated,
                //or the endpoint is currently armed, and the host is performing clear
                //endpoint halt, even though the endpoint wasn't stalled.
                if((current_ep_data.bits.transfer_terminated != 0) || (p->STAT.UOWN == 1))
                {
                    //We are going to call the user transfer terminated callback.
                    //Clear the flag so we know we took care of it and don't need
                    //to call it again later.
                    if(!SetupPkt.EPDir)
                    {
                        ep_data_out[SetupPkt.EPNum].bits.transfer_terminated = 0;
                    }
                    else
                    {
                        ep_data_in[SetupPkt.EPNum].bits.transfer_terminated = 0;
                    }
 
                    //Clear UOWN and remove the STALL condition.   
                    //  In this case we also need to set the DTS bit to 1 so that
                    //  it toggles to DATA0 the next time the application firmware
                    //  calls USBTransferOnePacket() (or equivalent macro).  
                    p->STAT.Val &= ~(0x84);  
                    p->STAT.Val |= 0x40;
                    //Let the application firmware know a transaction just
                    //got terminated by the host, and that it is now free to
                    //re-arm the endpoint or do other tasks if desired.                                        
                    USER_USB_CALLBACK_EVENT_HANDLER(EVENT_TRANSFER_TERMINATED,p,sizeof(p));
                }
                else
                {
                    //Clear UOWN and remove the STALL condition.   
                    //  In this case we also need to set the DTS bit to 1 so that
                    //  it toggles to DATA0 the next time the application firmware
                    //  calls USBTransferOnePacket() (or equivalent macro).  
                    p->STAT.Val &= ~(0x84);  
                    p->STAT.Val |= 0x40;
                } 
            
			//Get a pointer to the appropriate UEPn register
                pUEP = (uint32_t*)(&U1EP0);
                pUEP += (SetupPkt.EPNum*4);

			//Clear the STALL bit in the UEP register
            *pUEP &= ~0x02;            
        }//end if(SetupPkt.bRequest == USB_REQUEST_SET_FEATURE)
    }//end if (lots of checks for set/clear endpoint halt)
}//end USBStdFeatureReqHandler

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/** V A R I A B L E S ********************************************************/
extern volatile CTRL_TRF_SETUP SetupPkt;    //Common buffer that receives the 
                                            //8-byte SETUP packet data from the 
                                            //host during control transfer 
                                            //requests.

void USBCheckVendorRequest(void)
{
        uint16_t Length;

        //Check if the most recent SETUP request is class specific
        if(SetupPkt.bmRequestType == 0b11000000)    //Class specific, device to host, device level target
        {
            //Check if the host is requesting an MS feature descriptor
            if(SetupPkt.bRequest == GET_MS_DESCRIPTOR)
            {
                //Figure out which descriptor is being requested
                if(SetupPkt.wIndex == EXTENDED_COMPAT_ID)
                {
                    //Determine number of bytes to send to host 
                    //Lesser of: requested amount, or total size of the descriptor
                    Length = CompatIDFeatureDescriptor.dwLength;
                    if(SetupPkt.wLength < Length)
                    {
                        Length = SetupPkt.wLength;
                    }    
                         
                    //Prepare to send the requested descriptor to the host
                    //USBEP0SendROMPtr((const uint8_t*)&CompatIDFeatureDescriptor, Length, USB_EP0_ROM | USB_EP0_INCLUDE_ZERO);
                    //(src,size,options)
                    inPipes[0].pSrc.bRom = (const uint8_t*)&CompatIDFeatureDescriptor;
                    inPipes[0].wCount.Val = Length;
                    inPipes[0].info.Val = 0xc0;
                }
            }            
        }//if(SetupPkt.bmRequestType == 0b11000000)    
        else if(SetupPkt.bmRequestType == 0b11000001)    //Class specific, device to host, interface target
        {
            //Check if the host is requesting an MS feature descriptor
            if(SetupPkt.bRequest == GET_MS_DESCRIPTOR)
            {
                //Figure out which descriptor is being requested
                if(SetupPkt.wIndex == EXTENDED_PROPERTIES)    
                {
                    //Determine number of bytes to send to host 
                    //Lesser of: requested amount, or total size of the descriptor
                    Length = ExtPropertyFeatureDescriptor.dwLength;
                    if(SetupPkt.wLength < Length)
                    {
                        Length = SetupPkt.wLength;
                    }    
                         
                    //Prepare to send the requested descriptor to the host
                    inPipes[0].pSrc.bRom = (const uint8_t*)&ExtPropertyFeatureDescriptor;
                    inPipes[0].wCount.Val = Length;
                    inPipes[0].info.Val = 0xc0;

                }    
            }                   
        }//else if(SetupPkt.bmRequestType == 0b11000001)    
}

/* Device Descriptor */
const USB_DEVICE_DESCRIPTOR device_dsc=
{
    0x12,                   // Size of this descriptor in bytes
    0x01,                   // DEVICE descriptor type
    0x0200,                 // USB Spec Release Number in BCD format        
    0x00,                   // Class Code
    0x00,                   // Subclass code
    0x00,                   // Protocol code
    0x08,                   // Max packet size for EP0, see usb_config.h
    0x1209,                 // Vendor ID: 0x04D8 is Microchip's Vendor ID
    0x0001,                 // Product ID: 0x0053
    0x1800,                 // Device release number in BCD format
    0x01,                   // Manufacturer string index
    0x02,                   // Product string index
    0x03,                   // Device serial number string index
    0x01                    // Number of possible configurations
};

/* Configuration 1 Descriptor */
const uint8_t configDescriptor1[]={
    /* Configuration Descriptor */
    0x09,//sizeof(USB_CFG_DSC),    // Size of this descriptor in bytes
    USB_DESCRIPTOR_CONFIGURATION,                // CONFIGURATION descriptor type
    0x20,0x00,            // Total length of data for this cfg
    1,                      // Number of interfaces in this cfg
    1,                      // Index value of this configuration
    0,                      // Configuration string index
    _DEFAULT | _SELF,               // Attributes, see usb_device.h
    50,                     // Max power consumption (2X mA)
							
    /* Interface Descriptor */
    0x09,//sizeof(USB_INTF_DSC),   // Size of this descriptor in bytes
    USB_DESCRIPTOR_INTERFACE,               // INTERFACE descriptor type
    0,                      // Interface Number
    0,                      // Alternate Setting Number
    2,                      // Number of endpoints in this intf
    0xFF,                   // Class code
    0xFF,                   // Subclass code
    0xFF,                   // Protocol code
    0,                      // Interface string index
    
    /* Endpoint Descriptor */
    0x07,                       /*sizeof(USB_EP_DSC)*/
    USB_DESCRIPTOR_ENDPOINT,    //Endpoint Descriptor
    _EP01_OUT,                  //EndpointAddress
    _BULK,                       //Attributes
    64,0x00,        //size
    1,                         //Interval
    
    0x07,                       /*sizeof(USB_EP_DSC)*/
    USB_DESCRIPTOR_ENDPOINT,    //Endpoint Descriptor
    _EP01_IN,                   //EndpointAddress
    _BULK,                       //Attributes
    64,0x00,        //size
    1                          //Interval
};


//Language code string descriptor
const struct{uint8_t bLength;uint8_t bDscType;uint16_t string[1];}sd000={
sizeof(sd000),USB_DESCRIPTOR_STRING,{0x0409}};

//Manufacturer string descriptor
const struct{uint8_t bLength;uint8_t bDscType;uint16_t string[12];}sd001={
sizeof(sd001),USB_DESCRIPTOR_STRING,
{'L','a','r','r','y',' ','K','n','i','g','h','t'}};

//Product string descriptor
const struct{uint8_t bLength;uint8_t bDscType;uint16_t string[20];}sd002={
sizeof(sd002),USB_DESCRIPTOR_STRING,
{'M','a','i','n','B','r','a','i','n',' ','W','i','n','U','S','B',' ','1','.','8'}};

//Serial number string descriptor
const struct{uint8_t bLength;uint8_t bDscType;uint16_t string[3];}sd003={
sizeof(sd003),USB_DESCRIPTOR_STRING,
{'1','8','0'}};

 //Array of configuration descriptors
const uint8_t *const USB_CD_Ptr[]=
{
    (const uint8_t *const)&configDescriptor1
};
//Array of string descriptors
const uint8_t *const USB_SD_Ptr[]=
{
    (const uint8_t *const)&sd000,
    (const uint8_t *const)&sd001,
    (const uint8_t *const)&sd002,
    (const uint8_t *const)&sd003,
};

    const MS_OS_DESCRIPTOR MSOSDescriptor =
    {   
        sizeof(MSOSDescriptor),         //bLength - lenght of this descriptor in bytes
        USB_DESCRIPTOR_STRING,          //bDescriptorType - "string"
        {'M','S','F','T','1','0','0'},  //qwSignature - special values that specifies the OS descriptor spec version that this firmware implements
        GET_MS_DESCRIPTOR,              //bMS_VendorCode - defines the "GET_MS_DESCRIPTOR" bRequest literal value
        0x00                            //bPad - always 0x00
    };
    
    
    //Extended Compat ID OS Feature Descriptor
    const MS_COMPAT_ID_FEATURE_DESC CompatIDFeatureDescriptor =
    {
        //----------Header Section--------------
        sizeof(CompatIDFeatureDescriptor),  //dwLength
        0x0100,                             //bcdVersion = 1.00
        EXTENDED_COMPAT_ID,                 //wIndex
        0x01,                               //bCount - 0x01 "Function Section(s)" implemented in this descriptor
        {0,0,0,0,0,0,0},                    //Reserved[7]
        //----------Function Section 1----------
        0x00,                               //bFirstInterfaceNumber: the WinUSB interface in this firmware is interface #0
        0x01,                               //Reserved - fill this reserved byte with 0x01 according to documentation
        {'W','I','N','U','S','B',0x00,0x00},//compatID - "WINUSB" (with two null terminators to fill all 8 bytes)
        {0,0,0,0,0,0,0,0},                  //subCompatID - eight bytes of 0
        {0,0,0,0,0,0}                       //Reserved
    };    
    
    
    //Extended Properties OS Feature Descriptor
    const MS_EXT_PROPERTY_FEATURE_DESC ExtPropertyFeatureDescriptor =
    {
        //----------Header Section--------------
        sizeof(ExtPropertyFeatureDescriptor),   //dwLength
        0x0100,                                 //bcdVersion = 1.00
        EXTENDED_PROPERTIES,                    //wIndex
        0x0001,                                 //wCount - 0x0001 "Property Sections" implemented in this descriptor
        //----------Property Section 1----------
        132,                                    //dwSize - 132 bytes in this Property Section
        0x00000001,                             //dwPropertyDataType (Unicode string)
        40,                                     //wPropertyNameLength - 40 bytes in the bPropertyName field
        {'D','e','v','i','c','e','I','n','t','e','r','f','a','c','e','G','U','I','D', 0x0000},  //bPropertyName - "DeviceInterfaceGUID"
        78,                                     //dwPropertyDataLength - 78 bytes in the bPropertyData field (GUID value in UNICODE formatted string, with braces and dashes)
        //The below value is the Device Interface GUID (a 128-bit long "globally unique identifier")
        //Please modify the GUID value in your application before moving to production.
        //When you change the GUID, you must also change the PC application software
        //that connects to this device, as the software looks for the device based on 
        //VID, PID, and GUID.  All three values in the PC application must match 
        //the values in this firmware.
        //The GUID value can be a randomly generated 128-bit hexadecimal number, 
        //formatted like the below value.  The actual value is not important,
        //so long as it is almost certain to be globally unique, and matches the
        //PC software that communicates with this firmware.
        {'{','5','8','d','0','7','2','1','0','-','2','7','c','1','-','1','1','d','d','-','b','d','0','b','-','0','8','0','0','2','0','0','c','9','a','6','6','}',0x0000}  //bPropertyData - this is the actual GUID value.  Make sure this matches the PC application code trying to connect to the device.
    };    

void DisplayUSBData(void)
{
    //BDT base address
    char MyBuffer1[5] = {"BDT:"};
    hchar = 10;
    vchar = 20;
    WriteString(hchar, vchar, MyBuffer1, black);
    Binary2ASCIIHex((int)&BDT[0]);
    WriteChar(hchar, vchar, d_hex[7], black);
    WriteChar(hchar, vchar, d_hex[6], black);
    WriteChar(hchar, vchar, d_hex[5], black);
    WriteChar(hchar, vchar, d_hex[4], black);
    WriteChar(hchar, vchar, d_hex[3], black);
    WriteChar(hchar, vchar, d_hex[2], black);
    WriteChar(hchar, vchar, d_hex[1], black);
    WriteChar(hchar, vchar, d_hex[0], black);

    //U1ADDR
    hchar = hchar + 20;
    char MyBuffer2[8] = {"U1ADDR:"};
    WriteString(hchar, vchar, MyBuffer2, black);
    Binary2ASCIIHex(U1ADDR);
    WriteChar(hchar, vchar, d_hex[1], black);
    WriteChar(hchar, vchar, d_hex[0], black);

    //U1OTGIR
    hchar = 10;
    vchar = 45;
    char MyBuffer2a[9] = {"U1OTGIR:"};
    WriteString(hchar, vchar, MyBuffer2a, black);
    Binary2ASCIIHex(U1OTGIR);
    WriteChar(hchar, vchar, d_hex[1], black);
    WriteChar(hchar, vchar, d_hex[0], black);

    //U1OTGIE
    hchar = hchar + 20;
    char MyBuffer2b[9] = {"U1OTGIE:"};
    WriteString(hchar, vchar, MyBuffer2b, black);
    Binary2ASCIIHex(U1OTGIE);
    WriteChar(hchar, vchar, d_hex[1], black);
    WriteChar(hchar, vchar, d_hex[0], black);

    //UICON
    hchar = 10;
    vchar = vchar + 20;
    char MyBuffer3[7] = {"U1CON:"};
    WriteString(hchar, vchar, MyBuffer3, black);
    Binary2ASCIIHex(U1CON);
    WriteChar(hchar, vchar, d_hex[1], black);
    WriteChar(hchar, vchar, d_hex[0], black);

    //U1STAT
    hchar = hchar + 20;
    char MyBuffer4[15] = {"U1STAT:"};
    WriteString(hchar, vchar, MyBuffer4, black);
    Binary2ASCIIHex(USTATcopy.Val);
    WriteChar(hchar, vchar, d_hex[1], black);
    WriteChar(hchar, vchar, d_hex[0], black);

    //U1IE
    hchar = hchar + 20;
    char MyBuffer5[15] = {"U1IE:"};
    WriteString(hchar, vchar, MyBuffer5, black);
    Binary2ASCIIHex(U1IE);
    WriteChar(hchar, vchar, d_hex[1], black);
    WriteChar(hchar, vchar, d_hex[0], black);

    hchar = 10;
    vchar = 100;

    char MyBuffer6[15] = {"U1BDTP3:"};
    WriteString(hchar, vchar, MyBuffer6, black);
    Binary2ASCIIHex(U1BDTP3);
    WriteChar(hchar, vchar, d_hex[7], 0x0);
    WriteChar(hchar, vchar, d_hex[6], 0x0);
    WriteChar(hchar, vchar, d_hex[5], 0x0);
    WriteChar(hchar, vchar, d_hex[4], 0x0);
    WriteChar(hchar, vchar, d_hex[3], 0x0);
    WriteChar(hchar, vchar, d_hex[2], 0x0);
    WriteChar(hchar, vchar, d_hex[1], 0x0);
    WriteChar(hchar, vchar, d_hex[0], 0x0);

    hchar = 10;
    vchar = vchar + 20;
    
    char MyBuffer7[15] = {"U1BDTP2:"};
    WriteString(hchar, vchar, MyBuffer7, black);
    Binary2ASCIIHex(U1BDTP2);
    WriteChar(hchar, vchar, d_hex[7], 0x0);
    WriteChar(hchar, vchar, d_hex[6], 0x0);
    WriteChar(hchar, vchar, d_hex[5], 0x0);
    WriteChar(hchar, vchar, d_hex[4], 0x0);
    WriteChar(hchar, vchar, d_hex[3], 0x0);
    WriteChar(hchar, vchar, d_hex[2], 0x0);
    WriteChar(hchar, vchar, d_hex[1], 0x0);
    WriteChar(hchar, vchar, d_hex[0], 0x0);
    
    hchar = 10;
    vchar = vchar + 20;      

    char MyBuffer8[15] = {"U1BDTP1:"};
    WriteString(hchar, vchar, MyBuffer8, black);
    Binary2ASCIIHex(U1BDTP1);
    WriteChar(hchar, vchar, d_hex[7], 0x0);
    WriteChar(hchar, vchar, d_hex[6], 0x0);
    WriteChar(hchar, vchar, d_hex[5], 0x0);
    WriteChar(hchar, vchar, d_hex[4], 0x0);
    WriteChar(hchar, vchar, d_hex[3], 0x0);
    WriteChar(hchar, vchar, d_hex[2], 0x0);
    WriteChar(hchar, vchar, d_hex[1], 0x0);
    WriteChar(hchar, vchar, d_hex[0], 0x0);

    hchar = 10;
    vchar = 180;
    //EP0-0
    char MyBuffer9[15] = {"EP00:"};
    WriteString(hchar, vchar, MyBuffer9, black);
    Binary2ASCIIHex(BDT[0].ADR);
    WriteChar(hchar, vchar, d_hex[7], 0x0);
    WriteChar(hchar, vchar, d_hex[6], 0x0);
    WriteChar(hchar, vchar, d_hex[5], 0x0);
    WriteChar(hchar, vchar, d_hex[4], 0x0);
    WriteChar(hchar, vchar, d_hex[3], 0x0);
    WriteChar(hchar, vchar, d_hex[2], 0x0);
    WriteChar(hchar, vchar, d_hex[1], 0x0);
    WriteChar(hchar, vchar, d_hex[0], 0x0);

    WriteChar(hchar, vchar, ':', 0x0);

    Binary2ASCIIHex(BDT[0].CNT);
    WriteChar(hchar, vchar, d_hex[7], 0x0);
    WriteChar(hchar, vchar, d_hex[6], 0x0);
    WriteChar(hchar, vchar, d_hex[5], 0x0);
    WriteChar(hchar, vchar, d_hex[4], 0x0);
    WriteChar(hchar, vchar, d_hex[3], 0x0);
    WriteChar(hchar, vchar, d_hex[2], 0x0);
    WriteChar(hchar, vchar, d_hex[1], 0x0);
    WriteChar(hchar, vchar, d_hex[0], 0x0);

    WriteChar(hchar, vchar, ':', 0x0);

    Binary2ASCIIHex(BDT[0].STAT.Val);
    WriteChar(hchar, vchar, d_hex[7], 0x0);
    WriteChar(hchar, vchar, d_hex[6], 0x0);
    WriteChar(hchar, vchar, d_hex[5], 0x0);
    WriteChar(hchar, vchar, d_hex[4], 0x0);
    WriteChar(hchar, vchar, d_hex[3], 0x0);
    WriteChar(hchar, vchar, d_hex[2], 0x0);
    WriteChar(hchar, vchar, d_hex[1], 0x0);
    WriteChar(hchar, vchar, d_hex[0], 0x0);

    hchar = 10;
    vchar = vchar + 20;
    //EP0-1
    char MyBuffer10[6] = {"EP01:"};
    WriteString(hchar, vchar, MyBuffer10, black);
    Binary2ASCIIHex(BDT[2].ADR);
    WriteChar(hchar, vchar, d_hex[7], 0x0);
    WriteChar(hchar, vchar, d_hex[6], 0x0);
    WriteChar(hchar, vchar, d_hex[5], 0x0);
    WriteChar(hchar, vchar, d_hex[4], 0x0);
    WriteChar(hchar, vchar, d_hex[3], 0x0);
    WriteChar(hchar, vchar, d_hex[2], 0x0);
    WriteChar(hchar, vchar, d_hex[1], 0x0);
    WriteChar(hchar, vchar, d_hex[0], 0x0);

    WriteChar(hchar, vchar, ':', 0x0);

    Binary2ASCIIHex(BDT[2].CNT);
    WriteChar(hchar, vchar, d_hex[7], 0x0);
    WriteChar(hchar, vchar, d_hex[6], 0x0);
    WriteChar(hchar, vchar, d_hex[5], 0x0);
    WriteChar(hchar, vchar, d_hex[4], 0x0);
    WriteChar(hchar, vchar, d_hex[3], 0x0);
    WriteChar(hchar, vchar, d_hex[2], 0x0);
    WriteChar(hchar, vchar, d_hex[1], 0x0);
    WriteChar(hchar, vchar, d_hex[0], 0x0);

    WriteChar(hchar, vchar, ':', 0x0);

    Binary2ASCIIHex(BDT[2].STAT.Val);
    WriteChar(hchar, vchar, d_hex[7], 0x0);
    WriteChar(hchar, vchar, d_hex[6], 0x0);
    WriteChar(hchar, vchar, d_hex[5], 0x0);
    WriteChar(hchar, vchar, d_hex[4], 0x0);
    WriteChar(hchar, vchar, d_hex[3], 0x0);
    WriteChar(hchar, vchar, d_hex[2], 0x0);
    WriteChar(hchar, vchar, d_hex[1], 0x0);
    WriteChar(hchar, vchar, d_hex[0], 0x0);

    hchar = 10;
    vchar = vchar + 20;
    //EP1-0
    char MyBuffer11[6] = {"EP10:"};
    WriteString(hchar, vchar, MyBuffer11, black);
    Binary2ASCIIHex(BDT[4].ADR);
    WriteChar(hchar, vchar, d_hex[7], 0x0);
    WriteChar(hchar, vchar, d_hex[6], 0x0);
    WriteChar(hchar, vchar, d_hex[5], 0x0);
    WriteChar(hchar, vchar, d_hex[4], 0x0);
    WriteChar(hchar, vchar, d_hex[3], 0x0);
    WriteChar(hchar, vchar, d_hex[2], 0x0);
    WriteChar(hchar, vchar, d_hex[1], 0x0);
    WriteChar(hchar, vchar, d_hex[0], 0x0);

    WriteChar(hchar, vchar, ':', 0x0);

    Binary2ASCIIHex(BDT[4].CNT);
    WriteChar(hchar, vchar, d_hex[7], 0x0);
    WriteChar(hchar, vchar, d_hex[6], 0x0);
    WriteChar(hchar, vchar, d_hex[5], 0x0);
    WriteChar(hchar, vchar, d_hex[4], 0x0);
    WriteChar(hchar, vchar, d_hex[3], 0x0);
    WriteChar(hchar, vchar, d_hex[2], 0x0);
    WriteChar(hchar, vchar, d_hex[1], 0x0);
    WriteChar(hchar, vchar, d_hex[0], 0x0);

    WriteChar(hchar, vchar, ':', 0x0);

    Binary2ASCIIHex(BDT[4].STAT.Val);
    WriteChar(hchar, vchar, d_hex[7], 0x0);
    WriteChar(hchar, vchar, d_hex[6], 0x0);
    WriteChar(hchar, vchar, d_hex[5], 0x0);
    WriteChar(hchar, vchar, d_hex[4], 0x0);
    WriteChar(hchar, vchar, d_hex[3], 0x0);
    WriteChar(hchar, vchar, d_hex[2], 0x0);
    WriteChar(hchar, vchar, d_hex[1], 0x0);
    WriteChar(hchar, vchar, d_hex[0], 0x0);

    hchar = 10;
    vchar = vchar + 20;
    //EP1-1
    char MyBuffer12[6] = {"EP11:"};
    WriteString(hchar, vchar, MyBuffer12, black);
    Binary2ASCIIHex(BDT[6].ADR);
    WriteChar(hchar, vchar, d_hex[7], 0x0);
    WriteChar(hchar, vchar, d_hex[6], 0x0);
    WriteChar(hchar, vchar, d_hex[5], 0x0);
    WriteChar(hchar, vchar, d_hex[4], 0x0);
    WriteChar(hchar, vchar, d_hex[3], 0x0);
    WriteChar(hchar, vchar, d_hex[2], 0x0);
    WriteChar(hchar, vchar, d_hex[1], 0x0);
    WriteChar(hchar, vchar, d_hex[0], 0x0);

    WriteChar(hchar, vchar, ':', 0x0);

    Binary2ASCIIHex(BDT[6].CNT);
    WriteChar(hchar, vchar, d_hex[7], 0x0);
    WriteChar(hchar, vchar, d_hex[6], 0x0);
    WriteChar(hchar, vchar, d_hex[5], 0x0);
    WriteChar(hchar, vchar, d_hex[4], 0x0);
    WriteChar(hchar, vchar, d_hex[3], 0x0);
    WriteChar(hchar, vchar, d_hex[2], 0x0);
    WriteChar(hchar, vchar, d_hex[1], 0x0);
    WriteChar(hchar, vchar, d_hex[0], 0x0);

    WriteChar(hchar, vchar, ':', 0x0);

    Binary2ASCIIHex(BDT[6].STAT.Val);
    WriteChar(hchar, vchar, d_hex[7], 0x0);
    WriteChar(hchar, vchar, d_hex[6], 0x0);
    WriteChar(hchar, vchar, d_hex[5], 0x0);
    WriteChar(hchar, vchar, d_hex[4], 0x0);
    WriteChar(hchar, vchar, d_hex[3], 0x0);
    WriteChar(hchar, vchar, d_hex[2], 0x0);
    WriteChar(hchar, vchar, d_hex[1], 0x0);
    WriteChar(hchar, vchar, d_hex[0], 0x0);

    vchar = vchar + 30;
    hchar = 10;
    
    //Pointer
    int *pTest;
    pTest = (int*)0xa0000220;

    Binary2ASCIIHex(*pTest);
    WriteChar(hchar, vchar, d_hex[7], 0x0);
    WriteChar(hchar, vchar, d_hex[6], 0x0);
    WriteChar(hchar, vchar, d_hex[5], 0x0);
    WriteChar(hchar, vchar, d_hex[4], 0x0);
    WriteChar(hchar, vchar, d_hex[3], 0x0);
    WriteChar(hchar, vchar, d_hex[2], 0x0);
    WriteChar(hchar, vchar, d_hex[1], 0x0);
    WriteChar(hchar, vchar, d_hex[0], 0x0);
}