From c241dbd7e78c50327781a35d88d9f2db7ff2b271 Mon Sep 17 00:00:00 2001 From: Yuval Adam Date: Mon, 29 Oct 2012 23:08:53 +0200 Subject: Added usblib --- usblib/device/usbdenum.c | 2900 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2900 insertions(+) create mode 100644 usblib/device/usbdenum.c (limited to 'usblib/device/usbdenum.c') diff --git a/usblib/device/usbdenum.c b/usblib/device/usbdenum.c new file mode 100644 index 0000000..e8aa64e --- /dev/null +++ b/usblib/device/usbdenum.c @@ -0,0 +1,2900 @@ +//***************************************************************************** +// +// usbenum.c - Enumeration code to handle all endpoint zero traffic. +// +// Copyright (c) 2007-2012 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 9453 of the Stellaris USB Library. +// +//***************************************************************************** + +#include "inc/hw_ints.h" +#include "inc/hw_memmap.h" +#include "inc/hw_types.h" +#include "driverlib/debug.h" +#include "driverlib/interrupt.h" +#include "driverlib/rom.h" +#include "driverlib/rom_map.h" +#include "driverlib/sysctl.h" +#include "driverlib/usb.h" +#include "driverlib/rtos_bindings.h" +#include "usblib/usblib.h" +#include "usblib/device/usbdevice.h" +#include "usblib/device/usbdevicepriv.h" +#include "usblib/usblibpriv.h" + +//***************************************************************************** +// +// External prototypes. +// +//***************************************************************************** +extern tUSBMode g_eUSBMode; + +//***************************************************************************** +// +// Local functions prototypes. +// +//***************************************************************************** +static void USBDGetStatus(void *pvInstance, tUSBRequest *pUSBRequest); +static void USBDClearFeature(void *pvInstance, tUSBRequest *pUSBRequest); +static void USBDSetFeature(void *pvInstance, tUSBRequest *pUSBRequest); +static void USBDSetAddress(void *pvInstance, tUSBRequest *pUSBRequest); +static void USBDGetDescriptor(void *pvInstance, tUSBRequest *pUSBRequest); +static void USBDSetDescriptor(void *pvInstance, tUSBRequest *pUSBRequest); +static void USBDGetConfiguration(void *pvInstance, + tUSBRequest *pUSBRequest); +static void USBDSetConfiguration(void *pvInstance, + tUSBRequest *pUSBRequest); +static void USBDGetInterface(void *pvInstance, tUSBRequest *pUSBRequest); +static void USBDSetInterface(void *pvInstance, tUSBRequest *pUSBRequest); +static void USBDSyncFrame(void *pvInstance, tUSBRequest *pUSBRequest); +static void USBDEP0StateTx(unsigned long ulIndex); +static void USBDEP0StateTxConfig(unsigned long ulIndex); +static long USBDStringIndexFromRequest(unsigned short usLang, + unsigned short usIndex); + +//***************************************************************************** +// +//! \addtogroup device_api +//! @{ +// +//***************************************************************************** + +//***************************************************************************** +// +//! The default USB endpoint FIFO configuration structure. This structure +//! contains definitions to set all USB FIFOs into single buffered mode with +//! no DMA use. Each endpoint's FIFO is sized to hold the largest maximum +//! packet size for any interface alternate setting in the current +//! configuration descriptor. A pointer to this structure may be passed in the +//! psFIFOConfig field of the tDeviceInfo structure passed to USBCDCInit if the +//! application does not require any special handling of the USB controller +//! FIFO. +// +//***************************************************************************** +const tFIFOConfig g_sUSBDefaultFIFOConfig = +{ + { + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 } + }, + { + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 }, + { false, 0 } + }, +}; + +//***************************************************************************** +// +// Indices into the ucHalt array to select the IN or OUT endpoint group. +// +//***************************************************************************** +#define HALT_EP_IN 0 +#define HALT_EP_OUT 1 + +//***************************************************************************** +// +// Define the max packet size for endpoint zero. +// +//***************************************************************************** +#define EP0_MAX_PACKET_SIZE 64 + +//***************************************************************************** +// +// This is a flag used with g_sUSBDeviceState.ulDevAddress 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. This may be overridden by calling USBDCDSetDefaultConfiguration() +// during processing of the device reset handler if required. +// +//***************************************************************************** +#define DEFAULT_CONFIG_ID 1 + +//***************************************************************************** +// +// This label defines the number of milliseconds that the remote wake up signal +// must remain asserted before removing it. Section 7.1.7.7 of the USB 2.0 spec +// states that "the remote wake up 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 wake up 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 signaling when the device's initial signal is +// detected) must hold the resume signaling for at least 20mS. +// +//***************************************************************************** +#define REMOTE_WAKEUP_READY_MS 20 + +//***************************************************************************** +// +// The buffer for reading data coming into EP0 +// +//***************************************************************************** +static unsigned char g_pucDataBufferIn[EP0_MAX_PACKET_SIZE]; + +tDeviceInstance g_psUSBDevice[1]; + +//***************************************************************************** +// +// Function table to handle standard requests. +// +//***************************************************************************** +static const tStdRequest g_psUSBDStdRequests[] = +{ + USBDGetStatus, + USBDClearFeature, + 0, + USBDSetFeature, + 0, + USBDSetAddress, + USBDGetDescriptor, + USBDSetDescriptor, + USBDGetConfiguration, + USBDSetConfiguration, + USBDGetInterface, + USBDSetInterface, + USBDSyncFrame +}; + +//***************************************************************************** +// +// Functions accessible by USBLIB clients. +// +//***************************************************************************** + +//***************************************************************************** +// +//! Initialize the USB library device control driver for a given hardware +//! controller. +//! +//! \param ulIndex is the index of the USB controller which is to be +//! initialized. +//! \param psDevice is a pointer to a structure containing information that +//! the USB library requires to support operation of this application's +//! device. The structure contains event handler callbacks and pointers to the +//! various standard descriptors that the device wishes to publish to the +//! host. +//! +//! This function must be called by any application which wishes to operate +//! as a USB device. It initializes the USB device control driver for the +//! given controller and saves the device information for future use. Prior to +//! returning from this function, the device is connected to the USB bus. +//! Following return, the caller can expect to receive a callback to the +//! supplied pfnResetHandler function when a host connects to the +//! device. +//! +//! The device information structure passed in \e psDevice must remain +//! unchanged between this call and any matching call to USBDCDTerm() since +//! it is not copied by the USB library. +//! +//! The USBStackModeSet() function can be called with USB_MODE_FORCE_DEVICE in +//! order to cause the USB library to force the USB operating mode to a device +//! controller. This allows the application to used the USBVBUS and USBID pins +//! as GPIOs on devices that support forcing OTG to operate as a device only +//! controller. By default the USB library will assume that the USBVBUS and +//! USBID pins are configured as USB pins and not GPIOs. +//! +//! \note Forcing of the USB controller mode feature is not available on all +//! Stellaris microcontrollers. Consult the data sheet for the microcontroller +//! that the application is using to determine if this feature is available. +//! +//! \return None. +// +//***************************************************************************** +void +USBDCDInit(unsigned long ulIndex, tDeviceInfo *psDevice) +{ + const tConfigHeader *psHdr; + const tConfigDescriptor *psDesc; + + // + // Check the arguments. + // + ASSERT(ulIndex == 0); + ASSERT(psDevice != 0); + + // + // Should not call this if the stack is in host mode. + // + ASSERT(g_eUSBMode != USB_MODE_HOST); + ASSERT(g_eUSBMode != USB_MODE_FORCE_HOST); + + // + // Initialize a couple of fields in the device state structure. + // + g_psUSBDevice[0].ulConfiguration = DEFAULT_CONFIG_ID; + g_psUSBDevice[0].ulDefaultConfiguration = DEFAULT_CONFIG_ID; + + // + // Remember the device information pointer. + // + g_psUSBDevice[0].psInfo = psDevice; + g_psUSBDevice[0].pvInstance = psDevice->pvInstance; + g_psUSBDevice[0].eEP0State = USB_STATE_IDLE; + + // + // Default to device mode if no mode was set. + // + if(g_eUSBMode == USB_MODE_NONE) + { + g_eUSBMode = USB_MODE_DEVICE; + } + + // + // Only do hardware update if the stack is in not in OTG mode. + // + if(g_eUSBMode != USB_MODE_OTG) + { + // + // Reset the USB controller. + // + MAP_SysCtlPeripheralReset(SYSCTL_PERIPH_USB0); + + // + // Enable Clocking to the USB controller. + // + MAP_SysCtlPeripheralEnable(SYSCTL_PERIPH_USB0); + + // + // Turn on USB Phy clock. + // + MAP_SysCtlUSBPLLEnable(); + + // + // If the application is requesting OTG or DEVICE mode then set the mode + // detection to OTG. If the mode was device then the rest of the library + // should make sure that no OTG signaling actually occurs. + // + if((g_eUSBMode == USB_MODE_DEVICE) || (g_eUSBMode == USB_MODE_OTG)) + { + // + // Switch to OTG mode to detect VBUS changes. + // + MAP_USBOTGMode(USB0_BASE); + } + else + { + // + // Force device mode on devices that support forcing mode. + // + MAP_USBDevMode(USB0_BASE); + } + + // + // In all other cases, set the mode to device this function should not + // be called in OTG mode. + // + g_eUSBMode = USB_MODE_DEVICE; + } + + // + // Initialize the USB tick module. + // + InternalUSBTickInit(); + + // + // Get a pointer to the default configuration descriptor. + // + psHdr = psDevice->ppConfigDescriptors[ + g_psUSBDevice[0].ulDefaultConfiguration - 1]; + psDesc = (const tConfigDescriptor *)(psHdr->psSections[0]->pucData); + + // + // Default to the state where remote wake up is disabled. + // + g_psUSBDevice[0].ucStatus = 0; + g_psUSBDevice[0].bRemoteWakeup = false; + + // + // Determine the self- or bus-powered state based on the flags the + // user provided. + // + g_psUSBDevice[0].bPwrSrcSet = false; + + if((psDesc->bmAttributes & USB_CONF_ATTR_PWR_M) == USB_CONF_ATTR_SELF_PWR) + { + g_psUSBDevice[0].ucStatus |= USB_STATUS_SELF_PWR; + } + else + { + g_psUSBDevice[0].ucStatus &= ~USB_STATUS_SELF_PWR; + } + + // + // Only do hardware update if the stack is not in OTG mode. + // + if(g_eUSBMode != USB_MODE_OTG) + { + // + // Get the current interrupt status.to clear all pending USB interrupts. + // + MAP_USBIntStatusControl(USB0_BASE); + MAP_USBIntStatusEndpoint(USB0_BASE); + + // + // Enable USB Interrupts. + // + MAP_USBIntEnableControl(USB0_BASE, USB_INTCTRL_RESET | + USB_INTCTRL_DISCONNECT | + USB_INTCTRL_RESUME | + USB_INTCTRL_SUSPEND | + USB_INTCTRL_SOF); + MAP_USBIntEnableEndpoint(USB0_BASE, USB_INTEP_ALL); + + // + // Make sure we disconnect from the host for a while. This ensures + // that the host will enumerate us even if we were previously + // connected to the bus. + // + MAP_USBDevDisconnect(USB0_BASE); + + // + // Wait about 100mS. + // + OS_DELAY(MAP_SysCtlClockGet() / 30); + + // + // Attach the device using the soft connect. + // + MAP_USBDevConnect(USB0_BASE); + + // + // Enable the USB interrupt. + // + OS_INT_ENABLE(INT_USB0); + } +} + +//***************************************************************************** +// +//! Free the USB library device control driver for a given hardware controller. +//! +//! \param ulIndex is the index of the USB controller which is to be +//! freed. +//! +//! This function should be called by an application if it no longer requires +//! the use of a given USB controller to support its operation as a USB device. +//! It frees the controller for use by another client. +//! +//! It is the caller's responsibility to remove its device from the USB bus +//! prior to calling this function. +//! +//! \return None. +// +//***************************************************************************** +void +USBDCDTerm(unsigned long ulIndex) +{ + // + // Check the arguments. + // + ASSERT(ulIndex == 0); + + // + // Disable the USB interrupts. + // + OS_INT_DISABLE(INT_USB0); + + // + // Reset the tick handlers so that they can be reconfigured when and if + // USBDCDInit() is called. + // + InternalUSBTickReset(); + + g_psUSBDevice[0].psInfo = (tDeviceInfo *)0; + g_psUSBDevice[0].pvInstance = 0; + + MAP_USBIntDisableControl(USB0_BASE, USB_INTCTRL_ALL); + MAP_USBIntDisableEndpoint(USB0_BASE, USB_INTEP_ALL); + + // + // Detach the device using the soft connect. + // + MAP_USBDevDisconnect(USB0_BASE); + + // + // Clear any pending interrupts. + // + MAP_USBIntStatusControl(USB0_BASE); + MAP_USBIntStatusEndpoint(USB0_BASE); + + // + // Turn off USB Phy clock. + // + MAP_SysCtlUSBPLLDisable(); + + // + // Disable the USB peripheral + // + MAP_SysCtlPeripheralDisable(SYSCTL_PERIPH_USB0); +} + +//***************************************************************************** +// +//! This function starts the request for data from the host on endpoint zero. +//! +//! \param ulIndex is the index of the USB controller from which the data +//! is being requested. +//! \param pucData is a pointer to the buffer to fill with data from the USB +//! host. +//! \param ulSize 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. If the application needs notification +//! when the data has been received, +//! tDeviceInfo.sCallbacks.pfnDataReceived should contain valid +//! function pointer. In nearly all cases this is necessary because the caller +//! of this function would likely need to know that the data requested was +//! received. +//! +//! \return None. +// +//***************************************************************************** +void +USBDCDRequestDataEP0(unsigned long ulIndex, unsigned char *pucData, + unsigned long ulSize) +{ + ASSERT(ulIndex == 0); + + // + // Enter the RX state on end point 0. + // + g_psUSBDevice[0].eEP0State = USB_STATE_RX; + + // + // Save the pointer to the data. + // + g_psUSBDevice[0].pEP0Data = pucData; + + // + // Location to save the current number of bytes received. + // + g_psUSBDevice[0].ulOUTDataSize = ulSize; + + // + // Bytes remaining to be received. + // + g_psUSBDevice[0].ulEP0DataRemain = ulSize; +} + +//***************************************************************************** +// +//! This function requests transfer of data to the host on endpoint zero. +//! +//! \param ulIndex is the index of the USB controller which is to be used to +//! send the data. +//! \param pucData is a pointer to the buffer to send via endpoint zero. +//! \param ulSize 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. If +//! the application needs notification when this is complete, +//! tDeviceInfo.sCallbacks.pfnDataSent should contain a valid function +//! pointer. This callback could be used to free up the buffer passed into +//! this function in the \e pucData parameter. The contents of the \e pucData +//! buffer must remain unchanged until the pfnDataSent callback is +//! received. +//! +//! \return None. +// +//***************************************************************************** +void +USBDCDSendDataEP0(unsigned long ulIndex, unsigned char *pucData, + unsigned long ulSize) +{ + ASSERT(ulIndex == 0); + + // + // Return the externally provided device descriptor. + // + g_psUSBDevice[0].pEP0Data = pucData; + + // + // The size of the device descriptor is in the first byte. + // + g_psUSBDevice[0].ulEP0DataRemain = ulSize; + + // + // Save the total size of the data sent. + // + g_psUSBDevice[0].ulOUTDataSize = ulSize; + + // + // Now in the transmit data state. + // + USBDEP0StateTx(0); +} + +//***************************************************************************** +// +//! This function sets the default configuration for the device. +//! +//! \param ulIndex is the index of the USB controller whose default +//! configuration is to be set. +//! \param ulDefaultConfig is the configuration identifier (byte 6 of the +//! standard configuration descriptor) which is to be presented to the host +//! as the default configuration in cases where the configuration descriptor is +//! queried prior to any specific configuration being set. +//! +//! This function allows a device to override the default configuration +//! descriptor that will be returned to a host whenever it is queried prior +//! to a specific configuration having been set. The parameter passed must +//! equal one of the configuration identifiers found in the +//! ppConfigDescriptors array for the device. +//! +//! If this function is not called, the USB library will return the first +//! configuration in the ppConfigDescriptors array as the default +//! configuration. +//! +//! \note The USB device stack assumes that the configuration IDs (byte 6 of +//! the configuration descriptor, bConfigurationValue) stored within +//! the configuration descriptor array, ppConfigDescriptors, +//! are equal to the array index + 1. In other words, the first entry in the +//! array must contain a descriptor with bConfigurationValue 1, the +//! second must have bConfigurationValue 2 and so on. +//! +//! \return None. +// +//***************************************************************************** +void +USBDCDSetDefaultConfiguration(unsigned long ulIndex, + unsigned long ulDefaultConfig) +{ + ASSERT(ulIndex == 0); + + g_psUSBDevice[0].ulDefaultConfiguration = ulDefaultConfig; +} + +//***************************************************************************** +// +//! This function generates a stall condition on endpoint zero. +//! +//! \param ulIndex is the index of the USB controller whose endpoint zero is to +//! be stalled. +//! +//! 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 in the USB library. +//! +//! \return None. +// +//***************************************************************************** +void +USBDCDStallEP0(unsigned long ulIndex) +{ + ASSERT(ulIndex == 0); + + // + // Stall the endpoint in question. + // + MAP_USBDevEndpointStall(USB0_BASE, USB_EP_0, USB_EP_DEV_OUT); + + // + // Enter the stalled state. + // + g_psUSBDevice[0].eEP0State = USB_STATE_STALL; +} + +//***************************************************************************** +// +//! Reports the device power status (bus- or self-powered) to the library. +//! +//! \param ulIndex is the index of the USB controller whose device power +//! status is being reported. +//! \param ucPower indicates the current power status, either \b +//! USB_STATUS_SELF_PWR or \b USB_STATUS_BUS_PWR. +//! +//! Applications which support switching between bus- or self-powered +//! operation should call this function whenever the power source changes +//! to indicate the current power status to the USB library. This information +//! is required by the library to allow correct responses to be provided when +//! the host requests status from the device. +//! +//! \return None. +// +//***************************************************************************** +void +USBDCDPowerStatusSet(unsigned long ulIndex, unsigned char ucPower) +{ + // + // Check for valid parameters. + // + ASSERT((ucPower == USB_STATUS_BUS_PWR) || + (ucPower == USB_STATUS_SELF_PWR)); + ASSERT(ulIndex == 0); + + // + // Update the device status with the new power status flag. + // + g_psUSBDevice[0].bPwrSrcSet = true; + g_psUSBDevice[0].ucStatus &= ~USB_STATUS_PWR_M; + g_psUSBDevice[0].ucStatus |= ucPower; +} + +//***************************************************************************** +// +//! Requests a remote wake up to resume communication when in suspended state. +//! +//! \param ulIndex is the index of the USB controller that will request +//! a bus wake up. +//! +//! When the bus is suspended, an application which supports remote wake up +//! (advertised to the host via the configuration descriptor) may call this +//! function to initiate remote wake up signaling to the host. If the remote +//! wake up feature has not been disabled by the host, this will cause the bus +//! to resume operation within 20mS. If the host has disabled remote wake up, +//! \b false will be returned to indicate that the wake up request was not +//! successful. +//! +//! \return Returns \b true if the remote wake up is not disabled and the +//! signaling was started or \b false if remote wake up is disabled or if +//! signaling is currently ongoing following a previous call to this function. +// +//***************************************************************************** +tBoolean +USBDCDRemoteWakeupRequest(unsigned long ulIndex) +{ + // + // Check for parameter validity. + // + ASSERT(ulIndex == 0); + + // + // Is remote wake up signaling currently enabled? + // + if(g_psUSBDevice[0].ucStatus & USB_STATUS_REMOTE_WAKE) + { + // + // The host has not disabled remote wake up. Are we still in the + // middle of a previous wake up sequence? + // + if(!g_psUSBDevice[0].bRemoteWakeup) + { + // + // No - we are not in the middle of a wake up sequence so start + // one here. + // + g_psUSBDevice[0].ucRemoteWakeupCount = 0; + g_psUSBDevice[0].bRemoteWakeup = true; + MAP_USBHostResume(USB0_BASE, true); + return(true); + } + } + + // + // If we drop through to here, signaling was not initiated so return + // false. + return(false); +} + +//***************************************************************************** +// +// Internal Functions, not to be called by applications +// +//***************************************************************************** + +//***************************************************************************** +// +// This internal function is called on the SOF interrupt to process any +// outstanding remote wake up requests. +// +// \return None. +// +//***************************************************************************** +void +USBDeviceResumeTickHandler(tDeviceInstance *psDevInst) +{ + if(g_psUSBDevice[0].bRemoteWakeup) + { + // + // Increment the millisecond counter we use to time the resume + // signaling. + // + g_psUSBDevice[0].ucRemoteWakeupCount++; + + // + // Have we reached the 10mS mark? If so, we need to turn the signaling + // off again. + // + if(g_psUSBDevice[0].ucRemoteWakeupCount == REMOTE_WAKEUP_PULSE_MS) + { + MAP_USBHostResume(USB0_BASE, false); + } + + // + // Have we reached the point at which we can tell the client that the + // bus has resumed? The controller doesn't give us an interrupt if we + // initiated the wake up signaling so we just wait until 20mS have + // passed then tell the client all is well. + // + if(g_psUSBDevice[0].ucRemoteWakeupCount == REMOTE_WAKEUP_READY_MS) + { + // + // We are now finished with the remote wake up signaling. + // + g_psUSBDevice[0].bRemoteWakeup = false; + + // + // If the client has registered a resume callback, call it. In the + // case of a remote wake up request, we do not get a resume + // interrupt from the controller so we need to fake it here. + // + if(g_psUSBDevice[0].psInfo->sCallbacks.pfnResumeHandler) + { + g_psUSBDevice[0].psInfo->sCallbacks.pfnResumeHandler( + g_psUSBDevice[0].pvInstance); + } + } + } +} + +//***************************************************************************** +// +// 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(unsigned long ulIndex) +{ + unsigned long ulSize; + tUSBRequest *pRequest; + + // + // Cast the buffer to a request structure. + // + pRequest = (tUSBRequest *)g_pucDataBufferIn; + + // + // Set the buffer size. + // + ulSize = EP0_MAX_PACKET_SIZE; + + // + // Get the data from the USB controller end point 0. + // + MAP_USBEndpointDataGet(USB0_BASE, + USB_EP_0, + g_pucDataBufferIn, + &ulSize); + + // + // If there was a null setup packet then just return. + // + if(!ulSize) + { + return; + } + + // + // See if this is a standard request or not. + // + if((pRequest->bmRequestType & USB_RTYPE_TYPE_M) != USB_RTYPE_STANDARD) + { + // + // Since this is not a standard request, see if there is + // an external handler present. + // + if(g_psUSBDevice[0].psInfo->sCallbacks.pfnRequestHandler) + { + g_psUSBDevice[0].psInfo->sCallbacks.pfnRequestHandler( + g_psUSBDevice[0].pvInstance, pRequest); + } + else + { + // + // If there is no handler then stall this request. + // + USBDCDStallEP0(0); + } + } + else + { + // + // Assure that the jump table is not out of bounds. + // + if((pRequest->bRequest < + (sizeof(g_psUSBDStdRequests) / sizeof(tStdRequest))) && + (g_psUSBDStdRequests[pRequest->bRequest] != 0)) + { + // + // Jump table to the appropriate handler. + // + g_psUSBDStdRequests[pRequest->bRequest](&g_psUSBDevice[0], + pRequest); + } + else + { + // + // If there is no handler then stall this request. + // + USBDCDStallEP0(0); + } + } +} + +//***************************************************************************** +// +// This is 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(tDeviceInstance *pDevInstance) +{ + unsigned long ulEPStatus; + + // + // Get the end point 0 status. + // + ulEPStatus = MAP_USBEndpointStatus(USB0_BASE, USB_EP_0); + + switch(pDevInstance->eEP0State) + { + // + // 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. + // + pDevInstance->eEP0State = USB_STATE_IDLE; + + // + // If there is a pending address change then set the address. + // + if(pDevInstance->ulDevAddress & DEV_ADDR_PENDING) + { + // + // Clear the pending address change and set the address. + // + pDevInstance->ulDevAddress &= ~DEV_ADDR_PENDING; + MAP_USBDevAddrSet(USB0_BASE, pDevInstance->ulDevAddress); + } + + // + // If a new packet is already pending, we need to read it + // and handle whatever request it contains. + // + if(ulEPStatus & USB_DEV_EP0_OUT_PKTRDY) + { + // + // Process the newly arrived packet. + // + USBDReadAndDispatchRequest(0); + } + 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(ulEPStatus & USB_DEV_EP0_OUT_PKTRDY) + { + // + // Yes - process it. + // + USBDReadAndDispatchRequest(0); + } + break; + } + + // + // Data is still being sent to the host so handle this in the + // EP0StateTx() function. + // + case USB_STATE_TX: + { + USBDEP0StateTx(0); + break; + } + + // + // We are still in the middle of sending the configuration descriptor + // so handle this in the EP0StateTxConfig() function. + // + case USB_STATE_TX_CONFIG: + { + USBDEP0StateTxConfig(0); + break; + } + + // + // Handle the receive state for commands that are receiving data on + // endpoint zero. + // + case USB_STATE_RX: + { + unsigned long ulDataSize; + + // + // Set the number of bytes to get out of this next packet. + // + if(pDevInstance->ulEP0DataRemain > EP0_MAX_PACKET_SIZE) + { + // + // Don't send more than EP0_MAX_PACKET_SIZE bytes. + // + ulDataSize = EP0_MAX_PACKET_SIZE; + } + else + { + // + // There was space so send the remaining bytes. + // + ulDataSize = pDevInstance->ulEP0DataRemain; + } + + // + // Get the data from the USB controller end point 0. + // + MAP_USBEndpointDataGet(USB0_BASE, USB_EP_0, pDevInstance->pEP0Data, + &ulDataSize); + + // + // 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(pDevInstance->ulEP0DataRemain < EP0_MAX_PACKET_SIZE) + { + // + // Need to ACK the data on end point 0 in this case and set the + // data end as this is the last of the data. + // + MAP_USBDevEndpointDataAck(USB0_BASE, USB_EP_0, true); + + // + // Return to the idle state. + // + pDevInstance->eEP0State = USB_STATE_STATUS; + + // + // If there is a receive callback then call it. + // + if((pDevInstance->psInfo->sCallbacks.pfnDataReceived) && + (pDevInstance->ulOUTDataSize != 0)) + { + // + // Call the custom receive handler to handle the data + // that was received. + // + pDevInstance->psInfo->sCallbacks.pfnDataReceived( + pDevInstance->pvInstance, + pDevInstance->ulOUTDataSize); + + // + // Indicate that there is no longer any data being waited + // on. + // + pDevInstance->ulOUTDataSize = 0; + } + } + else + { + // + // Need to ACK the data on end point 0 in this case + // without setting data end because more data is coming. + // + MAP_USBDevEndpointDataAck(USB0_BASE, USB_EP_0, false); + } + + // + // Advance the pointer. + // + pDevInstance->pEP0Data += ulDataSize; + + // + // Decrement the number of bytes that are being waited on. + // + pDevInstance->ulEP0DataRemain -= ulDataSize; + + 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(ulEPStatus & USB_DEV_EP0_SENT_STALL) + { + // + // Clear the Setup End condition. + // + MAP_USBDevEndpointStatusClear(USB0_BASE, USB_EP_0, + USB_DEV_EP0_SENT_STALL); + + // + // Reset the global end point 0 state to IDLE. + // + pDevInstance->eEP0State = USB_STATE_IDLE; + + } + break; + } + // + // Halt on an unknown state, but only in DEBUG mode builds. + // + default: + { + ASSERT(0); + } + } +} + +//***************************************************************************** +// +// 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(tDeviceInstance *pDevInstance) +{ + unsigned long ulLoop; + + // + // Disable remote wake up signaling (as per USB 2.0 spec 9.1.1.6). + // + pDevInstance->ucStatus &= ~USB_STATUS_REMOTE_WAKE; + pDevInstance->bRemoteWakeup = false; + + // + // Call the device dependent code to indicate a bus reset has occurred. + // + if(pDevInstance->psInfo->sCallbacks.pfnResetHandler) + { + pDevInstance->psInfo->sCallbacks.pfnResetHandler( + pDevInstance->pvInstance); + } + + // + // Reset the default configuration identifier and alternate function + // selections. + // + pDevInstance->ulConfiguration = pDevInstance->ulDefaultConfiguration; + + for(ulLoop = 0; ulLoop < USB_MAX_INTERFACES_PER_DEVICE; ulLoop++) + { + pDevInstance->pucAltSetting[ulLoop] = (unsigned char)0; + } +} + +//***************************************************************************** +// +// This function handles the GET_STATUS standard USB request. +// +// \param pvInstance is the USB device controller instance data. +// \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(void *pvInstance, tUSBRequest *pUSBRequest) +{ + unsigned short usData; + tDeviceInstance *psUSBControl; + + ASSERT(pUSBRequest != 0); + ASSERT(pvInstance != 0); + + // + // Create the device information pointer. + // + psUSBControl = (tDeviceInstance *)pvInstance; + + // + // Need to ACK the data on end point 0 without setting last data as there + // will be a data phase. + // + MAP_USBDevEndpointDataAck(USB0_BASE, USB_EP_0, false); + + // + // 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. + // + usData = (unsigned short)psUSBControl->ucStatus; + + break; + } + + // + // This was a Interface status request. + // + case USB_RTYPE_INTERFACE: + { + // + // Interface status always returns 0. + // + usData = (unsigned short)0; + + break; + } + + // + // This was an endpoint status request. + // + case USB_RTYPE_ENDPOINT: + { + unsigned short usIndex; + unsigned long ulDir; + + // + // Which endpoint are we dealing with? + // + usIndex = pUSBRequest->wIndex & USB_REQ_EP_NUM_M; + + // + // Check if this was a valid endpoint request. + // + if((usIndex == 0) || (usIndex >= NUM_USB_EP)) + { + USBDCDStallEP0(0); + return; + } + else + { + // + // Are we dealing with an IN or OUT endpoint? + // + ulDir = ((pUSBRequest->wIndex & USB_REQ_EP_DIR_M) == + USB_REQ_EP_DIR_IN) ? HALT_EP_IN : HALT_EP_OUT; + + // + // Get the current halt status for this endpoint. + // + usData = + (unsigned short)psUSBControl->ucHalt[ulDir][usIndex - 1]; + } + break; + } + + // + // This was an unknown request. + // + default: + { + // + // Anything else causes a stall condition to indicate that the + // command was not supported. + // + USBDCDStallEP0(0); + return; + } + } + + // + // Send the two byte status response. + // + psUSBControl->ulEP0DataRemain = 2; + psUSBControl->pEP0Data = (unsigned char *)&usData; + + // + // Send the response. + // + USBDEP0StateTx(0); +} + +//***************************************************************************** +// +// This function handles the CLEAR_FEATURE standard USB request. +// +// \param pvInstance is the USB device controller instance data. +// \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. The device can only +// clear the Remote Wake feature. This device request should only be made if +// the descriptor indicates that Remote Wake is implemented by the device. +// Endpoints can only 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(void *pvInstance, tUSBRequest *pUSBRequest) +{ + tDeviceInstance *psUSBControl; + + ASSERT(pUSBRequest != 0); + ASSERT(pvInstance != 0); + + // + // Create the device information pointer. + // + psUSBControl = (tDeviceInstance *)pvInstance; + + // + // Need to ACK the data on end point 0 with last data set as this has no + // data phase. + // + MAP_USBDevEndpointDataAck(USB0_BASE, USB_EP_0, true); + + // + // 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 can be cleared by this function. + // + if(USB_FEATURE_REMOTE_WAKE & pUSBRequest->wValue) + { + // + // Clear the remote wake up state. + // + psUSBControl->ucStatus &= ~USB_STATUS_REMOTE_WAKE; + } + else + { + USBDCDStallEP0(0); + } + break; + } + + // + // This is a clear feature request at the endpoint level. + // + case USB_RTYPE_ENDPOINT: + { + unsigned long ulDir; + unsigned short usIndex; + + // + // Which endpoint are we dealing with? + // + usIndex = pUSBRequest->wIndex & USB_REQ_EP_NUM_M; + + // + // Not a valid endpoint. + // + if((usIndex == 0) || (usIndex > NUM_USB_EP)) + { + USBDCDStallEP0(0); + } + else + { + // + // Only the halt feature is supported. + // + if(USB_FEATURE_EP_HALT == pUSBRequest->wValue) + { + // + // Are we dealing with an IN or OUT endpoint? + // + ulDir = ((pUSBRequest->wIndex & USB_REQ_EP_DIR_M) == + USB_REQ_EP_DIR_IN) ? HALT_EP_IN : HALT_EP_OUT; + + // + // Clear the halt condition on this endpoint. + // + psUSBControl->ucHalt[ulDir][usIndex - 1] = 0; + + if(ulDir == HALT_EP_IN) + { + MAP_USBDevEndpointStallClear(USB0_BASE, + INDEX_TO_USB_EP(usIndex), + USB_EP_DEV_IN); + } + else + { + MAP_USBDevEndpointStallClear(USB0_BASE, + INDEX_TO_USB_EP(usIndex), + USB_EP_DEV_OUT); + } + } + else + { + // + // If any other feature is requested, this is an error. + // + USBDCDStallEP0(0); + return; + } + } + break; + } + + // + // This is an unknown request. + // + default: + { + USBDCDStallEP0(0); + return; + } + } +} + +//***************************************************************************** +// +// This function handles the SET_FEATURE standard USB request. +// +// \param pvInstance is the USB device controller instance data. +// \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. The device can only +// set the Remote Wake feature. This device request should only be made if the +// descriptor indicates that Remote Wake is implemented by the device. +// Endpoint requests can only 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(void *pvInstance, tUSBRequest *pUSBRequest) +{ + tDeviceInstance *psUSBControl; + + ASSERT(pUSBRequest != 0); + ASSERT(pvInstance != 0); + + // + // Create the device information pointer. + // + psUSBControl = (tDeviceInstance *)pvInstance; + + // + // Need to ACK the data on end point 0 with last data set as this has no + // data phase. + // + MAP_USBDevEndpointDataAck(USB0_BASE, USB_EP_0, true); + + // + // 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 the only feature that can be set by this + // function. + // + if(USB_FEATURE_REMOTE_WAKE & pUSBRequest->wValue) + { + // + // Set the remote wake up state. + // + psUSBControl->ucStatus |= USB_STATUS_REMOTE_WAKE; + } + else + { + USBDCDStallEP0(0); + } + break; + } + + // + // This is a set feature request at the endpoint level. + // + case USB_RTYPE_ENDPOINT: + { + unsigned short usIndex; + unsigned long ulDir; + + // + // Which endpoint are we dealing with? + // + usIndex = pUSBRequest->wIndex & USB_REQ_EP_NUM_M; + + // + // Not a valid endpoint? + // + if((usIndex == 0) || (usIndex >= NUM_USB_EP)) + { + USBDCDStallEP0(0); + } + else + { + // + // Only the Halt feature can be set. + // + if(USB_FEATURE_EP_HALT == pUSBRequest->wValue) + { + // + // Are we dealing with an IN or OUT endpoint? + // + ulDir = ((pUSBRequest->wIndex & USB_REQ_EP_DIR_M) == + USB_REQ_EP_DIR_IN) ? HALT_EP_IN : HALT_EP_OUT; + + // + // Clear the halt condition on this endpoint. + // + psUSBControl->ucHalt[ulDir][usIndex - 1] = 1; + } + else + { + // + // No other requests are supported. + // + USBDCDStallEP0(0); + return; + } + } + break; + } + + // + // This is an unknown request. + // + default: + { + USBDCDStallEP0(0); + return; + } + } +} + +//***************************************************************************** +// +// This function handles the SET_ADDRESS standard USB request. +// +// \param pvInstance is the USB device controller instance data. +// \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(void *pvInstance, tUSBRequest *pUSBRequest) +{ + tDeviceInstance *psUSBControl; + + ASSERT(pUSBRequest != 0); + ASSERT(pvInstance != 0); + + // + // Create the device information pointer. + // + psUSBControl = (tDeviceInstance *)pvInstance; + + // + // Need to ACK the data on end point 0 with last data set as this has no + // data phase. + // + MAP_USBDevEndpointDataAck(USB0_BASE, USB_EP_0, true); + + // + // Save the device address as we cannot change address until the status + // phase is complete. + // + psUSBControl->ulDevAddress = pUSBRequest->wValue | DEV_ADDR_PENDING; + + // + // Transition directly to the status state since there is no data phase + // for this request. + // + psUSBControl->eEP0State = USB_STATE_STATUS; +} + +//***************************************************************************** +// +// This function handles the GET_DESCRIPTOR standard USB request. +// +// \param pvInstance is the USB device controller instance data. +// \param pUSBRequest holds the data for this request. +// +// This function will return most of the descriptors requested by the host +// controller. The descriptor specified by \e +// pvInstance->psInfo->pDeviceDescriptor will be returned when the device +// descriptor is requested. If a request for a specific configuration +// descriptor is made, then the appropriate descriptor from the \e +// g_pConfigDescriptors will be returned. When a request for a string +// descriptor is made, the appropriate string from the +// \e pvInstance->psInfo->pStringDescriptors will be returned. If the \e +// pvInstance->psInfo->sCallbacks.GetDescriptor is specified it will be +// called to handle the request. In this case it must call the +// USBDCDSendDataEP0() function to send the data to the host controller. If +// the callback is not specified, and the descriptor request is not for a +// device, configuration, or string descriptor then this function will stall +// the request to indicate that the request was not supported by the device. +// +// \return None. +// +//***************************************************************************** +static void +USBDGetDescriptor(void *pvInstance, tUSBRequest *pUSBRequest) +{ + tBoolean bConfig; + tDeviceInstance *psUSBControl; + tDeviceInfo *psDevice; + + ASSERT(pUSBRequest != 0); + ASSERT(pvInstance != 0); + + // + // Create the device information pointer. + // + psUSBControl = (tDeviceInstance *)pvInstance; + psDevice = psUSBControl->psInfo; + + // + // Need to ACK the data on end point 0 without setting last data as there + // will be a data phase. + // + MAP_USBDevEndpointDataAck(USB0_BASE, USB_EP_0, false); + + // + // Assume we are not sending the configuration descriptor until we + // determine otherwise. + // + bConfig = false; + + // + // 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. + // + psUSBControl->pEP0Data = + (unsigned char *)psDevice->pDeviceDescriptor; + + // + // The size of the device descriptor is in the first byte. + // + psUSBControl->ulEP0DataRemain = psDevice->pDeviceDescriptor[0]; + + break; + } + + // + // This request was for a configuration descriptor. + // + case USB_DTYPE_CONFIGURATION: + { + const tConfigHeader *psConfig; + const tDeviceDescriptor *psDeviceDesc; + unsigned char ucIndex; + + // + // Which configuration are we being asked for? + // + ucIndex = (unsigned char)(pUSBRequest->wValue & 0xFF); + + // + // Is this valid? + // + psDeviceDesc = + (const tDeviceDescriptor *)psDevice->pDeviceDescriptor; + + if(ucIndex >= psDeviceDesc->bNumConfigurations) + { + // + // This is an invalid configuration index. Stall EP0 to + // indicate a request error. + // + USBDCDStallEP0(0); + psUSBControl->pEP0Data = 0; + psUSBControl->ulEP0DataRemain = 0; + } + else + { + // + // Return the externally specified configuration descriptor. + // + psConfig = psDevice->ppConfigDescriptors[ucIndex]; + + // + // Start by sending data from the beginning of the first + // descriptor. + // + psUSBControl->ucConfigSection = 0; + psUSBControl->ucSectionOffset = 0; + psUSBControl->pEP0Data = (unsigned char *) + psConfig->psSections[0]->pucData; + + // + // Determine the total size of the configuration descriptor + // by counting the sizes of the sections comprising it. + // + psUSBControl->ulEP0DataRemain = + USBDCDConfigDescGetSize(psConfig); + + // + // Remember that we need to send the configuration descriptor + // and which descriptor we need to send. + // + psUSBControl->ucConfigIndex = ucIndex; + + bConfig = true; + } + break; + } + + // + // This request was for a string descriptor. + // + case USB_DTYPE_STRING: + { + long lIndex; + + // + // Determine the correct descriptor index based on the requested + // language ID and index. + // + lIndex = 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(lIndex == -1) + { + USBDCDStallEP0(0); + break; + } + + // + // Return the externally specified configuration descriptor. + // + psUSBControl->pEP0Data = + (unsigned char *)psDevice->ppStringDescriptors[lIndex]; + + // + // The total size of a string descriptor is in byte 0. + // + psUSBControl->ulEP0DataRemain = + psDevice->ppStringDescriptors[lIndex][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: + { + // + // If there is a handler for requests that are not handled then + // call it. + // + if(psDevice->sCallbacks.pfnGetDescriptor) + { + psDevice->sCallbacks.pfnGetDescriptor(psUSBControl->pvInstance, + pUSBRequest); + } + else + { + // + // Whatever this was this handler does not understand it so + // just stall the request. + // + USBDCDStallEP0(0); + } + + return; + } + } + + // + // If this request has data to send, then send it. + // + if(psUSBControl->pEP0Data) + { + // + // If there is more data to send than is requested then just + // send the requested amount of data. + // + if(psUSBControl->ulEP0DataRemain > pUSBRequest->wLength) + { + psUSBControl->ulEP0DataRemain = pUSBRequest->wLength; + } + + // + // Now in the transmit data state. Be careful to call the correct + // function since we need to handle the configuration descriptor + // differently from the others. + // + if(!bConfig) + { + USBDEP0StateTx(0); + } + else + { + USBDEP0StateTxConfig(0); + } + } +} + +//***************************************************************************** +// +// This function determines which string descriptor to send to satisfy a +// request for a given index and language. +// +// \param usLang is the requested string language ID. +// \param usIndex 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 long +USBDStringIndexFromRequest(unsigned short usLang, unsigned short usIndex) +{ + tString0Descriptor *pLang; + unsigned long ulNumLangs; + unsigned long ulNumStringsPerLang; + unsigned long ulLoop; + + // + // Make sure we have a string table at all. + // + if((g_psUSBDevice[0].psInfo == 0) || + (g_psUSBDevice[0].psInfo->ppStringDescriptors == 0)) + { + return(-1); + } + + // + // 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(usIndex == 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). + // + ulNumLangs = (g_psUSBDevice[0].psInfo->ppStringDescriptors[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. + // + ulNumStringsPerLang = ((g_psUSBDevice[0].psInfo->ulNumStringDescriptors - 1) / + ulNumLangs); + + // + // 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 + (ulNumStringsPerLang * ulNumLangs)) != + g_psUSBDevice[0].psInfo->ulNumStringDescriptors) + { + 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_psUSBDevice[0].psInfo->ppStringDescriptors[0]); + + // + // Look through the supported languages looking for the one we were asked + // for. + // + for(ulLoop = 0; ulLoop < ulNumLangs; ulLoop++) + { + // + // Have we found the requested language? + // + if(pLang->wLANGID[ulLoop] == usLang) + { + // + // Yes - calculate the index of the descriptor to send. + // + return((ulNumStringsPerLang * ulLoop) + usIndex); + } + } + + // + // 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 pvInstance is the USB device controller instance data. +// \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(void *pvInstance, tUSBRequest *pUSBRequest) +{ + // + // Need to ACK the data on end point 0 without setting last data as there + // will be a data phase. + // + MAP_USBDevEndpointDataAck(USB0_BASE, USB_EP_0, false); + + // + // This function is not handled by default. + // + USBDCDStallEP0(0); +} + +//***************************************************************************** +// +// This function handles the GET_CONFIGURATION standard USB request. +// +// \param pvInstance is the USB device controller instance data. +// \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(void *pvInstance, tUSBRequest *pUSBRequest) +{ + unsigned char ucValue; + tDeviceInstance *psUSBControl; + + ASSERT(pUSBRequest != 0); + ASSERT(pvInstance != 0); + + // + // Create the device information pointer. + // + psUSBControl = (tDeviceInstance *)pvInstance; + + // + // Need to ACK the data on end point 0 without setting last data as there + // will be a data phase. + // + MAP_USBDevEndpointDataAck(USB0_BASE, USB_EP_0, false); + + // + // If we still have an address pending then the device is still not + // configured. + // + if(psUSBControl->ulDevAddress & DEV_ADDR_PENDING) + { + ucValue = 0; + } + else + { + ucValue = (unsigned char)psUSBControl->ulConfiguration; + } + + psUSBControl->ulEP0DataRemain = 1; + psUSBControl->pEP0Data = &ucValue; + + // + // Send the single byte response. + // + USBDEP0StateTx(0); +} + +//***************************************************************************** +// +// This function handles the SET_CONFIGURATION standard USB request. +// +// \param pvInstance is the USB device controller instance data. +// \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. This number should be one +// of the configurations that was specified in the descriptors. If the +// \e ConfigChange callback is specified in \e pvInstance->psInfo->sCallbacks, +// it will be called so that the application can respond to a change in +// configuration. +// +// \return None. +// +//***************************************************************************** +static void +USBDSetConfiguration(void *pvInstance, tUSBRequest *pUSBRequest) +{ + tDeviceInstance *psUSBControl; + tDeviceInfo *psDevice; + + // + // Create the device information pointer. + // + psUSBControl = (tDeviceInstance *)pvInstance; + psDevice = psUSBControl->psInfo; + + // + // Need to ACK the data on end point 0 with last data set as this has no + // data phase. + // + MAP_USBDevEndpointDataAck(USB0_BASE, USB_EP_0, true); + + // + // 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 > psUSBControl->psInfo->pDeviceDescriptor[17]) + { + // + // The passed configuration number is not valid. Stall the endpoint to + // signal the error to the host. + // + USBDCDStallEP0(0); + } + else + { + // + // Save the configuration. + // + psUSBControl->ulConfiguration = 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(psUSBControl->ulConfiguration) + { + const tConfigHeader *psHdr; + const tConfigDescriptor *psDesc; + + // + // Get a pointer to the configuration descriptor. This will always + // be the first section in the current configuration. + // + psHdr = psDevice->ppConfigDescriptors[pUSBRequest->wValue - 1]; + psDesc = (const tConfigDescriptor *)(psHdr->psSections[0]->pucData); + + // + // Remember the new self- or bus-powered state if the user has not + // already called us to tell us the state to report. + // + if(!psUSBControl->bPwrSrcSet) + { + if((psDesc->bmAttributes & USB_CONF_ATTR_PWR_M) == + USB_CONF_ATTR_SELF_PWR) + { + psUSBControl->ucStatus |= USB_STATUS_SELF_PWR; + } + else + { + psUSBControl->ucStatus &= ~USB_STATUS_SELF_PWR; + } + } + + // + // Configure endpoints for the new configuration. + // + USBDeviceConfig(psUSBControl, + psDevice->ppConfigDescriptors[pUSBRequest->wValue - 1]); + } + + // + // If there is a configuration change callback then call it. + // + if(psDevice->sCallbacks.pfnConfigChange) + { + psDevice->sCallbacks.pfnConfigChange( + psUSBControl->pvInstance, psUSBControl->ulConfiguration); + } + } +} + +//***************************************************************************** +// +// This function handles the GET_INTERFACE standard USB request. +// +// \param pvInstance is the USB device controller instance data. +// \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(void *pvInstance, tUSBRequest *pUSBRequest) +{ + unsigned char ucValue; + tDeviceInstance *psUSBControl; + + ASSERT(pUSBRequest != 0); + ASSERT(pvInstance != 0); + + // + // Create the device information pointer. + // + psUSBControl = (tDeviceInstance *)pvInstance; + + // + // Need to ACK the data on end point 0 without setting last data as there + // will be a data phase. + // + MAP_USBDevEndpointDataAck(USB0_BASE, USB_EP_0, false); + + // + // If we still have an address pending then the device is still not + // configured. + // + if(psUSBControl->ulDevAddress & DEV_ADDR_PENDING) + { + ucValue = (unsigned char)0; + } + else + { + // + // Is the interface number valid? + // + if(pUSBRequest->wIndex < USB_MAX_INTERFACES_PER_DEVICE) + { + // + // Read the current alternate setting for the required interface. + // + ucValue = psUSBControl->pucAltSetting[pUSBRequest->wIndex]; + } + else + { + // + // An invalid interface number was specified. + // + USBDCDStallEP0(0); + return; + } + } + + // + // Send the single byte response. + // + psUSBControl->ulEP0DataRemain = 1; + psUSBControl->pEP0Data = &ucValue; + + // + // Send the single byte response. + // + USBDEP0StateTx(0); +} + +//***************************************************************************** +// +// This function handles the SET_INTERFACE standard USB request. +// +// \param pvInstance is the USB device controller instance data. +// \param pUSBRequest holds the data for this request. +// +// This function is called when a standard request for changing the interface +// is received from the host controller. If this is a valid request the +// function will call the function specified by the InterfaceChange in the +// \e pvInstance->psInfo->sCallbacks variable to notify the application that the +// interface has changed and will pass it the new alternate interface number. +// +// \return None. +// +//***************************************************************************** +static void +USBDSetInterface(void *pvInstance, tUSBRequest *pUSBRequest) +{ + const tConfigHeader *psConfig; + tInterfaceDescriptor *psInterface; + unsigned long ulLoop; + unsigned long ulSection; + unsigned long ulNumInterfaces; + unsigned char ucInterface; + tBoolean bRetcode; + tDeviceInstance *psUSBControl; + tDeviceInfo *psDevice; + + ASSERT(pUSBRequest != 0); + ASSERT(pvInstance != 0); + + // + // Create the device information pointer. + // + psUSBControl = (tDeviceInstance *)pvInstance; + psDevice = psUSBControl->psInfo; + + // + // Need to ACK the data on end point 0 with last data set as this has no + // data phase. + // + MAP_USBDevEndpointDataAck(USB0_BASE, USB_EP_0, true); + + // + // Use the current configuration. + // + psConfig = psDevice->ppConfigDescriptors[psUSBControl->ulConfiguration - 1]; + + // + // How many interfaces are included in the descriptor? + // + ulNumInterfaces = USBDCDConfigDescGetNum(psConfig, + USB_DTYPE_INTERFACE); + + // + // Find the interface descriptor for the supplied interface and alternate + // setting numbers. + // + for(ulLoop = 0; ulLoop < ulNumInterfaces; ulLoop++) + { + // + // Get the next interface descriptor in the configuration descriptor. + // + psInterface = USBDCDConfigGetInterface(psConfig, ulLoop, USB_DESC_ANY, + &ulSection); + + // + // Is this the required interface with the correct alternate setting? + // + if(psInterface && + (psInterface->bInterfaceNumber == pUSBRequest->wIndex) && + (psInterface->bAlternateSetting == pUSBRequest->wValue)) + { + ucInterface = psInterface->bInterfaceNumber; + + // + // Make sure we don't write outside the bounds of the pucAltSetting + // array (in a debug build, anyway, since this indicates an error + // in the device descriptor). + // + ASSERT(ucInterface < USB_MAX_INTERFACES_PER_DEVICE); + + // + // This is the correct interface descriptor so save the + // setting. + // + psUSBControl->pucAltSetting[ucInterface] = + psInterface->bAlternateSetting; + + // + // Reconfigure the endpoints to match the requirements of the + // new alternate setting for the interface. + // + bRetcode = USBDeviceConfigAlternate(psUSBControl, psConfig, ucInterface, + psInterface->bAlternateSetting); + + // + // If there is a callback then notify the application of the + // change to the alternate interface. + // + if(bRetcode && psDevice->sCallbacks.pfnInterfaceChange) + { + psDevice->sCallbacks.pfnInterfaceChange( + psUSBControl->pvInstance, + pUSBRequest->wIndex, + pUSBRequest->wValue); + } + + // + // All done. + // + return; + } + } + + // + // If we drop out of the loop, we didn't find an interface descriptor + // matching the requested number and alternate setting or there was an + // error while trying to set up for the new alternate setting. + // + USBDCDStallEP0(0); +} + +//***************************************************************************** +// +// This function handles the SYNC_FRAME standard USB request. +// +// \param pvInstance is the USB device controller instance data. +// \param pUSBRequest holds the data for this request. +// +// This is currently a stub function that will stall indicating that the +// command is not supported. +// +// \return None. +// +//***************************************************************************** +static void +USBDSyncFrame(void *pvInstance, tUSBRequest *pUSBRequest) +{ + // + // Need to ACK the data on end point 0 with last data set as this has no + // data phase. + // + MAP_USBDevEndpointDataAck(USB0_BASE, USB_EP_0, true); + + // + // Not handled yet so stall this request. + // + USBDCDStallEP0(0); +} + +//***************************************************************************** +// +// This internal function handles sending data on endpoint zero. +// +// \param ulIndex is the index of the USB controller which is to be +// initialized. +// +// \return None. +// +//***************************************************************************** +static void +USBDEP0StateTx(unsigned long ulIndex) +{ + unsigned long ulNumBytes; + unsigned char *pData; + + ASSERT(ulIndex == 0); + + // + // In the TX state on endpoint zero. + // + g_psUSBDevice[0].eEP0State = USB_STATE_TX; + + // + // Set the number of bytes to send this iteration. + // + ulNumBytes = g_psUSBDevice[0].ulEP0DataRemain; + + // + // Limit individual transfers to 64 bytes. + // + if(ulNumBytes > EP0_MAX_PACKET_SIZE) + { + ulNumBytes = EP0_MAX_PACKET_SIZE; + } + + // + // Save the pointer so that it can be passed to the USBEndpointDataPut() + // function. + // + pData = (unsigned char *)g_psUSBDevice[0].pEP0Data; + + // + // Advance the data pointer and counter to the next data to be sent. + // + g_psUSBDevice[0].ulEP0DataRemain -= ulNumBytes; + g_psUSBDevice[0].pEP0Data += ulNumBytes; + + // + // Put the data in the correct FIFO. + // + MAP_USBEndpointDataPut(USB0_BASE, USB_EP_0, pData, ulNumBytes); + + // + // If this is exactly 64 then don't set the last packet yet. + // + if(ulNumBytes == 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. + // + MAP_USBEndpointDataSend(USB0_BASE, USB_EP_0, USB_TRANS_IN); + } + else + { + // + // Now go to the status state and wait for the transmit to complete. + // + g_psUSBDevice[0].eEP0State = USB_STATE_STATUS; + + // + // Send the last bit of data. + // + MAP_USBEndpointDataSend(USB0_BASE, USB_EP_0, USB_TRANS_IN_LAST); + + // + // If there is a sent callback then call it. + // + if((g_psUSBDevice[0].psInfo->sCallbacks.pfnDataSent) && + (g_psUSBDevice[0].ulOUTDataSize != 0)) + { + // + // Call the custom handler. + // + g_psUSBDevice[0].psInfo->sCallbacks.pfnDataSent( + g_psUSBDevice[0].pvInstance, g_psUSBDevice[0].ulOUTDataSize); + + // + // There is no longer any data pending to be sent. + // + g_psUSBDevice[0].ulOUTDataSize = 0; + } + } +} + +//***************************************************************************** +// +// This internal function handles sending the configuration descriptor on +// endpoint zero. +// +// \param ulIndex is the index of the USB controller which is to be used. +// +// +// \return None. +// +//***************************************************************************** +static void +USBDEP0StateTxConfig(unsigned long ulIndex) +{ + unsigned long ulNumBytes; + unsigned long ulSecBytes; + unsigned long ulToSend; + unsigned char *pData; + tConfigDescriptor sConfDesc; + const tConfigHeader *psConfig; + const tConfigSection *psSection; + + ASSERT(ulIndex == 0); + + // + // In the TX state on endpoint zero. + // + g_psUSBDevice[0].eEP0State = USB_STATE_TX_CONFIG; + + // + // Find the current configuration descriptor definition. + // + psConfig = g_psUSBDevice[0].psInfo->ppConfigDescriptors[ + g_psUSBDevice[0].ucConfigIndex]; + + // + // Set the number of bytes to send this iteration. + // + ulNumBytes = g_psUSBDevice[0].ulEP0DataRemain; + + // + // Limit individual transfers to 64 bytes. + // + if(ulNumBytes > EP0_MAX_PACKET_SIZE) + { + ulNumBytes = EP0_MAX_PACKET_SIZE; + } + + // + // If this is the first call, we need to fix up the total length of the + // configuration descriptor. This has already been determined and set in + // g_sUSBDeviceState.ulEP0DataRemain. + // + if((g_psUSBDevice[0].ucSectionOffset == 0) && + (g_psUSBDevice[0].ucConfigSection == 0)) + { + // + // Copy the USB configuration descriptor from the beginning of the + // first section of the current configuration. + // + sConfDesc = *(tConfigDescriptor *)g_psUSBDevice[0].pEP0Data; + + // + // Update the total size. + // + sConfDesc.wTotalLength = (unsigned short)USBDCDConfigDescGetSize( + psConfig); + + // + // Write the descriptor to the USB FIFO. + // + ulToSend = (ulNumBytes < sizeof(tConfigDescriptor)) ? ulNumBytes : + sizeof(tConfigDescriptor); + MAP_USBEndpointDataPut(USB0_BASE, USB_EP_0, (unsigned char *)&sConfDesc, + ulToSend); + + // + // Did we reach the end of the first section? + // + if(psConfig->psSections[0]->usSize == ulToSend) + { + // + // Update our tracking indices to point to the start of the next + // section. + // + g_psUSBDevice[0].ucSectionOffset = 0; + g_psUSBDevice[0].ucConfigSection = 1; + } + else + { + // + // Note that we have sent the first few bytes of the descriptor. + // + g_psUSBDevice[0].ucSectionOffset = (unsigned char)ulToSend; + } + + // + // How many bytes do we have remaining to send on this iteration? + // + ulToSend = ulNumBytes - ulToSend; + } + else + { + // + // Set the number of bytes we still have to send on this call. + // + ulToSend = ulNumBytes; + } + + // + // Add the relevant number of bytes to the USB FIFO + // + while(ulToSend) + { + // + // Get a pointer to the current configuration section. + // + psSection = psConfig->psSections[g_psUSBDevice[0].ucConfigSection]; + + // + // Calculate bytes are available in the current configuration section. + // + ulSecBytes = (unsigned long)(psSection->usSize - + g_psUSBDevice[0].ucSectionOffset); + + // + // Save the pointer so that it can be passed to the + // USBEndpointDataPut() function. + // + pData = (unsigned char *)psSection->pucData + + g_psUSBDevice[0].ucSectionOffset; + + // + // Are there more bytes in this section that we still have to send? + // + if(ulSecBytes > ulToSend) + { + // + // Yes - send only the remaining bytes in the transfer. + // + ulSecBytes = ulToSend; + } + + // + // Put the data in the correct FIFO. + // + MAP_USBEndpointDataPut(USB0_BASE, USB_EP_0, pData, ulSecBytes); + + // + // Fix up our pointers for the next iteration. + // + ulToSend -= ulSecBytes; + g_psUSBDevice[0].ucSectionOffset += (unsigned char)ulSecBytes; + + // + // Have we reached the end of a section? + // + if(g_psUSBDevice[0].ucSectionOffset == psSection->usSize) + { + // + // Yes - move to the next one. + // + g_psUSBDevice[0].ucConfigSection++; + g_psUSBDevice[0].ucSectionOffset = 0; + } + } + + // + // Fix up the number of bytes remaining to be sent and the start pointer. + // + g_psUSBDevice[0].ulEP0DataRemain -= ulNumBytes; + + // + // If we ran out of bytes in the configuration section, bail and just + // send out what we have. + // + if(psConfig->ucNumSections <= g_psUSBDevice[0].ucConfigSection) + { + g_psUSBDevice[0].ulEP0DataRemain = 0; + } + + // + // If there is no more data don't keep looking or ucConfigSection might + // overrun the available space. + // + if(g_psUSBDevice[0].ulEP0DataRemain != 0) + { + pData =(unsigned char *) + psConfig->psSections[g_psUSBDevice[0].ucConfigSection]->pucData; + ulToSend = g_psUSBDevice[0].ucSectionOffset; + g_psUSBDevice[0].pEP0Data = (pData + ulToSend); + } + + // + // If this is exactly 64 then don't set the last packet yet. + // + if(ulNumBytes == 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. + // + MAP_USBEndpointDataSend(USB0_BASE, USB_EP_0, USB_TRANS_IN); + } + else + { + // + // Send the last bit of data. + // + MAP_USBEndpointDataSend(USB0_BASE, USB_EP_0, USB_TRANS_IN_LAST); + + // + // If there is a sent callback then call it. + // + if((g_psUSBDevice[0].psInfo->sCallbacks.pfnDataSent) && + (g_psUSBDevice[0].ulOUTDataSize != 0)) + { + // + // Call the custom handler. + // + g_psUSBDevice[0].psInfo->sCallbacks.pfnDataSent( + g_psUSBDevice[0].pvInstance, g_psUSBDevice[0].ulOUTDataSize); + + // + // There is no longer any data pending to be sent. + // + g_psUSBDevice[0].ulOUTDataSize = 0; + } + + // + // Now go to the status state and wait for the transmit to complete. + // + g_psUSBDevice[0].eEP0State = USB_STATE_STATUS; + } +} + +//***************************************************************************** +// +// The internal USB device interrupt handler. +// +// \param ulIndex is the USB controller associated with this interrupt. +// \param ulStatus is the current interrupt status as read via a call to +// USBIntStatusControl(). +// +// This function is called from either \e USB0DualModeIntHandler() or +// \e USB0DeviceIntHandler() to process USB interrupts when in device mode. +// This handler will branch the interrupt off to the appropriate application or +// stack handlers depending on the current status of the USB controller. +// +// The two-tiered structure for the interrupt handler ensures that it is +// possible to use the same handler code in both device and OTG modes and +// means that host code can be excluded from applications that only require +// support for USB device mode operation. +// +// \return None. +// +//***************************************************************************** +void +USBDeviceIntHandlerInternal(unsigned long ulIndex, unsigned long ulStatus) +{ + static unsigned long ulSOFDivide = 0; + tDeviceInfo *psInfo; + void *pvInstance; + + // + // If device initialization has not been performed then just disconnect + // from the USB bus and return from the handler. + // + if(g_psUSBDevice[0].psInfo == 0) + { + MAP_USBDevDisconnect(USB0_BASE); + return; + } + + psInfo = g_psUSBDevice[0].psInfo; + pvInstance = g_psUSBDevice[0].pvInstance; + + // + // Received a reset from the host. + // + if(ulStatus & USB_INTCTRL_RESET) + { + USBDeviceEnumResetHandler(&g_psUSBDevice[0]); + } + + // + // Suspend was signaled on the bus. + // + if(ulStatus & USB_INTCTRL_SUSPEND) + { + // + // Call the SuspendHandler() if it was specified. + // + if(psInfo->sCallbacks.pfnSuspendHandler) + { + psInfo->sCallbacks.pfnSuspendHandler(pvInstance); + } + } + + // + // Resume was signaled on the bus. + // + if(ulStatus & USB_INTCTRL_RESUME) + { + // + // Call the ResumeHandler() if it was specified. + // + if(psInfo->sCallbacks.pfnResumeHandler) + { + psInfo->sCallbacks.pfnResumeHandler(pvInstance); + } + } + + // + // USB device was disconnected. + // + if(ulStatus & USB_INTCTRL_DISCONNECT) + { + // + // Call the DisconnectHandler() if it was specified. + // + if(psInfo->sCallbacks.pfnDisconnectHandler) + { + psInfo->sCallbacks.pfnDisconnectHandler(pvInstance); + } + } + + // + // Start of Frame was received. + // + if(ulStatus & USB_INTCTRL_SOF) + { + // + // Increment the global Start of Frame counter. + // + g_ulUSBSOFCount++; + + // + // Increment our SOF divider. + // + ulSOFDivide++; + + // + // Handle resume signaling if required. + // + USBDeviceResumeTickHandler(&g_psUSBDevice[0]); + + // + // Have we counted enough SOFs to allow us to call the tick function? + // + if(ulSOFDivide == USB_SOF_TICK_DIVIDE) + { + // + // Yes - reset the divider and call the SOF tick handler. + // + ulSOFDivide = 0; + InternalUSBStartOfFrameTick(USB_SOF_TICK_DIVIDE); + } + } + + // + // Get the controller interrupt status. + // + ulStatus = MAP_USBIntStatusEndpoint(USB0_BASE); + + // + // Handle end point 0 interrupts. + // + if(ulStatus & USB_INTEP_0) + { + USBDeviceEnumHandler(&g_psUSBDevice[0]); + } + + // + // Because there is no way to detect if a uDMA interrupt has occurred, + // check for an endpoint callback and call it if it is available. + // + if(psInfo->sCallbacks.pfnEndpointHandler) + { + psInfo->sCallbacks.pfnEndpointHandler(pvInstance, ulStatus); + } +} + +//***************************************************************************** +// +// Close the Doxygen group. +//! @} +// +//***************************************************************************** -- cgit v1.3.1