From 044ad7c3987460ede48ff27afd6bdb0ca05a0432 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Mon, 4 Jul 2011 20:52:54 +0200 Subject: import at91lib from at91lib_20100901_softpack_1_9_v_1_0_svn_v15011 it's sad to see that atmel doesn't publish their svn repo or has a centralized location or even puts proper version/release info into the library itself --- usb/device/core/USBDDriver.c | 753 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 753 insertions(+) create mode 100644 usb/device/core/USBDDriver.c (limited to 'usb/device/core/USBDDriver.c') diff --git a/usb/device/core/USBDDriver.c b/usb/device/core/USBDDriver.c new file mode 100644 index 0000000..6a7c000 --- /dev/null +++ b/usb/device/core/USBDDriver.c @@ -0,0 +1,753 @@ +/* ---------------------------------------------------------------------------- + * ATMEL Microcontroller Software Support + * ---------------------------------------------------------------------------- + * Copyright (c) 2008, Atmel Corporation + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the disclaimer below. + * + * Atmel's name may not be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE + * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "USBDDriver.h" +#include "USBDDriverCallbacks.h" +#include "USBD.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +//------------------------------------------------------------------------------ +// Local functions +//------------------------------------------------------------------------------ +#if defined(CHIP_USB_OTGHS) +static unsigned char otg_features_supported = 0; +#endif + +//------------------------------------------------------------------------------ +/// Send a NULL packet +//------------------------------------------------------------------------------ +static void TerminateCtrlInWithNull(void *pArg, + unsigned char status, + unsigned int transferred, + unsigned int remaining) +{ + USBD_Write(0, // Endpoint #0 + 0, // No data buffer + 0, // No data buffer + (TransferCallback) 0, + (void *) 0); +} + +//------------------------------------------------------------------------------ +/// Configures the device by setting it into the Configured state and +/// initializing all endpoints. +/// \param pDriver Pointer to a USBDDriver instance. +/// \param cfgnum Configuration number to set. +//------------------------------------------------------------------------------ +static void SetConfiguration(USBDDriver *pDriver, unsigned char cfgnum) +{ + USBEndpointDescriptor *pEndpoints[CHIP_USB_NUMENDPOINTS+1]; + const USBConfigurationDescriptor *pConfiguration; + + // Use different descriptor depending on device speed + if (USBD_IsHighSpeed()) { + + pConfiguration = pDriver->pDescriptors->pHsConfiguration; + } + else { + + pConfiguration = pDriver->pDescriptors->pFsConfiguration; + } + + // Set & save the desired configuration + USBD_SetConfiguration(cfgnum); + pDriver->cfgnum = cfgnum; + + // If the configuration is not 0, configure endpoints + if (cfgnum != 0) { + + // Parse configuration to get endpoint descriptors + USBConfigurationDescriptor_Parse(pConfiguration, 0, pEndpoints, 0); + + // Configure endpoints + int i = 0; + while (pEndpoints[i] != 0) { + + USBD_ConfigureEndpoint(pEndpoints[i]); + i++; + } + } + // Should be done before send the ZLP + USBDDriverCallbacks_ConfigurationChanged(cfgnum); + + // Acknowledge the request + USBD_Write(0, // Endpoint #0 + 0, // No data buffer + 0, // No data buffer + (TransferCallback) 0, + (void *) 0); +} + +//------------------------------------------------------------------------------ +/// Sends the current configuration number to the host. +/// \param pDriver Pointer to a USBDDriver instance. +//------------------------------------------------------------------------------ +static void GetConfiguration(const USBDDriver *pDriver) +{ + USBD_Write(0, &(pDriver->cfgnum), 1, 0, 0); +} + +//------------------------------------------------------------------------------ +/// Sends the current status of the device to the host. +/// \param pDriver Pointer to a USBDDriver instance. +//------------------------------------------------------------------------------ +static void GetDeviceStatus(const USBDDriver *pDriver) +{ + static unsigned short data; + const USBConfigurationDescriptor *pConfiguration; + + data = 0; + // Use different configuration depending on device speed + if (USBD_IsHighSpeed()) { + + pConfiguration = pDriver->pDescriptors->pHsConfiguration; + } + else { + + pConfiguration = pDriver->pDescriptors->pFsConfiguration; + } + + // Check current configuration for power mode (if device is configured) + if (pDriver->cfgnum != 0) { + + if (USBConfigurationDescriptor_IsSelfPowered(pConfiguration)) { + + data |= 1; + } + } + + // Check if remote wake-up is enabled + if (pDriver->isRemoteWakeUpEnabled) { + + data |= 2; + } + + // Send the device status + USBD_Write(0, &data, 2, 0, 0); +} + +//------------------------------------------------------------------------------ +/// Sends the current status of an endpoints to the USB host. +/// \param bEndpoint Endpoint number. +//------------------------------------------------------------------------------ +static void GetEndpointStatus(unsigned char bEndpoint) +{ + static unsigned short data; + + data = 0; + // Check if the endpoint exists + if (bEndpoint > CHIP_USB_NUMENDPOINTS) { + + USBD_Stall(0); + } + else { + + // Check if the endpoint if currently halted + if (USBD_IsHalted(bEndpoint)) { + + data = 1; + } + + // Send the endpoint status + USBD_Write(0, &data, 2, 0, 0); + } +} + +//------------------------------------------------------------------------------ +/// Sends the requested USB descriptor to the host if available, or STALLs the +/// request. +/// \param pDriver Pointer to a USBDDriver instance. +/// \param type Type of the requested descriptor +/// \param index Index of the requested descriptor. +/// \param length Maximum number of bytes to return. +//------------------------------------------------------------------------------ +static void GetDescriptor( + const USBDDriver *pDriver, + unsigned char type, + unsigned char indexRDesc, + unsigned int length) +{ + const USBDeviceDescriptor *pDevice; + const USBConfigurationDescriptor *pConfiguration; + const USBDeviceQualifierDescriptor *pQualifier; + const USBConfigurationDescriptor *pOtherSpeed; + const USBGenericDescriptor **pStrings = + (const USBGenericDescriptor **) pDriver->pDescriptors->pStrings; + const USBGenericDescriptor *pString; + unsigned char numStrings = pDriver->pDescriptors->numStrings; + unsigned char terminateWithNull = 0; + + // Use different set of descriptors depending on device speed + if (USBD_IsHighSpeed()) { + + TRACE_DEBUG("HS "); + pDevice = pDriver->pDescriptors->pHsDevice; + pConfiguration = pDriver->pDescriptors->pHsConfiguration; + pQualifier = pDriver->pDescriptors->pHsQualifier; + pOtherSpeed = pDriver->pDescriptors->pHsOtherSpeed; + } + else { + + TRACE_DEBUG("FS "); + pDevice = pDriver->pDescriptors->pFsDevice; + pConfiguration = pDriver->pDescriptors->pFsConfiguration; + pQualifier = pDriver->pDescriptors->pFsQualifier; + pOtherSpeed = pDriver->pDescriptors->pFsOtherSpeed; + } + + // Check the descriptor type + switch (type) { + + case USBGenericDescriptor_DEVICE: + TRACE_INFO_WP("Dev "); + + // Adjust length and send descriptor + if (length > USBGenericDescriptor_GetLength((USBGenericDescriptor *) pDevice)) { + + length = USBGenericDescriptor_GetLength((USBGenericDescriptor *) pDevice); + } + USBD_Write(0, pDevice, length, 0, 0); + break; + + case USBGenericDescriptor_CONFIGURATION: + TRACE_INFO_WP("Cfg "); + + // Adjust length and send descriptor + if (length > USBConfigurationDescriptor_GetTotalLength(pConfiguration)) { + + length = USBConfigurationDescriptor_GetTotalLength(pConfiguration); + terminateWithNull = ((length % pDevice->bMaxPacketSize0) == 0); + } + USBD_Write(0, + pConfiguration, + length, + terminateWithNull ? TerminateCtrlInWithNull : 0, + 0); + break; + + case USBGenericDescriptor_DEVICEQUALIFIER: + TRACE_INFO_WP("Qua "); + + // Check if descriptor exists + if (!pQualifier) { + + USBD_Stall(0); + } + else { + + // Adjust length and send descriptor + if (length > USBGenericDescriptor_GetLength((USBGenericDescriptor *) pQualifier)) { + + length = USBGenericDescriptor_GetLength((USBGenericDescriptor *) pQualifier); + } + USBD_Write(0, pQualifier, length, 0, 0); + } + break; + + case USBGenericDescriptor_OTHERSPEEDCONFIGURATION: + TRACE_INFO_WP("OSC "); + + // Check if descriptor exists + if (!pOtherSpeed) { + + USBD_Stall(0); + } + else { + + // Adjust length and send descriptor + if (length > USBConfigurationDescriptor_GetTotalLength(pOtherSpeed)) { + + length = USBConfigurationDescriptor_GetTotalLength(pOtherSpeed); + terminateWithNull = ((length % pDevice->bMaxPacketSize0) == 0); + } + USBD_Write(0, + pOtherSpeed, + length, + terminateWithNull ? TerminateCtrlInWithNull : 0, + 0); + } + break; + + case USBGenericDescriptor_STRING: + TRACE_INFO_WP("Str%d ", indexRDesc); + + // Check if descriptor exists + if (indexRDesc >= numStrings) { + + USBD_Stall(0); + } + else { + + pString = pStrings[indexRDesc]; + + // Adjust length and send descriptor + if (length > USBGenericDescriptor_GetLength(pString)) { + + length = USBGenericDescriptor_GetLength(pString); + terminateWithNull = ((length % pDevice->bMaxPacketSize0) == 0); + } + USBD_Write(0, + pString, + length, + terminateWithNull ? TerminateCtrlInWithNull : 0, + 0); + } + break; + + default: + TRACE_WARNING( + "USBDDriver_GetDescriptor: Unknown descriptor type (%d)\n\r", + type); + USBD_Stall(0); + } +} + +//------------------------------------------------------------------------------ +/// Sets the active setting of the given interface if the configuration supports +/// it; otherwise, the control pipe is STALLed. If the setting of an interface +/// changes. +/// \parma pDriver Pointer to a USBDDriver instance. +/// \parma infnum Interface number. +/// \parma setting New active setting for the interface. +//------------------------------------------------------------------------------ +static void SetInterface( + USBDDriver *pDriver, + unsigned char infnum, + unsigned char setting) +{ + // Make sure alternate settings are supported + if (!pDriver->pInterfaces) { + + USBD_Stall(0); + } + else { + + // Change the current setting of the interface and trigger the callback + // if necessary + if (pDriver->pInterfaces[infnum] != setting) { + + pDriver->pInterfaces[infnum] = setting; + USBDDriverCallbacks_InterfaceSettingChanged(infnum, setting); + } + + // Acknowledge the request + USBD_Write(0, 0, 0, 0, 0); + } +} + +//------------------------------------------------------------------------------ +/// Sends the currently active setting of the given interface to the USB +/// host. If alternate settings are not supported, this function STALLs the +/// control pipe. +/// \param pDriver Pointer to a USBDDriver instance. +/// \param infnum Interface number. +//------------------------------------------------------------------------------ +static void GetInterface( + const USBDDriver *pDriver, + unsigned char infnum) +{ + // Make sure alternate settings are supported, or STALL the control pipe + if (!pDriver->pInterfaces) { + + USBD_Stall(0); + } + else { + + // Sends the current interface setting to the host + USBD_Write(0, &(pDriver->pInterfaces[infnum]), 1, 0, 0); + } +} + +#if defined(CHIP_USB_UDPHS) || defined(CHIP_USB_OTGHS) +//------------------------------------------------------------------------------ +// Performs the selected test on the USB device (high-speed only). +// \param test Test selector value. +//------------------------------------------------------------------------------ +static void USBDDriver_Test(unsigned char test) +{ + TRACE_DEBUG("UDPHS_Test\n\r"); + + // the lower byte of wIndex must be zero + // the most significant byte of wIndex is used to specify the specific test mode + switch (test) { + case USBFeatureRequest_TESTPACKET: + //Test mode Test_Packet: + //Upon command, a port must repetitively transmit the following test packet until + //the exit action is taken. This enables the testing of rise and fall times, eye + //patterns, jitter, and any other dynamic waveform specifications. + //The test packet is made up by concatenating the following strings. + //(Note: For J/K NRZI data, and for NRZ data, the bit on the left is the first one + //transmitted. “S” indicates that a bit stuff occurs, which inserts an “extra” NRZI data bit. + //“* N” is used to indicate N occurrences of a string of bits or symbols.) + //A port in Test_Packet mode must send this packet repetitively. The inter-packet timing + //must be no less than the minimum allowable inter-packet gap as defined in Section 7.1.18 and + //no greater than 125 us. + // Send ZLP + USBD_Test(USBFeatureRequest_TESTSENDZLP); + // Tst PACKET + USBD_Test(USBFeatureRequest_TESTPACKET); + while (1); + //break; not reached + + case USBFeatureRequest_TESTJ: + //Test mode Test_J: + //Upon command, a port’s transceiver must enter the high-speed J state and remain in that + //state until the exit action is taken. This enables the testing of the high output drive + //level on the D+ line. + // Send ZLP + USBD_Test(USBFeatureRequest_TESTSENDZLP); + // Tst J + USBD_Test(USBFeatureRequest_TESTJ); + while (1); + //break; not reached + + case USBFeatureRequest_TESTK: + //Test mode Test_K: + //Upon command, a port’s transceiver must enter the high-speed K state and remain in + //that state until the exit action is taken. This enables the testing of the high output drive + //level on the D- line. + // Send a ZLP + USBD_Test(USBFeatureRequest_TESTSENDZLP); + USBD_Test(USBFeatureRequest_TESTK); + while (1); + //break; not reached + + case USBFeatureRequest_TESTSE0NAK: + //Test mode Test_SE0_NAK: + //Upon command, a port’s transceiver must enter the high-speed receive mode + //and remain in that mode until the exit action is taken. This enables the testing + //of output impedance, low level output voltage, and loading characteristics. + //In addition, while in this mode, upstream facing ports (and only upstream facing ports) + //must respond to any IN token packet with a NAK handshake (only if the packet CRC is + //determined to be correct) within the normal allowed device response time. This enables testing of + //the device squelch level circuitry and, additionally, provides a general purpose stimulus/response + //test for basic functional testing. + // Send a ZLP + USBD_Test(USBFeatureRequest_TESTSENDZLP); + // Test SE0_NAK + USBD_Test(USBFeatureRequest_TESTSE0NAK); + while (1); + //break; not reached + + default: + USBD_Stall(0); + break; + + } + // The exit action is to power cycle the device. + // The device must be disconnected from the host +} +#endif + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Initializes a USBDDriver instance with a list of descriptors. If +/// interfaces can have multiple alternate settings, an array to store the +/// current setting for each interface must be provided. +/// \param pDriver Pointer to a USBDDriver instance. +/// \param pDescriptors Pointer to a USBDDriverDescriptors instance. +/// \param pInterfaces Pointer to an array for storing the current alternate +/// setting of each interface (optional). +//------------------------------------------------------------------------------ +void USBDDriver_Initialize( + USBDDriver *pDriver, + const USBDDriverDescriptors *pDescriptors, + unsigned char *pInterfaces) +{ + + pDriver->cfgnum = 0; +#if (BOARD_USB_BMATTRIBUTES == USBConfigurationDescriptor_SELFPOWERED_RWAKEUP) \ + || (BOARD_USB_BMATTRIBUTES == USBConfigurationDescriptor_BUSPOWERED_RWAKEUP) + pDriver->isRemoteWakeUpEnabled = 1; +#else + pDriver->isRemoteWakeUpEnabled = 0; +#endif + + pDriver->pDescriptors = pDescriptors; + pDriver->pInterfaces = pInterfaces; + + // Initialize interfaces array if not null + if (pInterfaces != 0) { + + memset(pInterfaces, sizeof(pInterfaces), 0); + } +} + +//------------------------------------------------------------------------------ +/// Handles the given request if it is standard, otherwise STALLs it. +/// \param pDriver Pointer to a USBDDriver instance. +/// \param pRequest Pointer to a USBGenericRequest instance. +//------------------------------------------------------------------------------ +void USBDDriver_RequestHandler( + USBDDriver *pDriver, + const USBGenericRequest *pRequest) +{ + unsigned char cfgnum; + unsigned char infnum; + unsigned char eptnum; + unsigned char setting; + unsigned char type; + unsigned char indexDesc; + unsigned int length; + unsigned int address; + + TRACE_INFO_WP("Std "); + + // Check request code + switch (USBGenericRequest_GetRequest(pRequest)) { + + case USBGenericRequest_GETDESCRIPTOR: + TRACE_INFO_WP("gDesc "); + + // Send the requested descriptor + type = USBGetDescriptorRequest_GetDescriptorType(pRequest); + indexDesc = USBGetDescriptorRequest_GetDescriptorIndex(pRequest); + length = USBGenericRequest_GetLength(pRequest); + GetDescriptor(pDriver, type, indexDesc, length); + break; + + case USBGenericRequest_SETADDRESS: + TRACE_INFO_WP("sAddr "); + + // Sends a zero-length packet and then set the device address + address = USBSetAddressRequest_GetAddress(pRequest); + USBD_Write(0, 0, 0, (TransferCallback) USBD_SetAddress, (void *) address); + break; + + case USBGenericRequest_SETCONFIGURATION: + TRACE_INFO_WP("sCfg "); + + // Set the requested configuration + cfgnum = USBSetConfigurationRequest_GetConfiguration(pRequest); + SetConfiguration(pDriver, cfgnum); + break; + + case USBGenericRequest_GETCONFIGURATION: + TRACE_INFO_WP("gCfg "); + + // Send the current configuration number + GetConfiguration(pDriver); + break; + + case USBGenericRequest_GETSTATUS: + TRACE_INFO_WP("gSta "); + + // Check who is the recipient + switch (USBGenericRequest_GetRecipient(pRequest)) { + + case USBGenericRequest_DEVICE: + TRACE_INFO_WP("Dev "); + + // Send the device status + GetDeviceStatus(pDriver); + break; + + case USBGenericRequest_ENDPOINT: + TRACE_INFO_WP("Ept "); + + // Send the endpoint status + eptnum = USBGenericRequest_GetEndpointNumber(pRequest); + GetEndpointStatus(eptnum); + break; + + default: + TRACE_WARNING( + "USBDDriver_RequestHandler: Unknown recipient (%d)\n\r", + USBGenericRequest_GetRecipient(pRequest)); + USBD_Stall(0); + } + break; + + case USBGenericRequest_CLEARFEATURE: + TRACE_INFO_WP("cFeat "); + + // Check which is the requested feature + switch (USBFeatureRequest_GetFeatureSelector(pRequest)) { + + case USBFeatureRequest_ENDPOINTHALT: + TRACE_INFO_WP("Hlt "); + + // Unhalt endpoint and send a zero-length packet + USBD_Unhalt(USBGenericRequest_GetEndpointNumber(pRequest)); + USBD_Write(0, 0, 0, 0, 0); + break; + + case USBFeatureRequest_DEVICEREMOTEWAKEUP: + TRACE_INFO_WP("RmWU "); + + // Disable remote wake-up and send a zero-length packet + pDriver->isRemoteWakeUpEnabled = 0; + USBD_Write(0, 0, 0, 0, 0); + break; + + default: + TRACE_WARNING( + "USBDDriver_RequestHandler: Unknown feature selector (%d)\n\r", + USBFeatureRequest_GetFeatureSelector(pRequest)); + USBD_Stall(0); + } + break; + + case USBGenericRequest_SETFEATURE: + TRACE_INFO_WP("sFeat "); + + // Check which is the selected feature + switch (USBFeatureRequest_GetFeatureSelector(pRequest)) { + + case USBFeatureRequest_DEVICEREMOTEWAKEUP: + TRACE_INFO_WP("RmWU "); + + // Enable remote wake-up and send a ZLP + pDriver->isRemoteWakeUpEnabled = 1; + USBD_Write(0, 0, 0, 0, 0); + break; + + case USBFeatureRequest_ENDPOINTHALT: + TRACE_INFO_WP("Halt "); + // Halt endpoint + USBD_Halt(USBGenericRequest_GetEndpointNumber(pRequest)); + USBD_Write(0, 0, 0, 0, 0); + break; + +#if defined(CHIP_USB_UDPHS) || defined(CHIP_USB_OTGHS) + + case USBFeatureRequest_TESTMODE: + // 7.1.20 Test Mode Support, 9.4.9 SetFeature + if ((USBGenericRequest_GetRecipient(pRequest) == USBGenericRequest_DEVICE) + && ((USBGenericRequest_GetIndex(pRequest) & 0x000F) == 0)) { + + // Handle test request + USBDDriver_Test(USBFeatureRequest_GetTestSelector(pRequest)); + } + else { + + USBD_Stall(0); + } + break; +#endif +#if defined(CHIP_USB_OTGHS) + case USBFeatureRequest_OTG_B_HNP_ENABLE: + TRACE_INFO_WP("OTG_B_HNP_ENABLE "); + otg_features_supported |= 1<isRemoteWakeUpEnabled; +} + +#if defined(CHIP_USB_OTGHS) +//------------------------------------------------------------------------------ +/// Return OTG features supported +/// \return the OTG features +//------------------------------------------------------------------------------ +unsigned char USBDDriver_returnOTGFeatures(void) +{ + return otg_features_supported; +} + +//------------------------------------------------------------------------------ +/// Clear OTG features supported +/// \return none +//------------------------------------------------------------------------------ +void USBDDriver_clearOTGFeatures(void) +{ + otg_features_supported = 0; +} +#endif + -- cgit v1.2.3