text: //*****************************************************************************
  //
  // bl_usbfuncs.c - The subset of USB library functions required by the USB DFU
  //                 boot loader.
  //
  // Copyright (c) 2008-2017 Texas Instruments Incorporated.  All rights reserved.
  // Software License Agreement
  // 
  // Texas Instruments (TI) is supplying this software for use solely and
  // exclusively on TI's microcontroller products. The software is owned by
  // TI and/or its suppliers, and is protected under applicable copyright
  // laws. You may not combine this software with "viral" open-source
  // software in order to form a larger program.
  // 
  // THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS.
  // NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT
  // NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  // A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY
  // CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
  // DAMAGES, FOR ANY REASON WHATSOEVER.
  // 
  // This is part of revision 2.1.4.178 of the Tiva Firmware Development Package.
  //
  //*****************************************************************************
  
  #include <stdbool.h>
  #include <stdint.h>
  #include "inc/hw_types.h"
  #include "inc/hw_memmap.h"
  #include "inc/hw_usb.h"
  #include "inc/hw_sysctl.h"
  #include "inc/hw_nvic.h"
  #include "inc/hw_ints.h"
  #include "inc/hw_gpio.h"
  #include "bl_config.h"
  #include "boot_loader/bl_usbfuncs.h"
  
  //*****************************************************************************
  //
  //! \addtogroup bl_usb_api
  //! @{
  //
  //*****************************************************************************
  #if defined(USB_ENABLE_UPDATE) || defined(DOXYGEN)
  
  //*****************************************************************************
  //
  // Local functions prototypes.
  //
  //*****************************************************************************
  static void USBDGetStatus(tUSBRequest *pUSBRequest);
  static void USBDClearFeature(tUSBRequest *pUSBRequest);
  static void USBDSetFeature(tUSBRequest *pUSBRequest);
  static void USBDSetAddress(tUSBRequest *pUSBRequest);
  static void USBDGetDescriptor(tUSBRequest *pUSBRequest);
  static void USBDSetDescriptor(tUSBRequest *pUSBRequest);
  static void USBDGetConfiguration(tUSBRequest *pUSBRequest);
  static void USBDSetConfiguration(tUSBRequest *pUSBRequest);
  static void USBDGetInterface(tUSBRequest *pUSBRequest);
  static void USBDSetInterface(tUSBRequest *pUSBRequest);
  static void USBDEP0StateTx(void);
  static int32_t USBDStringIndexFromRequest(uint16_t ui16Lang,
                                            uint16_t ui16Index);
  
  //*****************************************************************************
  //
  // This structure holds the full state for the device enumeration.
  //
  //*****************************************************************************
  typedef struct
  {
      //
      // The devices current address, this also has a change pending bit in the
      // MSB of this value specified by DEV_ADDR_PENDING.
      //
      volatile uint32_t ui32DevAddress;
  
      //
      // This holds the current active configuration for this device.
      //
      uint32_t ui32Configuration;
  
      //
      // This holds the current alternate interface for this device. We only have
      // 1 interface so only need to hold 1 setting.
      //
      uint8_t ui8AltSetting;
  
      //
      // This is the pointer to the current data being sent out or received
      // on endpoint zero.
      //
      uint8_t *pui8EP0Data;
  
      //
      // This is the number of bytes that remain to be sent from or received
      // into the g_sUSBDeviceState.pui8EP0Data data buffer.
      //
      volatile uint32_t ui32EP0DataRemain;
  
      //
      // The amount of data being sent/received due to a custom request.
      //
      uint32_t ui32OUTDataSize;
  
      //
      // Holds the current device status.
      //
      uint8_t ui8Status;
  
      //
      // This flag indicates whether or not remote wakeup signalling is in
      // progress.
      //
      bool bRemoteWakeup;
  
      //
      // During remote wakeup signalling, this counter is used to track the
      // number of milliseconds since the signalling was initiated.
      //
      uint8_t ui8RemoteWakeupCount;
  }
  tDeviceState;
  
  //*****************************************************************************
  //
  // The states for endpoint zero during enumeration.
  //
  //*****************************************************************************
  typedef enum
  {
      //
      // The USB device is waiting on a request from the host controller on
      // endpoint zero.
      //
      USB_STATE_IDLE,
  
      //
      // The USB device is sending data back to the host due to an IN request.
      //
      USB_STATE_TX,
  
      //
      // The USB device is receiving data from the host due to an OUT
      // request from the host.
      //
      USB_STATE_RX,
  
      //
      // The USB device has completed the IN or OUT request and is now waiting
      // for the host to acknowledge the end of the IN/OUT transaction.  This
      // is the status phase for a USB control transaction.
      //
      USB_STATE_STATUS,
  
      //
      // This endpoint has signaled a stall condition and is waiting for the
      // stall to be acknowledged by the host controller.
      //
      USB_STATE_STALL
  }
  tEP0State;
  
  //*****************************************************************************
  //
  // Define the max packet size for endpoint zero.
  //
  //*****************************************************************************
  #define EP0_MAX_PACKET_SIZE     64
  
  //*****************************************************************************
  //
  // This is a flag used with g_sUSBDeviceState.ui32DevAddress to indicate that a
  // device address change is pending.
  //
  //*****************************************************************************
  #define DEV_ADDR_PENDING        0x80000000
  
  //*****************************************************************************
  //
  // This label defines the default configuration number to use after a bus
  // reset.
  //
  //*****************************************************************************
  #define DEFAULT_CONFIG_ID       1
  
  //*****************************************************************************
  //
  // This label defines the number of milliseconds that the remote wakeup signal
  // must remain asserted before removing it. Section 7.1.7.7 of the USB 2.0 spec
  // states that "the remote wakeup device must hold the resume signaling for at
  // least 1ms but for no more than 15ms" so 10mS seems a reasonable choice.
  //
  //*****************************************************************************
  #define REMOTE_WAKEUP_PULSE_MS 10
  
  //*****************************************************************************
  //
  // This label defines the number of milliseconds between the point where we
  // assert the remote wakeup signal and calling the client back to tell it that
  // bus operation has been resumed.  This value is based on the timings provided
  // in section 7.1.7.7 of the USB 2.0 specification which indicates that the host
  // (which takes over resume signalling when the device's initial signal is
  // detected) must hold the resume signalling for at least 20mS.
  //
  //*****************************************************************************
  #define REMOTE_WAKEUP_READY_MS 20
  
  //*****************************************************************************
  //
  // The buffer for reading data coming into EP0
  //
  //*****************************************************************************
  static uint8_t g_pui8DataBufferIn[EP0_MAX_PACKET_SIZE];
  
  //*****************************************************************************
  //
  // This global holds the current state information for the USB device.
  //
  //*****************************************************************************
  static volatile tDeviceState g_sUSBDeviceState;
  
  //*****************************************************************************
  //
  // This global holds the current state of endpoint zero.
  //
  //*****************************************************************************
  static volatile tEP0State g_eUSBDEP0State = USB_STATE_IDLE;
  
  //*****************************************************************************
  //
  // Function table to handle standard requests.
  //
  //*****************************************************************************
  static const tStdRequest g_ppfnUSBDStdRequests[] =
  {
      USBDGetStatus,
      USBDClearFeature,
      0,
      USBDSetFeature,
      0,
      USBDSetAddress,
      USBDGetDescriptor,
      USBDSetDescriptor,
      USBDGetConfiguration,
      USBDSetConfiguration,
      USBDGetInterface,
      USBDSetInterface,
  };
  
  //*****************************************************************************
  //
  // Amount to shift the RX interrupt sources by in the flags used in the
  // interrupt calls.
  //
  //*****************************************************************************
  #define USB_INT_RX_SHIFT        8
  
  //*****************************************************************************
  //
  // Amount to shift the status interrupt sources by in the flags used in the
  // interrupt calls.
  //
  //*****************************************************************************
  #define USB_INT_STATUS_SHIFT    24
  
  //*****************************************************************************
  //
  // Amount to shift the RX endpoint status sources by in the flags used in the
  // calls.
  //
  //*****************************************************************************
  #define USB_RX_EPSTATUS_SHIFT   16
  
  //*****************************************************************************
  //
  // Converts from an endpoint specifier to the offset of the endpoint's
  // control/status registers.
  //
  //*****************************************************************************
  #define EP_OFFSET(Endpoint)     (Endpoint - 0x10)
  
  //*****************************************************************************
  //
  // Retrieves data from endpoint 0's FIFO.
  //
  // \param pui8Data is a pointer to the data area used to return the data from
  // the FIFO.
  // \param pui32Size is initially the size of the buffer passed into this call
  // via the \e pui8Data parameter.  It will be set to the amount of data
  // returned in the buffer.
  //
  // This function will return the data from the FIFO for endpoint 0.
  // The \e pui32Size parameter should indicate the size of the buffer passed in
  // the \e pui32Data parameter.  The data in the \e pui32Size parameter will be
  // changed to match the amount of data returned in the \e pui8Data parameter.
  // If a zero byte packet was received this call will not return a error but
  // will instead just return a zero in the \e pui32Size parameter.  The only
  // error case occurs when there is no data packet available.
  //
  // \return This call will return 0, or -1 if no packet was received.
  //
  //*****************************************************************************
  int32_t
  USBEndpoint0DataGet(uint8_t *pui8Data, uint32_t *pui32Size)
  {
      uint32_t ui32ByteCount;
  
      //
      // Don't allow reading of data if the RxPktRdy bit is not set.
      //
      if((HWREGH(USB0_BASE + USB_O_CSRL0) & USB_CSRL0_RXRDY) == 0)
      {
          //
          // Can't read the data because none is available.
          //
          *pui32Size = 0;
  
          //
          // Return a failure since there is no data to read.
          //
          return(-1);
      }
  
      //
      // Get the byte count in the FIFO.
      //
      ui32ByteCount = HWREGH(USB0_BASE + USB_O_COUNT0 + USB_EP_0);
  
      //
      // Determine how many bytes we will actually copy.
      //
      ui32ByteCount = (ui32ByteCount < *pui32Size) ? ui32ByteCount : *pui32Size;
  
      //
      // Return the number of bytes we are going to read.
      //
      *pui32Size = ui32ByteCount;
  
      //
      // Read the data out of the FIFO.
      //
      for(; ui32ByteCount > 0; ui32ByteCount--)
      {
          //
          // Read a byte at a time from the FIFO.
          //
          *pui8Data++ = HWREGB(USB0_BASE + USB_O_FIFO0 + (USB_EP_0 >> 2));
      }
  
      //
      // Success.
      //
      return(0);
  }
  
  //*****************************************************************************
  //
  // Acknowledge that data was read from endpoint 0's FIFO.
  //
  // \param bIsLastPacket indicates if this is the last packet.
  //
  // This function acknowledges that the data was read from the endpoint 0's
  // FIFO.  The \e bIsLastPacket parameter is set to a \b true value if this is
  // the last in a series of data packets.  This call can be used if processing
  // is required between reading the data and acknowledging that the data has
  // been read.
  //
  // \return None.
  //
  //*****************************************************************************
  void
  USBDevEndpoint0DataAck(bool bIsLastPacket)
  {
      //
      // Clear RxPktRdy, and optionally DataEnd, on endpoint zero.
      //
      HWREGB(USB0_BASE + USB_O_CSRL0) =
          USB_CSRL0_RXRDYC | (bIsLastPacket ? USB_CSRL0_DATAEND : 0);
  
  }
  
  //*****************************************************************************
  //
  // Puts data into endpoint 0's FIFO.
  //
  // \param pui8Data is a pointer to the data area used as the source for the
  // data to put into the FIFO.
  // \param ui32Size is the amount of data to put into the FIFO.
  //
  // This function will put the data from the \e pui8Data parameter into the FIFO
  // for endpoint 0.  If a packet is already pending for transmission then
  // this call will not put any of the data into the FIFO and will return -1.
  //
  // \return This call will return 0 on success, or -1 to indicate that the FIFO
  // is in use and cannot be written.
  //
  //*****************************************************************************
  int32_t
  USBEndpoint0DataPut(uint8_t *pui8Data, uint32_t ui32Size)
  {
      //
      // Don't allow transmit of data if the TxPktRdy bit is already set.
      //
      if(HWREGB(USB0_BASE + USB_O_CSRL0 + USB_EP_0) & USB_CSRL0_TXRDY)
      {
          return(-1);
      }
  
      //
      // Write the data to the FIFO.
      //
      for(; ui32Size > 0; ui32Size--)
      {
          HWREGB(USB0_BASE + USB_O_FIFO0 + (USB_EP_0 >> 2)) = *pui8Data++;
      }
  
      //
      // Success.
      //
      return(0);
  }
  
  //*****************************************************************************
  //
  // Starts the transfer of data from endpoint 0's FIFO.
  //
  // \param ui32TransType is set to indicate what type of data is being sent.
  //
  // This function will start the transfer of data from the FIFO for
  // endpoint 0.  This is necessary if the \b USB_EP_AUTO_SET bit was not enabled
  // for the endpoint.  Setting the \e ui32TransType parameter will allow the
  // appropriate signaling on the USB bus for the type of transaction being
  // requested.  The \e ui32TransType parameter should be one of the following:
  //
  // - USB_TRANS_OUT for OUT transaction on any endpoint in host mode.
  // - USB_TRANS_IN for IN transaction on any endpoint in device mode.
  // - USB_TRANS_IN_LAST for the last IN transactions on endpoint zero in a
  //   sequence of IN transactions.
  // - USB_TRANS_SETUP for setup transactions on endpoint zero.
  // - USB_TRANS_STATUS for status results on endpoint zero.
  //
  // \return This call will return 0 on success, or -1 if a transmission is
  // already in progress.
  //
  //*****************************************************************************
  int32_t
  USBEndpoint0DataSend(uint32_t ui32TransType)
  {
      //
      // Don't allow transmit of data if the TxPktRdy bit is already set.
      //
      if(HWREGB(USB0_BASE + USB_O_CSRL0 + USB_EP_0) & USB_CSRL0_TXRDY)
      {
          return(-1);
      }
  
      //
      // Set TxPktRdy in order to send the data.
      //
      HWREGB(USB0_BASE + USB_O_CSRL0 + USB_EP_0) = ui32TransType & 0xff;
  
      //
      // Success.
      //
      return(0);
  }
  
  #if defined(USB_VBUS_CONFIG) || defined(USB_ID_CONFIG) || \
      defined(USB_DP_CONFIG) || defined(USB_DM_CONFIG) || defined(DOXYGEN)
  //*****************************************************************************
  //
  //! Initialize the pins used by USB functions.
  //!
  //! This function configures the pins for USB functions depending on defines
  //! from the bl_config.h file.
  //!
  //! \return None.
  //
  //*****************************************************************************
  void
  USBConfigurePins(void)
  {
      //
      // Enable the clocks to the GPIOs.
      //
      HWREG(SYSCTL_RCGCGPIO) |=
          (0x0 |
  #if defined(USB_VBUS_CONFIG)
           USB_VBUS_PERIPH |
  #endif
  #if defined(USB_ID_CONFIG)
           USB_ID_PERIPH |
  #endif
  #if defined(USB_DP_CONFIG)
           USB_DP_PERIPH |
  #endif
  #if defined(USB_DM_CONFIG)
           USB_DM_PERIPH
  #endif
          );
  
      //
      // Wait for the Peripherals to be Ready before accessing the register
      // address space.
      //
      while((HWREG(SYSCTL_PRGPIO) &
            (0x0 |
  #if defined(USB_VBUS_CONFIG)
             USB_VBUS_PERIPH |
  #endif
  #if defined(USB_ID_CONFIG)
             USB_ID_PERIPH |
  #endif
  #if defined(USB_DP_CONFIG)
             USB_DP_PERIPH |
  #endif
  #if defined(USB_DM_CONFIG)
             USB_DM_PERIPH
  #endif
          )) !=
              (0x0 |
  #if defined(USB_VBUS_CONFIG)
               USB_VBUS_PERIPH |
  #endif
  #if defined(USB_ID_CONFIG)
               USB_ID_PERIPH |
  #endif
  #if defined(USB_DP_CONFIG)
               USB_DP_PERIPH |
  #endif
  #if defined(USB_DM_CONFIG)
               USB_DM_PERIPH
  #endif
              ));
  
      // 
      // Setup the pins based on bl_config.h
      //
  #if defined(USB_VBUS_CONFIG)
      //
      // Set the VBUS pin to be an analog input.
      // 
      HWREG(USB_VBUS_PORT + GPIO_O_DIR) &= ~(1 << USB_VBUS_PIN);
      HWREG(USB_VBUS_PORT + GPIO_O_AMSEL) |= (1 << USB_VBUS_PIN);
  #endif
  
  #if defined(USB_ID_CONFIG)
      //
      // Set the ID pin to be an analog input.
      // 
      HWREG(USB_ID_PORT + GPIO_O_DIR) &= ~(1 << USB_ID_PIN);
      HWREG(USB_ID_PORT + GPIO_O_AMSEL) |= (1 << USB_ID_PIN);
  #endif
      
  #if defined(USB_DP_CONFIG)
      //
      // Set the DP pin to be an analog input.
      // 
      HWREG(USB_DP_PORT + GPIO_O_DIR) &= ~(1 << USB_DP_PIN);
      HWREG(USB_DP_PORT + GPIO_O_AMSEL) |= (1 << USB_DP_PIN);
  #endif
  
  #if defined(USB_DM_CONFIG)
      //
      // Set the DM pin to be an analog input.
      // 
      HWREG(USB_DM_PORT + GPIO_O_DIR) &= ~(1 << USB_DM_PIN);
      HWREG(USB_DM_PORT + GPIO_O_AMSEL) |= (1 << USB_DM_PIN);
  #endif
  
  }
  #endif
  
  //*****************************************************************************
  //
  //! Initialize the boot loader USB functions.
  //!
  //! This function initializes the boot loader USB functions and places the DFU
  //! device onto the USB bus.
  //!
  //! \return None.
  //
  //*****************************************************************************
  void
  USBBLInit(void)
  {
      //
      // Configure the USB Pins based on the bl_config.h settings.
      //
  #if defined(USB_VBUS_CONFIG) || defined(USB_ID_CONFIG) || \
      defined(USB_DP_CONFIG) || defined(USB_DM_CONFIG)
      USBConfigurePins();
  #endif
  
      //
      // Initialize a couple of fields in the device state structure.
      //
      g_sUSBDeviceState.ui32Configuration = DEFAULT_CONFIG_ID;
  
      //
      // Enable the USB controller.
      //
      HWREG(SYSCTL_RCGCUSB) = SYSCTL_RCGCUSB_R0;
  
      //
      // Wait for the peripheral ready
      //
      while((HWREG(SYSCTL_PRUSB) & SYSCTL_PRUSB_R0) != SYSCTL_PRUSB_R0)
      {
      }
  
  #if defined(TARGET_IS_TM4C129_RA0) ||                                         \
      defined(TARGET_IS_TM4C129_RA1) ||                                         \
      defined(TARGET_IS_TM4C129_RA2)
      //
      // Turn on USB Phy clock from PLL VCO
      //
      HWREG(USB0_BASE + USB_O_CC) = (USB_CC_CLKEN | (7 << USB_CC_CLKDIV_S));
  #else
      //
      // Turn on USB Phy clock.
      //
      HWREG(SYSCTL_RCC2) &= ~SYSCTL_RCC2_USBPWRDN;
  #endif
  
      //
      // Clear any pending interrupts.
      //
      HWREGH(USB0_BASE + USB_O_TXIS);
      HWREGB(USB0_BASE + USB_O_IS);
  
      //
      // Enable USB Interrupts.
      //
      HWREGH(USB0_BASE + USB_O_TXIE) = USB_TXIS_EP0;
      HWREGB(USB0_BASE + USB_O_IE) = (USB_IS_DISCON | USB_IS_RESET);
  
      //
      // Default to the state where remote wakeup is disabled.
      //
      g_sUSBDeviceState.ui8Status = 0;
      g_sUSBDeviceState.bRemoteWakeup = false;
  
      //
      // Determine the self- or bus-powered state based on bl_config.h setting.
      //
  #if USB_BUS_POWERED
      g_sUSBDeviceState.ui8Status &= ~USB_STATUS_SELF_PWR;
  #else
      g_sUSBDeviceState.ui8Status |= USB_STATUS_SELF_PWR;
  #endif
  
      //
      // Attach the device using the soft connect.
      //
      HWREGB(USB0_BASE + USB_O_POWER) |= USB_POWER_SOFTCONN;
  
      //
      // Enable the USB interrupt.
      //
      HWREG(NVIC_EN1) = 1 << (INT_USB0 - 48);
  }
  
  //*****************************************************************************
  //
  // This function starts the request for data from the host on endpoint zero.
  //
  // \param pui8Data is a pointer to the buffer to fill with data from the USB
  // host.
  // \param ui32Size is the size of the buffer or data to return from the USB
  // host.
  //
  // This function handles retrieving data from the host when a custom command
  // has been issued on endpoint zero.  When the requested data is received,
  // the function HandleEP0Data() will be called.
  //
  // \return None.
  //
  //*****************************************************************************
  void
  USBBLRequestDataEP0(uint8_t *pui8Data, uint32_t ui32Size)
  {
      //
      // Enter the RX state on end point 0.
      //
      g_eUSBDEP0State = USB_STATE_RX;
  
      //
      // Save the pointer to the data.
      //
      g_sUSBDeviceState.pui8EP0Data = pui8Data;
  
      //
      // Location to save the current number of bytes received.
      //
      g_sUSBDeviceState.ui32OUTDataSize = ui32Size;
  
      //
      // Bytes remaining to be received.
      //
      g_sUSBDeviceState.ui32EP0DataRemain = ui32Size;
  }
  
  //*****************************************************************************
  //
  //! This function requests transfer of data to the host on endpoint zero.
  //!
  //! \param pui8Data is a pointer to the buffer to send via endpoint zero.
  //! \param ui32Size is the amount of data to send in bytes.
  //!
  //! This function handles sending data to the host when a custom command is
  //! issued or non-standard descriptor has been requested on endpoint zero.
  //!
  //! \return None.
  //
  //*****************************************************************************
  void
  USBBLSendDataEP0(uint8_t *pui8Data, uint32_t ui32Size)
  {
      //
      // Return the externally provided device descriptor.
      //
      g_sUSBDeviceState.pui8EP0Data = pui8Data;
  
      //
      // The size of the device descriptor is in the first byte.
      //
      g_sUSBDeviceState.ui32EP0DataRemain = ui32Size;
  
      //
      // Save the total size of the data sent.
      //
      g_sUSBDeviceState.ui32OUTDataSize = ui32Size;
  
      //
      // Now in the transmit data state.
      //
      USBDEP0StateTx();
  }
  
  //*****************************************************************************
  //
  //! This function generates a stall condition on endpoint zero.
  //!
  //! This function is typically called to signal an error condition to the host
  //! when an unsupported request is received by the device.  It should be
  //! called from within the callback itself (in interrupt context) and not
  //! deferred until later since it affects the operation of the endpoint zero
  //! state machine.
  //!
  //! \return None.
  //
  //*****************************************************************************
  void
  USBBLStallEP0(void)
  {
      //
      // Perform a stall on endpoint zero.
      //
      HWREGB(USB0_BASE + USB_O_CSRL0) |= (USB_CSRL0_STALL | USB_CSRL0_RXRDYC);
  
      //
      // Enter the stalled state.
      //
      g_eUSBDEP0State = USB_STATE_STALL;
  }
  
  //*****************************************************************************
  //
  // This internal function reads a request data packet and dispatches it to
  // either a standard request handler or the registered device request
  // callback depending upon the request type.
  //
  // \return None.
  //
  //*****************************************************************************
  static void
  USBDReadAndDispatchRequest(void)
  {
      uint32_t ui32Size;
      tUSBRequest *pRequest;
  
      //
      // Cast the buffer to a request structure.
      //
      pRequest = (tUSBRequest *)g_pui8DataBufferIn;
  
      //
      // Set the buffer size.
      //
      ui32Size = EP0_MAX_PACKET_SIZE;
  
      //
      // Get the data from the USB controller end point 0.
      //
      USBEndpoint0DataGet(g_pui8DataBufferIn, &ui32Size);
  
      if(!ui32Size)
      {
          return;
      }
  
      //
      // See if this is a standard request or not.
      //
      if((pRequest->bmRequestType & USB_RTYPE_TYPE_M) != USB_RTYPE_STANDARD)
      {
          //
          // Pass this non-standard request on to the DFU handler
          //
          HandleRequests(pRequest);
      }
      else
      {
          //
          // Assure that the jump table is not out of bounds.
          //
          if((pRequest->bRequest <
              (sizeof(g_ppfnUSBDStdRequests) / sizeof(tStdRequest))) &&
             (g_ppfnUSBDStdRequests[pRequest->bRequest] != 0))
          {
              //
              // Jump table to the appropriate handler.
              //
              g_ppfnUSBDStdRequests[pRequest->bRequest](pRequest);
          }
          else
          {
              //
              // If there is no handler then stall this request.
              //
              USBBLStallEP0();
          }
      }
  }
  
  //*****************************************************************************
  //
  // This is the low level interrupt handler for endpoint zero.
  //
  // This function handles all interrupts on endpoint zero in order to maintain
  // the state needed for the control endpoint on endpoint zero.  In order to
  // successfully enumerate and handle all USB standard requests, all requests
  // on endpoint zero must pass through this function.  The endpoint has the
  // following states: \b USB_STATE_IDLE, \b USB_STATE_TX, \b USB_STATE_RX,
  // \b USB_STATE_STALL, and \b USB_STATE_STATUS.  In the \b USB_STATE_IDLE
  // state the USB controller has not received the start of a request, and once
  // it does receive the data for the request it will either enter the
  // \b USB_STATE_TX, \b USB_STATE_RX, or \b USB_STATE_STALL depending on the
  // command.  If the controller enters the \b USB_STATE_TX or \b USB_STATE_RX
  // then once all data has been sent or received, it must pass through the
  // \b USB_STATE_STATUS state to allow the host to acknowledge completion of
  // the request.  The \b USB_STATE_STALL is entered from \b USB_STATE_IDLE in
  // the event that the USB request was not valid.  Both the \b USB_STATE_STALL
  // and \b USB_STATE_STATUS are transitional states that return to the
  // \b USB_STATE_IDLE state.
  //
  // \return None.
  //
  // USB_STATE_IDLE -*--> USB_STATE_TX -*-> USB_STATE_STATUS -*->USB_STATE_IDLE
  //                 |                  |                     |
  //                 |--> USB_STATE_RX -                      |
  //                 |                                        |
  //                 |--> USB_STATE_STALL ---------->---------
  //
  //  ----------------------------------------------------------------
  // | Current State       | State 0           | State 1              |
  // | --------------------|-------------------|----------------------
  // | USB_STATE_IDLE      | USB_STATE_TX/RX   | USB_STATE_STALL      |
  // | USB_STATE_TX        | USB_STATE_STATUS  |                      |
  // | USB_STATE_RX        | USB_STATE_STATUS  |                      |
  // | USB_STATE_STATUS    | USB_STATE_IDLE    |                      |
  // | USB_STATE_STALL     | USB_STATE_IDLE    |                      |
  //  ----------------------------------------------------------------
  //
  //*****************************************************************************
  void
  USBDeviceEnumHandler(void)
  {
      uint32_t ui32EPStatus;
  
      //
      // Get the TX portion of the endpoint status.
      //
      ui32EPStatus = HWREGH(USB0_BASE + EP_OFFSET(USB_EP_0) + USB_O_TXCSRL1);
  
      //
      // Get the RX portion of the endpoint status.
      //
      ui32EPStatus |=
          ((HWREGH(USB0_BASE + EP_OFFSET(USB_EP_0) + USB_O_RXCSRL1)) <<
           USB_RX_EPSTATUS_SHIFT);
  
      //
      // What state are we currently in?
      //
      switch(g_eUSBDEP0State)
      {
          //
          // Handle the status state, this is a transitory state from
          // USB_STATE_TX or USB_STATE_RX back to USB_STATE_IDLE.
          //
          case USB_STATE_STATUS:
          {
              //
              // Just go back to the idle state.
              //
              g_eUSBDEP0State = USB_STATE_IDLE;
  
              //
              // If there is a pending address change then set the address.
              //
              if(g_sUSBDeviceState.ui32DevAddress & DEV_ADDR_PENDING)
              {
                  //
                  // Clear the pending address change and set the address.
                  //
                  g_sUSBDeviceState.ui32DevAddress &= ~DEV_ADDR_PENDING;
                  HWREGB(USB0_BASE + USB_O_FADDR) =
                      (uint8_t)g_sUSBDeviceState.ui32DevAddress;
              }
  
              //
              // If a new packet is already pending, we need to read it
              // and handle whatever request it contains.
              //
              if(ui32EPStatus & USB_DEV_EP0_OUT_PKTRDY)
              {
                  //
                  // Process the newly arrived packet.
                  //
                  USBDReadAndDispatchRequest();
              }
              break;
          }
  
          //
          // In the IDLE state the code is waiting to receive data from the host.
          //
          case USB_STATE_IDLE:
          {
              //
              // Is there a packet waiting for us?
              //
              if(ui32EPStatus & USB_DEV_EP0_OUT_PKTRDY)
              {
                  //
                  // Yes - process it.
                  //
                  USBDReadAndDispatchRequest();
              }
              break;
          }
  
          //
          // Data is still being sent to the host so handle this in the
          // EP0StateTx() function.
          //
          case USB_STATE_TX:
          {
              USBDEP0StateTx();
              break;
          }
  
          //
          // Handle the receive state for commands that are receiving data on
          // endpoint zero.
          //
          case USB_STATE_RX:
          {
              uint32_t ui32DataSize;
  
              //
              // Set the number of bytes to get out of this next packet.
              //
              if(g_sUSBDeviceState.ui32EP0DataRemain > EP0_MAX_PACKET_SIZE)
              {
                  //
                  // Don't send more than EP0_MAX_PACKET_SIZE bytes.
                  //
                  ui32DataSize = EP0_MAX_PACKET_SIZE;
              }
              else
              {
                  //
                  // There was space so send the remaining bytes.
                  //
                  ui32DataSize = g_sUSBDeviceState.ui32EP0DataRemain;
              }
  
              //
              // Get the data from the USB controller end point 0.
              //
              USBEndpoint0DataGet(g_sUSBDeviceState.pui8EP0Data, &ui32DataSize);
  
              //
              // If there we not more that EP0_MAX_PACKET_SIZE or more bytes
              // remaining then this transfer is complete.  If there were exactly
              // EP0_MAX_PACKET_SIZE remaining then there still needs to be
              // null packet sent before this is complete.
              //
              if(g_sUSBDeviceState.ui32EP0DataRemain < EP0_MAX_PACKET_SIZE)
              {
                  //
                  // Need to ack the data on end point 0 in this case
                  // without setting data end.
                  //
                  USBDevEndpoint0DataAck(true);
  
                  //
                  // Return to the idle state.
                  //
                  g_eUSBDEP0State =  USB_STATE_IDLE;
  
                  //
                  // If there is a receive callback then call it.
                  //
                  if(g_sUSBDeviceState.ui32OUTDataSize != 0)
                  {
                      //
                      // Call the receive handler to handle the data
                      // that was received.
                      //
                      HandleEP0Data(g_sUSBDeviceState.ui32OUTDataSize);
  
                      //
                      // Indicate that there is no longer any data being waited
                      // on.
                      //
                      g_sUSBDeviceState.ui32OUTDataSize = 0;
                  }
              }
              else
              {
                  //
                  // Need to ack the data on end point 0 in this case
                  // without setting data end.
                  //
                  USBDevEndpoint0DataAck(false);
              }
  
              //
              // Advance the pointer.
              //
              g_sUSBDeviceState.pui8EP0Data += ui32DataSize;
  
              //
              // Decrement the number of bytes that are being waited on.
              //
              g_sUSBDeviceState.ui32EP0DataRemain -= ui32DataSize;
  
              break;
          }
          //
          // The device stalled endpoint zero so check if the stall needs to be
          // cleared once it has been successfully sent.
          //
          case USB_STATE_STALL:
          {
              //
              // If we sent a stall then acknowledge this interrupt.
              //
              if(ui32EPStatus & USB_DEV_EP0_SENT_STALL)
              {
                  //
                  // Clear the stall condition.
                  //
                  HWREGB(USB0_BASE + USB_O_CSRL0) &= ~(USB_DEV_EP0_SENT_STALL);
  
                  //
                  // Reset the global end point 0 state to IDLE.
                  //
                  g_eUSBDEP0State = USB_STATE_IDLE;
  
              }
              break;
          }
          //
          // Halt on an unknown state, but only in DEBUG mode builds.
          //
          default:
          {
  #ifdef DEBUG
              while(1);
  #endif
              break;
          }
      }
  }
  
  //*****************************************************************************
  //
  // This function handles bus reset notifications.
  //
  // This function is called from the low level USB interrupt handler whenever
  // a bus reset is detected.  It performs tidy-up as required and resets the
  // configuration back to defaults in preparation for descriptor queries from
  // the host.
  //
  // \return None.
  //
  //*****************************************************************************
  void
  USBDeviceEnumResetHandler(void)
  {
      //
      // Disable remote wakeup signalling (as per USB 2.0 spec 9.1.1.6).
      //
      g_sUSBDeviceState.ui8Status &= ~USB_STATUS_REMOTE_WAKE;
      g_sUSBDeviceState.bRemoteWakeup = false;
  
      //
      // Call the device dependent code to indicate a bus reset has occurred.
      //
      HandleReset();
  
      //
      // Reset the default configuration identifier and alternate function
      // selections.
      //
      g_sUSBDeviceState.ui32Configuration = DEFAULT_CONFIG_ID;
      g_sUSBDeviceState.ui8AltSetting = 0;
  }
  
  //*****************************************************************************
  //
  // This function handles the GET_STATUS standard USB request.
  //
  // \param pUSBRequest holds the request type and endpoint number if endpoint
  // status is requested.
  //
  // This function handles responses to a Get Status request from the host
  // controller.  A status request can be for the device, an interface or an
  // endpoint.  If any other type of request is made this function will cause
  // a stall condition to indicate that the command is not supported.  The
  // \e pUSBRequest structure holds the type of the request in the
  // bmRequestType field.  If the type indicates that this is a request for an
  // endpoint's status, then the wIndex field holds the endpoint number.
  //
  // \return None.
  //
  //*****************************************************************************
  static void
  USBDGetStatus(tUSBRequest *pUSBRequest)
  {
      uint16_t ui16Data;
  
      //
      // Determine what type of status was requested.
      //
      switch(pUSBRequest->bmRequestType & USB_RTYPE_RECIPIENT_M)
      {
          //
          // This was a Device Status request.
          //
          case USB_RTYPE_DEVICE:
          {
              //
              // Return the current status for the device.
              //
              ui16Data = g_sUSBDeviceState.ui8Status;
  
              break;
          }
  
          //
          // This was a Interface status request.
          //
          case USB_RTYPE_INTERFACE:
          {
              //
              // Interface status always returns 0.
              //
              ui16Data = 0;
  
              break;
          }
  
          //
          // This was an unknown request or a request for an endpoint (of which
          // we have none) so set a stall.
          //
          case USB_RTYPE_ENDPOINT:
          default:
          {
              //
              // Anything else causes a stall condition to indicate that the
              // command was not supported.
              //
              USBBLStallEP0();
              return;
          }
      }
  
      //
      // Send the two byte status response.
      //
      g_sUSBDeviceState.ui32EP0DataRemain = 2;
      g_sUSBDeviceState.pui8EP0Data = (uint8_t *)&ui16Data;
  
      //
      // Send the response.
      //
      USBDEP0StateTx();
  }
  
  //*****************************************************************************
  //
  // This function handles the CLEAR_FEATURE standard USB request.
  //
  // \param pUSBRequest holds the options for the Clear Feature USB request.
  //
  // This function handles device or endpoint clear feature requests.  The
  // \e pUSBRequest structure holds the type of the request in the bmRequestType
  // field and the feature is held in the wValue field.  For device, the only
  // clearable feature is the Remote Wake feature.  This device request
  // should only be made if the descriptor indicates that Remote Wake is
  // implemented by the device.  For endpoint requests the only clearable
  // feature is the ability to clear a halt on a given endpoint.  If any other
  // requests are made, then the device will stall the request to indicate to
  // the host that the command was not supported.
  //
  // \return None.
  //
  //*****************************************************************************
  static void
  USBDClearFeature(tUSBRequest *pUSBRequest)
  {
      //
      // Determine what type of status was requested.
      //
      switch(pUSBRequest->bmRequestType & USB_RTYPE_RECIPIENT_M)
      {
          //
          // This is a clear feature request at the device level.
          //
          case USB_RTYPE_DEVICE:
          {
              //
              // Only remote wake is clearable by this function.
              //
              if(USB_FEATURE_REMOTE_WAKE & pUSBRequest->wValue)
              {
                  //
                  // Clear the remote wake up state.
                  //
                  g_sUSBDeviceState.ui8Status &= ~USB_STATUS_REMOTE_WAKE;
  
                  //
                  // Need to ack the data on end point 0.
                  //
                  USBDevEndpoint0DataAck(true);
              }
              else
              {
                  USBBLStallEP0();
              }
              break;
          }
  
          //
          // This is an unknown request or one destined for an invalid endpoint.
          //
          case USB_RTYPE_ENDPOINT:
          default:
          {
              USBBLStallEP0();
              return;
          }
      }
  }
  
  //*****************************************************************************
  //
  // This function handles the SET_FEATURE standard USB request.
  //
  // \param pUSBRequest holds the feature in the wValue field of the USB
  // request.
  //
  // This function handles device or endpoint set feature requests.  The
  // \e pUSBRequest structure holds the type of the request in the bmRequestType
  // field and the feature is held in the wValue field.  For device, the only
  // settable feature is the Remote Wake feature.  This device request
  // should only be made if the descriptor indicates that Remote Wake is
  // implemented by the device.  For endpoint requests the only settable feature
  // is the ability to issue a halt on a given endpoint.  If any other requests
  // are made, then the device will stall the request to indicate to the host
  // that the command was not supported.
  //
  // \return None.
  //
  //*****************************************************************************
  static void
  USBDSetFeature(tUSBRequest *pUSBRequest)
  {
      //
      // Determine what type of status was requested.
      //
      switch(pUSBRequest->bmRequestType & USB_RTYPE_RECIPIENT_M)
      {
          //
          // This is a set feature request at the device level.
          //
          case USB_RTYPE_DEVICE:
          {
              //
              // Only remote wake is setable by this function.
              //
              if(USB_FEATURE_REMOTE_WAKE & pUSBRequest->wValue)
              {
                  //
                  // Set the remote wakeup state.
                  //
                  g_sUSBDeviceState.ui8Status |= USB_STATUS_REMOTE_WAKE;
  
                  //
                  // Need to ack the data on end point 0.
                  //
                  USBDevEndpoint0DataAck(true);
              }
              else
              {
                  USBBLStallEP0();
              }
              break;
          }
  
          //
          // This is an unknown request or one destined for an invalid endpoint.
          //
          case USB_RTYPE_ENDPOINT:
          default:
          {
              USBBLStallEP0();
              return;
          }
      }
  }
  
  //*****************************************************************************
  //
  // This function handles the SET_ADDRESS standard USB request.
  //
  // \param pUSBRequest holds the new address to use in the wValue field of the
  // USB request.
  //
  // This function is called to handle the change of address request from the
  // host controller.  This can only start the sequence as the host must
  // acknowledge that the device has changed address.  Thus this function sets
  // the address change as pending until the status phase of the request has
  // been completed successfully.  This prevents the devices address from
  // changing and not properly responding to the status phase.
  //
  // \return None.
  //
  //*****************************************************************************
  static void
  USBDSetAddress(tUSBRequest *pUSBRequest)
  {
      //
      // The data needs to be acknowledged on end point 0 without setting data
      // end because there is no data coming.
      //
      USBDevEndpoint0DataAck(true);
  
      //
      // Save the device address as we cannot change address until the status
      // phase is complete.
      //
      g_sUSBDeviceState.ui32DevAddress = pUSBRequest->wValue | DEV_ADDR_PENDING;
  
      //
      // Transition directly to the status state since there is no data phase
      // for this request.
      //
      g_eUSBDEP0State = USB_STATE_STATUS;
  
      //
      // Clear the DFU status just in case we were in an error state last time
      // the device was accessed and we were unplugged and replugged (for a self-
      // powered implementation, of course).
      //
      HandleSetAddress();
  }
  
  //*****************************************************************************
  //
  // This function handles the GET_DESCRIPTOR standard USB request.
  //
  // \param pUSBRequest holds the data for this request.
  //
  // This function will return all configured standard USB descriptors to the
  // host - device, config and string descriptors.  Any request for a descriptor
  // which is not available will result in endpoint 0 being stalled.
  //
  // \return None.
  //
  //*****************************************************************************
  static void
  USBDGetDescriptor(tUSBRequest *pUSBRequest)
  {
      uint32_t ui32Stall;
  
      //
      // Default to no stall.
      //
      ui32Stall = 0;
  
      //
      // Which descriptor are we being asked for?
      //
      switch(pUSBRequest->wValue >> 8)
      {
          //
          // This request was for a device descriptor.
          //
          case USB_DTYPE_DEVICE:
          {
              //
              // Return the externally provided device descriptor.
              //
              g_sUSBDeviceState.pui8EP0Data =
                  (uint8_t *)g_pui8DFUDeviceDescriptor;
  
              //
              // The size of the device descriptor is in the first byte.
              //
              g_sUSBDeviceState.ui32EP0DataRemain =
                  g_pui8DFUDeviceDescriptor[0];
              break;
          }
  
          //
          // This request was for a configuration descriptor.
          //
          case USB_DTYPE_CONFIGURATION:
          {
              uint8_t ui8Index;
  
              //
              // Which configuration are we being asked for?
              //
              ui8Index = (uint8_t)(pUSBRequest->wValue & 0xFF);
  
              //
              // Is this valid?
              //
              if(ui8Index != 0)
              {
                  //
                  // This is an invalid configuration index.  Stall EP0 to
                  // indicate a request error.
                  //
                  USBBLStallEP0();
                  g_sUSBDeviceState.pui8EP0Data = 0;
                  g_sUSBDeviceState.ui32EP0DataRemain = 0;
              }
              else
              {
                  //
                  // Start by sending data from the beginning of the first
                  // descriptor.
                  //
  
                  g_sUSBDeviceState.pui8EP0Data =
                      (uint8_t *)g_pui8DFUConfigDescriptor;
  
                  //
                  // Get the size of the config descriptor (remembering that in
                  // this case, we only have a single section)
                  //
                  g_sUSBDeviceState.ui32EP0DataRemain =
                      *(uint16_t *)&(g_pui8DFUConfigDescriptor[2]);
              }
              break;
          }
  
          //
          // This request was for a string descriptor.
          //
          case USB_DTYPE_STRING:
          {
              int32_t i32Index;
  
              //
              // Determine the correct descriptor index based on the requested
              // language ID and index.
              //
              i32Index = USBDStringIndexFromRequest(pUSBRequest->wIndex,
                                                    pUSBRequest->wValue & 0xFF);
  
              //
              // If the mapping function returned -1 then stall the request to
              // indicate that the request was not valid.
              //
              if(i32Index == -1)
              {
                  USBBLStallEP0();
                  break;
              }
  
              //
              // Return the externally specified configuration descriptor.
              //
              g_sUSBDeviceState.pui8EP0Data =
                  (uint8_t *)g_ppui8StringDescriptors[i32Index];
  
              //
              // The total size of a string descriptor is in byte 0.
              //
              g_sUSBDeviceState.ui32EP0DataRemain =
                  g_ppui8StringDescriptors[i32Index][0];
  
              break;
          }
  
          //
          // Any other request is not handled by the default enumeration handler
          // so see if it needs to be passed on to another handler.
          //
          default:
          {
              //
              // All other requests are not handled.
              //
              USBBLStallEP0();
              ui32Stall = 1;
              break;
          }
      }
  
      //
      // If there was no stall, ACK the data and see if data needs to be sent.
      //
      if(ui32Stall == 0)
      {
          //
          // Need to ack the data on end point 0 in this case without
          // setting data end.
          //
          USBDevEndpoint0DataAck(false);
  
          //
          // If this request has data to send, then send it.
          //
          if(g_sUSBDeviceState.pui8EP0Data)
          {
              //
              // If there is more data to send than is requested then just
              // send the requested amount of data.
              //
              if(g_sUSBDeviceState.ui32EP0DataRemain > pUSBRequest->wLength)
              {
                  g_sUSBDeviceState.ui32EP0DataRemain = pUSBRequest->wLength;
              }
  
              //
              // Now in the transmit data state.  Be careful to call the correct
              // function since we need to handle the config descriptor
              // differently from the others.
              //
              USBDEP0StateTx();
          }
      }
  }
  
  //*****************************************************************************
  //
  // This function determines which string descriptor to send to satisfy a
  // request for a given index and language.
  //
  // \param ui16Lang is the requested string language ID.
  // \param ui16Index is the requested string descriptor index.
  //
  // When a string descriptor is requested, the host provides a language ID and
  // index to identify the string ("give me string number 5 in French").  This
  // function maps these two parameters to an index within our device's string
  // descriptor array which is arranged as multiple groups of strings with
  // one group for each language advertised via string descriptor 0.
  //
  // We assume that there are an equal number of strings per language and
  // that the first descriptor is the language descriptor and use this fact to
  // perform the mapping.
  //
  // \return The index of the string descriptor to return or -1 if the string
  // could not be found.
  //
  //*****************************************************************************
  static int32_t
  USBDStringIndexFromRequest(uint16_t ui16Lang, uint16_t ui16Index)
  {
      tString0Descriptor *pLang;
      uint32_t ui32NumLangs;
      uint32_t ui32NumStringsPerLang;
      uint32_t ui32Loop;
  
      //
      // First look for the trivial case where descriptor 0 is being
      // requested.  This is the special case since descriptor 0 contains the
      // language codes supported by the device.
      //
      if(ui16Index == 0)
      {
          return(0);
      }
  
      //
      // How many languages does this device support?  This is determined by
      // looking at the length of the first descriptor in the string table,
      // subtracting 2 for the header and dividing by two (the size of each
      // language code).
      //
      ui32NumLangs = (g_ppui8StringDescriptors[0][0] - 2) / 2;
  
      //
      // We assume that the table includes the same number of strings for each
      // supported language.  We know the number of entries in the string table,
      // so how many are there for each language?  This may seem an odd way to
      // do this (why not just have the application tell us in the device info
      // structure?) but it's needed since we didn't want to change the API
      // after the first release which did not support multiple languages.
      //
      ui32NumStringsPerLang = ((NUM_STRING_DESCRIPTORS - 1) / ui32NumLangs);
  
      //
      // Just to be sure, make sure that the calculation indicates an equal
      // number of strings per language.  We expect the string table to contain
      // (1 + (strings_per_language * languages)) entries.
      //
      if((1 + (ui32NumStringsPerLang * ui32NumLangs)) != NUM_STRING_DESCRIPTORS)
      {
          return(-1);
      }
  
      //
      // Now determine which language we are looking for.  It is assumed that
      // the order of the groups of strings per language in the table is the
      // same as the order of the language IDs listed in the first descriptor.
      //
      pLang = (tString0Descriptor *)(g_ppui8StringDescriptors[0]);
  
      //
      // Look through the supported languages looking for the one we were asked
      // for.
      //
      for(ui32Loop = 0; ui32Loop < ui32NumLangs; ui32Loop++)
      {
          //
          // Have we found the requested language?
          //
          if(pLang->wLANGID[ui32Loop] == ui16Lang)
          {
              //
              // Yes - calculate the index of the descriptor to send.
              //
              return((ui32NumStringsPerLang * ui32Loop) + ui16Index);
          }
      }
  
      //
      // If we drop out of the loop, the requested language was not found so
      // return -1 to indicate the error.
      //
      return(-1);
  }
  
  //*****************************************************************************
  //
  // This function handles the SET_DESCRIPTOR standard USB request.
  //
  // \param pUSBRequest holds the data for this request.
  //
  // This function currently is not supported and will respond with a Stall
  // to indicate that this command is not supported by the device.
  //
  // \return None.
  //
  //*****************************************************************************
  static void
  USBDSetDescriptor(tUSBRequest *pUSBRequest)
  {
      //
      // This function is not handled by default.
      //
      USBBLStallEP0();
  }
  
  //*****************************************************************************
  //
  // This function handles the GET_CONFIGURATION standard USB request.
  //
  // \param pUSBRequest holds the data for this request.
  //
  // This function responds to a host request to return the current
  // configuration of the USB device.  The function will send the configuration
  // response to the host and return.  This value will either be 0 or the last
  // value received from a call to SetConfiguration().
  //
  // \return None.
  //
  //*****************************************************************************
  static void
  USBDGetConfiguration(tUSBRequest *pUSBRequest)
  {
      uint8_t ui8Value;
  
      //
      // If we still have an address pending then the device is still not
      // configured.
      //
      if(g_sUSBDeviceState.ui32DevAddress & DEV_ADDR_PENDING)
      {
          ui8Value = 0;
      }
      else
      {
          ui8Value = (uint8_t)g_sUSBDeviceState.ui32Configuration;
      }
  
      g_sUSBDeviceState.ui32EP0DataRemain = 1;
      g_sUSBDeviceState.pui8EP0Data = &ui8Value;
  
      //
      // Send the single byte response.
      //
      USBDEP0StateTx();
  }
  
  //*****************************************************************************
  //
  // This function handles the SET_CONFIGURATION standard USB request.
  //
  // \param pUSBRequest holds the data for this request.
  //
  // This function responds to a host request to change the current
  // configuration of the USB device.  The actual configuration number is taken
  // from the structure passed in via \e pUSBRequest.
  //
  // \return None.
  //
  //*****************************************************************************
  static void
  USBDSetConfiguration(tUSBRequest *pUSBRequest)
  {
      //
      // Cannot set the configuration to one that does not exist so check the
      // enumeration structure to see how many valid configurations are present.
      //
      if(pUSBRequest->wValue > 1)
      {
          //
          // The passed configuration number is not valid.  Stall the endpoint to
          // signal the error to the host.
          //
          USBBLStallEP0();
      }
      else
      {
          //
          // Need to ack the data on end point 0.
          //
          USBDevEndpoint0DataAck(true);
  
          //
          // Save the configuration.
          //
          g_sUSBDeviceState.ui32Configuration = pUSBRequest->wValue;
  
          //
          // If passed a configuration other than 0 (which tells us that we are
          // not currently configured), configure the endpoints (other than EP0)
          // appropriately.
          //
          if(g_sUSBDeviceState.ui32Configuration)
          {
              //
              // Set the power state
              //
  #if USB_BUS_POWERED
              g_sUSBDeviceState.ui8Status &= ~USB_STATUS_SELF_PWR;
  #else
              g_sUSBDeviceState.ui8Status |= USB_STATUS_SELF_PWR;
  #endif
          }
  
          //
          // Do whatever needs to be done as a result of the config change.
          //
          HandleConfigChange(g_sUSBDeviceState.ui32Configuration);
      }
  }
  
  //*****************************************************************************
  //
  // This function handles the GET_INTERFACE standard USB request.
  //
  // \param pUSBRequest holds the data for this request.
  //
  // This function is called when the host controller request the current
  // interface that is in use by the device.  This simply returns the value set
  // by the last call to SetInterface().
  //
  // \return None.
  //
  //*****************************************************************************
  static void
  USBDGetInterface(tUSBRequest *pUSBRequest)
  {
      uint8_t ui8Value;
  
      //
      // If we still have an address pending then the device is still not
      // configured.
      //
      if(g_sUSBDeviceState.ui32DevAddress & DEV_ADDR_PENDING)
      {
          ui8Value = 0;
      }
      else
      {
          //
          // Is the interface number valid?
          //
          if(pUSBRequest->wIndex == 0)
          {
              //
              // Read the current alternate setting for the required interface.
              //
              ui8Value = g_sUSBDeviceState.ui8AltSetting;
          }
          else
          {
              //
              // An invalid interface number was specified.
              //
              USBBLStallEP0();
              return;
          }
      }
  
      //
      // Send the single byte response.
      //
      g_sUSBDeviceState.ui32EP0DataRemain = 1;
      g_sUSBDeviceState.pui8EP0Data = &ui8Value;
  
      //
      // Send the single byte response.
      //
      USBDEP0StateTx();
  }
  
  //*****************************************************************************
  //
  // This function handles the SET_INTERFACE standard USB request.
  //
  // \param pUSBRequest holds the data for this request.
  //
  // The DFU device supports a single interface with no alternate settings so
  // this handler is hardcoded assuming this configuration.
  //
  // \return None.
  //
  //*****************************************************************************
  static void
  USBDSetInterface(tUSBRequest *pUSBRequest)
  {
      if((pUSBRequest->wIndex == 0) && (pUSBRequest->wValue == 0))
      {
          //
          // We were passed a valid interface number so acknowledge the request.
          //
          USBDevEndpoint0DataAck(true);
      }
      else
      {
          //
          // The values passed were not valid so stall endpoint 0.
          //
          USBBLStallEP0();
      }
  }
  
  //*****************************************************************************
  //
  // This internal function handles sending data on endpoint zero.
  //
  // \return None.
  //
  //*****************************************************************************
  static void
  USBDEP0StateTx(void)
  {
      uint32_t ui32NumBytes;
      uint8_t *pui8Data;
  
      //
      // In the TX state on endpoint zero.
      //
      g_eUSBDEP0State = USB_STATE_TX;
  
      //
      // Set the number of bytes to send this iteration.
      //
      ui32NumBytes = g_sUSBDeviceState.ui32EP0DataRemain;
  
      //
      // Limit individual transfers to 64 bytes.
      //
      if(ui32NumBytes > EP0_MAX_PACKET_SIZE)
      {
          ui32NumBytes = EP0_MAX_PACKET_SIZE;
      }
  
      //
      // Save the pointer so that it can be passed to the USBEndpointDataPut()
      // function.
      //
      pui8Data = g_sUSBDeviceState.pui8EP0Data;
  
      //
      // Advance the data pointer and counter to the next data to be sent.
      //
      g_sUSBDeviceState.ui32EP0DataRemain -= ui32NumBytes;
      g_sUSBDeviceState.pui8EP0Data += ui32NumBytes;
  
      //
      // Put the data in the correct FIFO.
      //
      USBEndpoint0DataPut(pui8Data, ui32NumBytes);
  
      //
      // If this is exactly 64 then don't set the last packet yet.
      //
      if(ui32NumBytes == EP0_MAX_PACKET_SIZE)
      {
          //
          // There is more data to send or exactly 64 bytes were sent, this
          // means that there is either more data coming or a null packet needs
          // to be sent to complete the transaction.
          //
          USBEndpoint0DataSend(USB_TRANS_IN);
      }
      else
      {
          //
          // Now go to the status state and wait for the transmit to complete.
          //
          g_eUSBDEP0State = USB_STATE_STATUS;
  
          //
          // Send the last bit of data.
          //
          USBEndpoint0DataSend(USB_TRANS_IN_LAST);
          g_sUSBDeviceState.ui32OUTDataSize = 0;
      }
  }
  
  //*****************************************************************************
  //
  // Close the Doxygen group.
  //! @}
  //
  //*****************************************************************************
  #endif // USB_ENABLE_UPDATE
  