From 4085ae3ddfbbf10c8ccbd3dccd43452c40a1fe40 Mon Sep 17 00:00:00 2001 From: Yuval Adam Date: Fri, 13 Mar 2015 12:24:52 +0200 Subject: Add bootloader, nfclib and sensorlib --- boot_loader/bl_usbfuncs.c | 1915 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1915 insertions(+) create mode 100644 boot_loader/bl_usbfuncs.c (limited to 'boot_loader/bl_usbfuncs.c') diff --git a/boot_loader/bl_usbfuncs.c b/boot_loader/bl_usbfuncs.c new file mode 100644 index 0000000..552fabc --- /dev/null +++ b/boot_loader/bl_usbfuncs.c @@ -0,0 +1,1915 @@ +//***************************************************************************** +// +// bl_usbfuncs.c - The subset of USB library functions required by the USB DFU +// boot loader. +// +// Copyright (c) 2008-2014 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.0.12573 of the Tiva Firmware Development Package. +// +//***************************************************************************** + +#include +#include +#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 + ); + + // + // 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_RCGC2) |= 0x10000; + + // + // Turn on USB Phy clock. + // + HWREG(SYSCTL_RCC2) &= ~SYSCTL_RCC2_USBPWRDN; + + // + // 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 -- cgit v1.3.1