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

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

#include "usb.h"
#include "MainBrain.h"

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

//USB handles - Must be initialized to 0 at startup
void* USBGenericOutHandle; 
void* USBGenericInHandle;

//Packet buffers
unsigned char INPacket[64];
unsigned char  OUTPacket[64];

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[];

/* 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,                       // 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
};

uint8_t device_qualifier[] = 
{
    0x0a,                       //Size of this descriptor in bytes
    0x06,                       //Descriptor type (0x06)
    0x00, 0x02,                 //BCD - USB version number (Must be 0x200 or higher)
    0xff,                       //Class Code
    0xff,                       //Subclass Code
    0xff,                       //Protocol
    0x40,                       //bMaxPacketSize0
    0x01,                       //bNumConfigurations
    0x00                        //Reserved

};

//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 - length 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)
    //Device Interface GUID
    //65d2ce9b-61ef-47e5-871a-41b0ef87760c
    {'{','6','5','d','2','c','e','9','b','-','6','1','e','f','-','4','7','e','5','-','8','7','1','a','-','4','1','b','0','e','f','8','7','7','6','0','c','}',0x0000}
};    
    
//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 USBCBCheckOtherReq(void);

//Initialize the USB module
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)
        {
            //Device qualifier
            case USB_DESCRIPTOR_DEVICE_QUALIFIER:
                inPipes[0].pSrc.bRom = (const uint8_t*)&device_qualifier;
                inPipes[0].wCount.Val = sizeof(device_qualifier);
                break;            
            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)
        {
            uint8_t 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 USBCBCheckOtherReq(void)
{
        uint16_t Length;
        
        //Check if the most recent SETUP request is class specific
        if(SetupPkt.bmRequestType == 0xc0)    //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)  //0xee
            {
                //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;
                }
            }            
        }   
        else if(SetupPkt.bmRequestType == 0xc1)    //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;
                }    
            }                   
        }
}

void USBCBSuspend(void)
{
	//Example power saving code.  Insert appropriate code here for the desired
	//application behavior.  If the microcontroller will be put to sleep, a
	//process similar to that shown below may be used:
	
	//ConfigureIOPinsForLowPower();
	//SaveStateOfAllInterruptEnableBits();
	//DisableAllInterruptEnableBits();
	//EnableOnlyTheInterruptsWhichWillBeUsedToWakeTheMicro();	//should enable at least USBActivityIF as a wake source
	//Sleep();
	//RestoreStateOfAllPreviouslySavedInterruptEnableBits();	//Preferrably, this should be done in the USBCBWakeFromSuspend() function instead.
	//RestoreIOPinsToNormal();									//Preferrably, this should be done in the USBCBWakeFromSuspend() function instead.

	//Alternatively, the microcontorller may use clock switching to reduce current consumption.

	//IMPORTANT NOTE: Do not clear the USBActivityIF (ACTVIF) bit here.  This bit is 
	//cleared inside the usb_device.c file.  Clearing USBActivityIF here will cause 
	//things to not work as intended.	
	
}

/******************************************************************************
 * Function:        void USBCBWakeFromSuspend(void)
 *
 * PreCondition:    None
 *
 * Input:           None
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        The host may put USB peripheral devices in low power
 *					suspend mode (by "sending" 3+ms of idle).  Once in suspend
 *					mode, the host may wake the device back up by sending non-
 *					idle state signalling.
 *					
 *					This call back is invoked when a wakeup from USB suspend 
 *					is detected.
 *
 * Note:            None
 *****************************************************************************/
void USBCBWakeFromSuspend(void)
{
	// If clock switching or other power savings measures were taken when
	// executing the USBCBSuspend() function, now would be a good time to
	// switch back to normal full power run mode conditions.  The host allows
	// 10+ milliseconds of wakeup time, after which the device must be 
	// fully back to normal, and capable of receiving and processing USB
	// packets.  In order to do this, the USB module must receive proper
	// clocking (IE: 48MHz clock must be available to SIE for full speed USB
	// operation).  
	// Make sure the selected oscillator settings are consistent with USB 
    // operation before returning from this function.

	//Switch clock back to main clock source necessary for USB operation
	//Previous clock source was something low frequency as set in the 
	//USBCBSuspend() function.
}

/********************************************************************
 * Function:        void USBCB_SOF_Handler(void)
 *
 * PreCondition:    None
 *
 * Input:           None
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        The USB host sends out a SOF packet to full-speed
 *                  devices every 1 ms. This interrupt may be useful
 *                  for isochronous pipes. End designers should
 *                  implement callback routine as necessary.
 *
 * Note:            None
 *******************************************************************/
void USBCB_SOF_Handler(void)
{
    // No need to clear UIRbits.SOFIF to 0 here.
    // Callback caller is already doing that.
}

/*******************************************************************
 * Function:        void USBCBErrorHandler(void)
 *
 * PreCondition:    None
 *
 * Input:           None
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        The purpose of this callback is mainly for
 *                  debugging during development. Check UEIR to see
 *                  which error causes the interrupt.
 *
 * Note:            None
 *******************************************************************/
void USBCBErrorHandler(void)
{
    // No need to clear UEIR to 0 here.
    // Callback caller is already doing that.

	// Typically, user firmware does not need to do anything special
	// if a USB error occurs.  For example, if the host sends an OUT
	// packet to your device, but the packet gets corrupted (ex:
	// because of a bad connection, or the user unplugs the
	// USB cable during the transmission) this will typically set
	// one or more USB error interrupt flags.  Nothing specific
	// needs to be done however, since the SIE will automatically
	// send a "NAK" packet to the host.  In response to this, the
	// host will normally retry to send the packet again, and no
	// data loss occurs.  The system will typically recover
	// automatically, without the need for application firmware
	// intervention.
	
	// Nevertheless, this callback function is provided, such as
	// for debugging purposes.
}

/*******************************************************************
 * Function:        void USBCBStdSetDscHandler(void)
 *
 * PreCondition:    None
 *
 * Input:           None
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        The USBCBStdSetDscHandler() callback function is
 *					called when a SETUP, bRequest: SET_DESCRIPTOR request
 *					arrives.  Typically SET_DESCRIPTOR requests are
 *					not used in most applications, and it is
 *					optional to support this type of request.
 *
 * Note:            None
 *****************************************************************************/
void USBCBStdSetDscHandler(void)
{
    // Must claim session ownership if supporting this request
}//end


/******************************************************************************
 * Function:        void USBCBInitEP(void)
 *
 * PreCondition:    None
 *
 * Input:           None
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        This function is called when the device becomes
 *                  initialized, which occurs after the host sends a
 * 					SET_CONFIGURATION (wValue not = 0) request.  This 
 *					callback function should initialize the endpoints 
 *					for the device's usage according to the current 
 *					configuration.
 *
 * Note:            None
 *****************************************************************************/
void USBCBInitEP(void)
{
    //Enable the appplication data endpoint(s)
    USBEnableEndpoint(1,0x1d);
    //Prepare the OUT endpoint for the next packet that the host might try to send
    //USBGenericOutHandle = USBRxOnePacket(1,(uint8_t*)&OUTPacket,64);
    USBGenericOutHandle = USBTransferOnePacket(1, 0, (uint8_t*)&OUTPacket, 64);
}

/********************************************************************
 * Function:        void USBCBSendResume(void)
 *
 * PreCondition:    None
 *
 * Input:           None
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        The USB specifications allow some types of USB
 * 					peripheral devices to wake up a host PC (such
 *					as if it is in a low power suspend to RAM state).
 *					This can be a very useful feature in some
 *					USB applications, such as an Infrared remote
 *					control	receiver.  If a user presses the "power"
 *					button on a remote control, it is nice that the
 *					IR receiver can detect this signalling, and then
 *					send a USB "command" to the PC to wake up.
 *					
 *					The USBCBSendResume() "callback" function is used
 *					to send this special USB signalling which wakes 
 *					up the PC.  This function may be called by
 *					application firmware to wake up the PC.  This
 *					function will only be able to wake up the host if
 *                  all of the below are true:
 *					
 *					1.  The USB driver used on the host PC supports
 *						the remote wakeup capability.
 *					2.  The USB configuration descriptor indicates
 *						the device is remote wakeup capable in the
 *						bmAttributes field.
 *					3.  The USB host PC is currently sleeping,
 *						and has previously sent your device a SET 
 *						FEATURE setup packet which "armed" the
 *						remote wakeup capability.   
 *
 *                  If the host has not armed the device to perform remote wakeup,
 *                  then this function will return without actually performing a
 *                  remote wakeup sequence.  This is the required behavior, 
 *                  as a USB device that has not been armed to perform remote 
 *                  wakeup must not drive remote wakeup signalling onto the bus;
 *                  doing so will cause USB compliance testing failure.
 *                  
 *					This callback should send a RESUME signal that
 *                  has the period of 1-15ms.
 *
 * Note:            This function does nothing and returns quickly, if the USB
 *                  bus and host are not in a suspended condition, or are 
 *                  otherwise not in a remote wakeup ready state.  Therefore, it
 *                  is safe to optionally call this function regularly, ex: 
 *                  anytime application stimulus occurs, as the function will
 *                  have no effect, until the bus really is in a state ready
 *                  to accept remote wakeup. 
 *
 *                  When this function executes, it may perform clock switching,
 *                  depending upon the application specific code in 
 *                  USBCBWakeFromSuspend().  This is needed, since the USB
 *                  bus will no longer be suspended by the time this function
 *                  returns.  Therefore, the USB module will need to be ready
 *                  to receive traffic from the host.
 *
 *                  The modifiable section in this routine may be changed
 *                  to meet the application needs. Current implementation
 *                  temporary blocks other functions from executing for a
 *                  period of ~3-15 ms depending on the core frequency.
 *
 *                  According to USB 2.0 specification section 7.1.7.7,
 *                  "The remote wakeup device must hold the resume signaling
 *                  for at least 1 ms but for no more than 15 ms."
 *                  The idea here is to use a delay counter loop, using a
 *                  common value that would work over a wide range of core
 *                  frequencies.
 *                  That value selected is 1800. See table below:
 *                  ==========================================================
 *                  Core Freq(MHz)      MIP         RESUME Signal Period (ms)
 *                  ==========================================================
 *                      48              12          1.05
 *                       4              1           12.6
 *                  ==========================================================
 *                  * These timing could be incorrect when using code
 *                    optimization or extended instruction mode,
 *                    or when having other interrupts enabled.
 *                    Make sure to verify using the MPLAB SIM's Stopwatch
 *                    and verify the actual signal on an oscilloscope.
 *******************************************************************/
void USBCBSendResume(void)
{
    static uint16_t delay_count;
    
    //First verify that the host has armed us to perform remote wakeup.
    //It does this by sending a SET_FEATURE request to enable remote wakeup,
    //usually just before the host goes to standby mode (note: it will only
    //send this SET_FEATURE request if the configuration descriptor declares
    //the device as remote wakeup capable, AND, if the feature is enabled
    //on the host (ex: on Windows based hosts, in the device manager 
    //properties page for the USB device, power management tab, the 
    //"Allow this device to bring the computer out of standby." checkbox 
    //should be checked).
    if(RemoteWakeup == true) 
    {
        //Verify that the USB bus is in fact suspended, before we send
        //remote wakeup signalling.
        if(USBBusIsSuspended == true)
        {
            IEC1CLR = 0x02000000;;
            
            //Clock switch to settings consistent with normal USB operation.
            USBCBWakeFromSuspend();
            U1PWRCbits.USUSPEND = 0; 
            USBBusIsSuspended = false;  //So we don't execute this code again, 
                                        //until a new suspend condition is detected.

            //Section 7.1.7.7 of the USB 2.0 specifications indicates a USB
            //device must continuously see 5ms+ of idle on the bus, before it sends
            //remote wakeup signalling.  One way to be certain that this parameter
            //gets met, is to add a 2ms+ blocking delay here (2ms plus at 
            //least 3ms from bus idle to USBIsBusSuspended() == true, yeilds
            //5ms+ total delay since start of idle).
            delay_count = 3600U;        
            do
            {
                delay_count--;
            }while(delay_count);
            
            //Now drive the resume K-state signalling onto the USB bus.
            U1CONbits.RESUME = 1;       // Start RESUME signaling
            delay_count = 1800U;        // Set RESUME line for 1-13 ms
            do
            {
                delay_count--;
            }while(delay_count);
            U1CONbits.RESUME = 0;       //Finished driving resume signalling

            IEC1SET = 0x02000000;;
        }
    }
}


/*******************************************************************
 * Function:        bool USER_USB_CALLBACK_EVENT_HANDLER(
 *                        int event, void *pdata, uint16_t size)
 *
 * PreCondition:    None
 *
 * Input:           int event - the type of event
 *                  void *pdata - pointer to the event data
 *                  uint16_t size - size of the event data
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        This function is called from the USB stack to
 *                  notify a user application that a USB event
 *                  occured.  This callback is in interrupt context
 *                  when the USB_INTERRUPT option is selected.
 *
 * Note:            None
 *******************************************************************/
bool USER_USB_CALLBACK_EVENT_HANDLER(int event, void *pdata, uint16_t size)
{
    
    switch( event )
    {
        case EVENT_TRANSFER:
            //Add application specific callback task or callback function here if desired.
            break;
        case EVENT_SOF:
            USBCB_SOF_Handler();
            break;
        case EVENT_SUSPEND:
            USBCBSuspend();
            break;
        case EVENT_RESUME:
            USBCBWakeFromSuspend();
            break;
        case EVENT_CONFIGURED: 
            USBCBInitEP();
            break;
        case EVENT_SET_DESCRIPTOR:
            USBCBStdSetDscHandler();
            break;
        case EVENT_EP0_REQUEST:
            USBCBCheckOtherReq();
            break;
        case EVENT_BUS_ERROR:
            USBCBErrorHandler();
            break;
        case EVENT_TRANSFER_TERMINATED:
            //Add application specific callback task or callback function here if desired.
            //The EVENT_TRANSFER_TERMINATED event occurs when the host performs a CLEAR
            //FEATURE (endpoint halt) request on an application endpoint which was 
            //previously armed (UOWN was = 1).  Here would be a good place to:
            //1.  Determine which endpoint the transaction that just got terminated was 
            //      on, by checking the handle value in the *pdata.
            //2.  Re-arm the endpoint if desired (typically would be the case for OUT 
            //      endpoints).
            break;            
        default:
            break;
    }      
    return true; 
}
