//***************************************************************************** // // 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. //! @} // //*****************************************************************************