From 98f9d442b44dbe2e3e4b3c8296be7e78d5d05450 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Sun, 24 Jul 2011 09:39:28 +0200 Subject: initial import of the usb ccid example for the sam7s --- at91lib/usb/device/core/USBD.h | 187 +++ at91lib/usb/device/core/USBDCallbacks.h | 65 + .../usb/device/core/USBDCallbacks_Initialized.c | 62 + at91lib/usb/device/core/USBDCallbacks_Reset.c | 47 + at91lib/usb/device/core/USBDDriver.c | 682 ++++++++ at91lib/usb/device/core/USBDDriver.h | 93 ++ at91lib/usb/device/core/USBDDriverCallbacks.h | 61 + at91lib/usb/device/core/USBDDriverCb_CfgChanged.c | 49 + .../device/core/USBDDriverCb_IfSettingChanged.c | 52 + at91lib/usb/device/core/USBDDriverDescriptors.h | 86 + at91lib/usb/device/core/USBD_OTGHS.c | 1677 +++++++++++++++++++ at91lib/usb/device/core/USBD_UDP.c | 1224 ++++++++++++++ at91lib/usb/device/core/USBD_UDPHS.c | 1680 ++++++++++++++++++++ 13 files changed, 5965 insertions(+) create mode 100644 at91lib/usb/device/core/USBD.h create mode 100644 at91lib/usb/device/core/USBDCallbacks.h create mode 100644 at91lib/usb/device/core/USBDCallbacks_Initialized.c create mode 100644 at91lib/usb/device/core/USBDCallbacks_Reset.c create mode 100644 at91lib/usb/device/core/USBDDriver.c create mode 100644 at91lib/usb/device/core/USBDDriver.h create mode 100644 at91lib/usb/device/core/USBDDriverCallbacks.h create mode 100644 at91lib/usb/device/core/USBDDriverCb_CfgChanged.c create mode 100644 at91lib/usb/device/core/USBDDriverCb_IfSettingChanged.c create mode 100644 at91lib/usb/device/core/USBDDriverDescriptors.h create mode 100644 at91lib/usb/device/core/USBD_OTGHS.c create mode 100644 at91lib/usb/device/core/USBD_UDP.c create mode 100644 at91lib/usb/device/core/USBD_UDPHS.c (limited to 'at91lib/usb/device/core') diff --git a/at91lib/usb/device/core/USBD.h b/at91lib/usb/device/core/USBD.h new file mode 100644 index 0000000..6680e27 --- /dev/null +++ b/at91lib/usb/device/core/USBD.h @@ -0,0 +1,187 @@ +/* ---------------------------------------------------------------------------- + * 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. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +/// \unit +/// +/// !!!Purpose +/// +/// Collection of methods for using the USB device controller on AT91 +/// microcontrollers. +/// +/// !!!Usage +/// +/// Please refer to the corresponding application note. +/// - "AT91 USB device framework" +/// - "USBD API" . "USBD API Methods" +//------------------------------------------------------------------------------ + +#ifndef USBD_H +#define USBD_H + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include +#include + +//------------------------------------------------------------------------------ +// Definitions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "USB device API return values" +/// +/// This page lists the return values of the USB %device driver API +/// +/// !Return codes +/// - USBD_STATUS_SUCCESS +/// - USBD_STATUS_LOCKED +/// - USBD_STATUS_ABORTED +/// - USBD_STATUS_RESET + +/// Indicates the operation was successful. +#define USBD_STATUS_SUCCESS 0 +/// Endpoint/device is already busy. +#define USBD_STATUS_LOCKED 1 +/// Operation has been aborted. +#define USBD_STATUS_ABORTED 2 +/// Operation has been aborted because the device has been reset. +#define USBD_STATUS_RESET 3 +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "USB device states" +/// +/// This page lists the %device states of the USB %device driver. +/// +/// !States +/// - USBD_STATE_SUSPENDED +/// - USBD_STATE_ATTACHED +/// - USBD_STATE_POWERED +/// - USBD_STATE_DEFAULT +/// - USBD_STATE_ADDRESS +/// - USBD_STATE_CONFIGURED + +/// The device is currently suspended. +#define USBD_STATE_SUSPENDED 0 +/// USB cable is plugged into the device. +#define USBD_STATE_ATTACHED 1 +/// Host is providing +5V through the USB cable. +#define USBD_STATE_POWERED 2 +/// Device has been reset. +#define USBD_STATE_DEFAULT 3 +/// The device has been given an address on the bus. +#define USBD_STATE_ADDRESS 4 +/// A valid configuration has been selected. +#define USBD_STATE_CONFIGURED 5 +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "USB device LEDs" +/// +/// This page lists the LEDs used in the USB %device driver. +/// +/// !LEDs +/// - USBD_LEDPOWER +/// - USBD_LEDUSB +/// - USBD_LEDOTHER + +/// LED for indicating that the device is powered. +#define USBD_LEDPOWER 0 +/// LED for indicating USB activity. +#define USBD_LEDUSB 1 +/// LED for custom usage. +#define USBD_LEDOTHER 2 +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// Types +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Callback used by transfer functions (USBD_Read & USBD_Write) to notify +/// that a transaction is complete. +//------------------------------------------------------------------------------ +typedef void (*TransferCallback)(void *pArg, + unsigned char status, + unsigned int transferred, + unsigned int remaining); + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +extern void USBD_InterruptHandler(void); + +extern void USBD_Init(void); + +extern void USBD_Connect(void); + +extern void USBD_Disconnect(void); + +extern char USBD_Write( + unsigned char bEndpoint, + const void *pData, + unsigned int size, + TransferCallback callback, + void *pArg); + +extern char USBD_Read( + unsigned char bEndpoint, + void *pData, + unsigned int dLength, + TransferCallback fCallback, + void *pArg); + +extern unsigned char USBD_Stall(unsigned char bEndpoint); + +extern void USBD_Halt(unsigned char bEndpoint); + +extern void USBD_Unhalt(unsigned char bEndpoint); + +extern void USBD_ConfigureEndpoint(const USBEndpointDescriptor *pDescriptor); + +extern unsigned char USBD_IsHalted(unsigned char bEndpoint); + +extern void USBD_RemoteWakeUp(void); + +extern void USBD_SetAddress(unsigned char address); + +extern void USBD_SetConfiguration(unsigned char cfgnum); + +extern unsigned char USBD_GetState(void); + +extern unsigned char USBD_IsHighSpeed(void); + +extern void USBD_Test(unsigned char bIndex); + +#endif //#ifndef USBD_H + diff --git a/at91lib/usb/device/core/USBDCallbacks.h b/at91lib/usb/device/core/USBDCallbacks.h new file mode 100644 index 0000000..d4d5c7e --- /dev/null +++ b/at91lib/usb/device/core/USBDCallbacks.h @@ -0,0 +1,65 @@ +/* ---------------------------------------------------------------------------- + * 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. + * ---------------------------------------------------------------------------- + */ + +/** + \unit + + !!!Purpose + + Definitions of callbacks used by the USBD API to notify the user + application of incoming events. These functions are declared as 'weak', + so they can be re-implemented elsewhere in the application in a + transparent way. +*/ + +#ifndef USBDCALLBACKS_H +#define USBDCALLBACKS_H + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +extern void USBDCallbacks_Initialized(void); + +extern void USBDCallbacks_Reset(void); + +extern void USBDCallbacks_Suspended(void); + +extern void USBDCallbacks_Resumed(void); + +extern void USBDCallbacks_RequestReceived(const USBGenericRequest *request); + +#endif //#ifndef USBDCALLBACKS_H + diff --git a/at91lib/usb/device/core/USBDCallbacks_Initialized.c b/at91lib/usb/device/core/USBDCallbacks_Initialized.c new file mode 100644 index 0000000..9c84773 --- /dev/null +++ b/at91lib/usb/device/core/USBDCallbacks_Initialized.c @@ -0,0 +1,62 @@ +/* ---------------------------------------------------------------------------- + * 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 "USBDCallbacks.h" +#include "USBD.h" +#include +#include + +//------------------------------------------------------------------------------ +// Exported function +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Invoked after the USB driver has been initialized. By default, configures +/// the UDP/UDPHS interrupt. +//------------------------------------------------------------------------------ +void USBDCallbacks_Initialized(void) +{ +#if defined(BOARD_USB_UDP) + // Configure and enable the UDP interrupt + AIC_ConfigureIT(AT91C_ID_UDP, 0, USBD_InterruptHandler); + AIC_EnableIT(AT91C_ID_UDP); + +#elif defined(BOARD_USB_UDPHS) + // Configure and enable the UDPHS interrupt + AIC_ConfigureIT(AT91C_ID_UDPHS, 0, USBD_InterruptHandler); + AIC_EnableIT(AT91C_ID_UDPHS); +#else + #error Unsupported controller. +#endif +} + diff --git a/at91lib/usb/device/core/USBDCallbacks_Reset.c b/at91lib/usb/device/core/USBDCallbacks_Reset.c new file mode 100644 index 0000000..64d9aaa --- /dev/null +++ b/at91lib/usb/device/core/USBDCallbacks_Reset.c @@ -0,0 +1,47 @@ +/* ---------------------------------------------------------------------------- + * 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 "USBDCallbacks.h" + +//------------------------------------------------------------------------------ +// Exported function +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Invoked when the USB driver is reset. Does nothing by default. +//------------------------------------------------------------------------------ +void USBDCallbacks_Reset(void) +{ + // Does nothing +} + diff --git a/at91lib/usb/device/core/USBDDriver.c b/at91lib/usb/device/core/USBDDriver.c new file mode 100644 index 0000000..c2a8abd --- /dev/null +++ b/at91lib/usb/device/core/USBDDriver.c @@ -0,0 +1,682 @@ +/* ---------------------------------------------------------------------------- + * 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 +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// 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[BOARD_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) +{ + unsigned short data = 0; + const USBConfigurationDescriptor *pConfiguration; + + // 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) +{ + unsigned short data = 0; + + // Check if the endpoint exists + if (bEndpoint > BOARD_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 index, + unsigned int length) +{ + const USBDeviceDescriptor *pDevice; + const USBConfigurationDescriptor *pConfiguration; + const USBDeviceQualifierDescriptor *pQualifier; + const USBConfigurationDescriptor *pOtherSpeed; + const USBGenericDescriptor **pStrings = + (const USBGenericDescriptor **) pDriver->pDescriptors->pStrings; + unsigned char numStrings = pDriver->pDescriptors->numStrings; + const USBGenericDescriptor *pString; + + // 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); + } + USBD_Write(0, pConfiguration, length, 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); + } + USBD_Write(0, pOtherSpeed, length, 0, 0); + } + break; + + case USBGenericDescriptor_STRING: + TRACE_INFO_WP("Str%d ", index); + + // Check if descriptor exists + if (index > numStrings) { + + USBD_Stall(0); + } + else { + + pString = pStrings[index]; + + // Adjust length and send descriptor + if (length > USBGenericDescriptor_GetLength(pString)) { + + length = USBGenericDescriptor_GetLength(pString); + } + USBD_Write(0, pString, length, 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); + } +} + +#ifdef BOARD_USB_UDPHS +//------------------------------------------------------------------------------ +// 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; + + 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; + + 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; + + 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. + USBD_Test(USBFeatureRequest_TESTSE0NAK); + // Send a ZLP + USBD_Test(USBFeatureRequest_TESTSENDZLP); + while (1); + break; + + 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 index; + 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); + index = USBGetDescriptorRequest_GetDescriptorIndex(pRequest); + length = USBGenericRequest_GetLength(pRequest); + GetDescriptor(pDriver, type, index, 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("Ept "); + + // Halt endpoint + USBD_Halt(USBGenericRequest_GetEndpointNumber(pRequest)); + USBD_Write(0, 0, 0, 0, 0); + break; + +#if defined(BOARD_USB_UDPHS) + + case USBFeatureRequest_TESTMODE: + // 7.1.20 Test Mode Support + if ((USBGenericRequest_GetType(pRequest) == USBGenericRequest_DEVICE) + && ((USBGenericRequest_GetIndex(pRequest) & 0x000F) == 0)) { + + // Handle test request + USBDDriver_Test(USBFeatureRequest_GetTestSelector(pRequest)); + } + else { + + USBD_Stall(0); + } + break; +#endif + + default: + TRACE_WARNING( + "USBDDriver_RequestHandler: Unknown feature selector (%d)\n\r", + USBFeatureRequest_GetFeatureSelector(pRequest)); + USBD_Stall(0); + } + break; + + case USBGenericRequest_SETINTERFACE: + TRACE_INFO_WP("sInterface "); + + infnum = USBInterfaceRequest_GetInterface(pRequest); + setting = USBInterfaceRequest_GetAlternateSetting(pRequest); + SetInterface(pDriver, infnum, setting); + break; + + case USBGenericRequest_GETINTERFACE: + TRACE_INFO_WP("gInterface "); + + infnum = USBInterfaceRequest_GetInterface(pRequest); + GetInterface(pDriver, infnum); + break; + + default: + TRACE_WARNING( + "USBDDriver_RequestHandler: Unknown request code (%d)\n\r", + USBGenericRequest_GetRequest(pRequest)); + USBD_Stall(0); + } +} + + +//------------------------------------------------------------------------------ +/// Test if RemoteWakeUP feature is enabled +/// \param pDriver Pointer to an USBDDriver instance. +/// \return 1 if remote wake up has been enabled by the host; otherwise, returns +/// 0 +//------------------------------------------------------------------------------ +unsigned char USBDDriver_IsRemoteWakeUpEnabled(const USBDDriver *pDriver) +{ + return pDriver->isRemoteWakeUpEnabled; +} + + diff --git a/at91lib/usb/device/core/USBDDriver.h b/at91lib/usb/device/core/USBDDriver.h new file mode 100644 index 0000000..7b14b8e --- /dev/null +++ b/at91lib/usb/device/core/USBDDriver.h @@ -0,0 +1,93 @@ +/* ---------------------------------------------------------------------------- + * 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. + * ---------------------------------------------------------------------------- + */ + +/** + \unit + + !!!Purpose + + USB Device Driver class definition. + + !!!Usage + + -# Instanciate a USBDDriver object and initialize it using + USBDDriver_Initialize. + -# When a USB SETUP request is received, forward it to the standard + driver using USBDDriver_RequestHandler. + -# Check the Remote Wakeup setting via USBDDriver_IsRemoteWakeUpEnabled. +*/ + +#ifndef USBDDRIVER_H +#define USBDDRIVER_H + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "USBDDriverDescriptors.h" +#include + +//------------------------------------------------------------------------------ +// Types +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// USB device driver structure, holding a list of descriptors identifying +/// the device as well as the driver current state. +//------------------------------------------------------------------------------ +typedef struct { + + /// List of descriptors used by the device. + const USBDDriverDescriptors *pDescriptors; + /// Current setting for each interface. + unsigned char *pInterfaces; + /// Current configuration number (0 -> device is not configured). + unsigned char cfgnum; + /// Indicates if remote wake up has been enabled by the host. + unsigned char isRemoteWakeUpEnabled; + +} USBDDriver; + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +extern void USBDDriver_Initialize( + USBDDriver *pDriver, + const USBDDriverDescriptors *pDescriptors, + unsigned char *pInterfaces); + +extern void USBDDriver_RequestHandler( + USBDDriver *pDriver, + const USBGenericRequest *pRequest); + +extern unsigned char USBDDriver_IsRemoteWakeUpEnabled(const USBDDriver *pDriver); + +#endif //#ifndef USBDDRIVER_H + diff --git a/at91lib/usb/device/core/USBDDriverCallbacks.h b/at91lib/usb/device/core/USBDDriverCallbacks.h new file mode 100644 index 0000000..053e8ac --- /dev/null +++ b/at91lib/usb/device/core/USBDDriverCallbacks.h @@ -0,0 +1,61 @@ +/* ---------------------------------------------------------------------------- + * 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. + * ---------------------------------------------------------------------------- + */ + +/** + \unit + + !!!Purpose + + Definition of several callbacks which are triggered by the USB software + driver after receiving specific requests. + + !!!Usage + + -# Re-implement the USBDDriverCallbacks_ConfigurationChanged + callback to know when the hosts changes the active configuration of + the device. + -# Re-implement the USBDDriverCallbacks_InterfaceSettingChanged + callback to get notified whenever the active setting of an interface + is changed by the host. +*/ + +#ifndef USBDDRIVERCALLBACKS_H +#define USBDDRIVERCALLBACKS_H + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +extern void USBDDriverCallbacks_ConfigurationChanged(unsigned char cfgnum); + +extern void USBDDriverCallbacks_InterfaceSettingChanged(unsigned char interface, + unsigned char setting); + +#endif //#ifndef USBDDRIVERCALLBACKS_H + diff --git a/at91lib/usb/device/core/USBDDriverCb_CfgChanged.c b/at91lib/usb/device/core/USBDDriverCb_CfgChanged.c new file mode 100644 index 0000000..08e2b2f --- /dev/null +++ b/at91lib/usb/device/core/USBDDriverCb_CfgChanged.c @@ -0,0 +1,49 @@ +/* ---------------------------------------------------------------------------- + * 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 "USBDDriverCallbacks.h" +#include + +//------------------------------------------------------------------------------ +// Global functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Indicates that the current configuration of the device has changed. +/// \param cfgnum New device configuration index. +//------------------------------------------------------------------------------ +void USBDDriverCallbacks_ConfigurationChanged(unsigned char cfgnum) +{ + TRACE_INFO_WP("ConfigurationChanged "); +} + diff --git a/at91lib/usb/device/core/USBDDriverCb_IfSettingChanged.c b/at91lib/usb/device/core/USBDDriverCb_IfSettingChanged.c new file mode 100644 index 0000000..c9049e6 --- /dev/null +++ b/at91lib/usb/device/core/USBDDriverCb_IfSettingChanged.c @@ -0,0 +1,52 @@ +/* ---------------------------------------------------------------------------- + * 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 "USBDDriverCallbacks.h" +#include + +//------------------------------------------------------------------------------ +// Global functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Notifies of a change in the currently active setting of an interface. +/// \param interface Number of the interface whose setting has changed. +/// \param setting New interface setting. +//------------------------------------------------------------------------------ +void USBDDriverCallbacks_InterfaceSettingChanged( + unsigned char interface, + unsigned char setting) +{ + TRACE_INFO_WP("InterfaceSettingChanged "); +} + diff --git a/at91lib/usb/device/core/USBDDriverDescriptors.h b/at91lib/usb/device/core/USBDDriverDescriptors.h new file mode 100644 index 0000000..f1064e4 --- /dev/null +++ b/at91lib/usb/device/core/USBDDriverDescriptors.h @@ -0,0 +1,86 @@ +/* ---------------------------------------------------------------------------- + * 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. + * ---------------------------------------------------------------------------- + */ + +/** + \unit + + !!!Purpose + + Definition of a class for declaring USB descriptors required by the + device driver. +*/ + +#ifndef USBDDRIVERDESCRIPTORS_H +#define USBDDRIVERDESCRIPTORS_H + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include +#include +#include + +//------------------------------------------------------------------------------ +// Types +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// List of all descriptors used by a USB device driver. Each descriptor can +/// be provided in two versions: full-speed and high-speed. Devices which are +/// not high-speed capable do not need to provided high-speed descriptors and +/// the full-speed qualifier & other speed descriptors. +//------------------------------------------------------------------------------ +typedef struct { + + /// Pointer to the full-speed device descriptor. + const USBDeviceDescriptor *pFsDevice; + /// Pointer to the full-speed configuration descriptor. + const USBConfigurationDescriptor *pFsConfiguration; + /// Pointer to the full-speed qualifier descriptor. + const USBDeviceQualifierDescriptor *pFsQualifier; + /// Pointer to the full-speed other speed configuration descriptor. + const USBConfigurationDescriptor *pFsOtherSpeed; + /// Pointer to the high-speed device descriptor. + const USBDeviceDescriptor *pHsDevice; + /// Pointer to the high-speed configuration descriptor. + const USBConfigurationDescriptor *pHsConfiguration; + /// Pointer to the high-speed qualifier descriptor. + const USBDeviceQualifierDescriptor *pHsQualifier; + /// Pointer to the high-speed other speed configuration descriptor. + const USBConfigurationDescriptor *pHsOtherSpeed; + /// Pointer to the list of string descriptors. + const unsigned char **pStrings; + /// Number of string descriptors in list. + unsigned char numStrings; + +} USBDDriverDescriptors; + +#endif //#ifndef USBDDRIVERDESCRIPTORS_H + diff --git a/at91lib/usb/device/core/USBD_OTGHS.c b/at91lib/usb/device/core/USBD_OTGHS.c new file mode 100644 index 0000000..0f87143 --- /dev/null +++ b/at91lib/usb/device/core/USBD_OTGHS.c @@ -0,0 +1,1677 @@ +/* ---------------------------------------------------------------------------- + * 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. + * ---------------------------------------------------------------------------- + */ + +/*! + Functions for OTGHS peripheral usage. +*/ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include + +#ifdef CHIP_OTGHS + +#include "common.h" +#include "trace.h" +#include "usb.h" + +//------------------------------------------------------------------------------ +// Definitions +//------------------------------------------------------------------------------ + +#define NUM_IT_MAX (AT91C_BASE_OTGHS->OTGHS_IPFEATURES & AT91C_OTGHS_EPT_NBR_MAX) +#define NUM_IT_MAX_DMA ((AT91C_BASE_OTGHS->OTGHS_IPFEATURES & AT91C_OTGHS_DMA_CHANNEL_NBR)>>4) + +#define SHIFT_DMA 24 +#define SHIFT_INTERUPT 12 + +#define DMA + +//------------------------------------------------------------------------------ +// Structures +//------------------------------------------------------------------------------ + +// \brief Endpoint states +typedef enum { + + endpointStateDisabled, + endpointStateIdle, + endpointStateWrite, + endpointStateRead, + endpointStateHalted + +} EndpointState_t; + +//------------------------------------------------------------------------------ +// Macros +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// Internal Functions +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// \brief Returns a pointer to the OTGHS controller interface used by an USB +// driver +// +// The pointer is cast to the correct type (AT91PS_OTGHS). +// \param pUsb Pointer to a S_usb instance +// \return Pointer to the USB controller interface +// \see S_usb +//------------------------------------------------------------------------------ +static AT91PS_OTGHS OTGHS_GetDriverInterface(const S_usb *pUsb) +{ + return (AT91PS_OTGHS) pUsb->pDriver->pInterface; +} + +//------------------------------------------------------------------------------ +// \fn OTGHS_GetInterfaceEPT +// \brief Returns OTGHS endpoint FIFO interface from S_usb structure +//------------------------------------------------------------------------------ +static AT91PS_OTGHS_EPTFIFO OTGHS_GetInterfaceEPT(const S_usb *pUsb) +{ + return (AT91PS_OTGHS_EPTFIFO) pUsb->pDriver->pEndpointFIFO; +} + + +//------------------------------------------------------------------------------ +// \brief Enables the peripheral clock of the USB controller associated with +// the specified USB driver +// \param pUsb Pointer to a S_usb instance +// \see S_usb +//------------------------------------------------------------------------------ +static void OTGHS_EnableMCK(const S_usb *pUsb) +{ + +} + +//------------------------------------------------------------------------------ +// \brief Disables the peripheral clock of the USB controller associated with +// the specified USB driver +// \param pUsb Pointer to a S_usb instance +// \see S_usb +//------------------------------------------------------------------------------ +static void OTGHS_DisableMCK(const S_usb *pUsb) +{ + +} + +//------------------------------------------------------------------------------ +// \brief Enables the 48MHz clock of the USB controller associated with +// the specified USB driver +// \param pUsb Pointer to a S_usb instance +// \see S_usb +//------------------------------------------------------------------------------ +static void OTGHS_EnableOTGHSCK(const S_usb *pUsb) +{ + +} + +//------------------------------------------------------------------------------ +// \brief Disables the 48MHz clock of the USB controller associated with +// the specified USB driver +// \param pUsb Pointer to a S_usb instance +// \see S_usb +//------------------------------------------------------------------------------ +static void OTGHS_DisableOTGHSCK(const S_usb *pUsb) +{ + +} + +//------------------------------------------------------------------------------ +// \brief Enables the transceiver of the USB controller associated with +// the specified USB driver +// \param pUsb Pointer to a S_usb instance +// \see S_usb +//------------------------------------------------------------------------------ +static void OTGHS_EnableTransceiver(const S_usb *pUsb) +{ + SET(OTGHS_GetDriverInterface(pUsb)->OTGHS_CTRL, AT91C_OTGHS_OTGPADE); +} + +//------------------------------------------------------------------------------ +// \brief Disables the transceiver of the USB controller associated with +// the specified USB driver +// \param pUsb Pointer to a S_usb instance +// \see S_usb +//------------------------------------------------------------------------------ +static void OTGHS_DisableTransceiver(const S_usb *pUsb) +{ + CLEAR(OTGHS_GetDriverInterface(pUsb)->OTGHS_CTRL, AT91C_OTGHS_OTGPADE); +} + +//------------------------------------------------------------------------------ +// \brief Invokes the callback associated with a finished transfer on an +// endpoint +// \param pEndpoint Pointer to a S_usb_endpoint instance +// \param bStatus Status code returned by the transfer operation +// \see Status codes +// \see S_usb_endpoint +//------------------------------------------------------------------------------ +static void OTGHS_EndOfTransfer(S_usb_endpoint *pEndpoint, + char bStatus) +{ + if ((pEndpoint->dState == endpointStateWrite) + || (pEndpoint->dState == endpointStateRead)) { + + TRACE_DEBUG_WP("E"); + + // Endpoint returns in Idle state + pEndpoint->dState = endpointStateIdle; + + // Invoke callback is present + if (pEndpoint->fCallback != 0) { + + pEndpoint->fCallback((unsigned int) pEndpoint->pArgument, + (unsigned int) bStatus, + pEndpoint->dBytesTransferred, + pEndpoint->dBytesRemaining + + pEndpoint->dBytesBuffered); + } + } +} + +//------------------------------------------------------------------------------ +// \brief Transfers a data payload from the current tranfer buffer to the +// endpoint FIFO. +// \param pUsb Pointer to a S_usb instance +// \param bEndpoint Index of endpoint +// \return Number of bytes transferred +// \see S_usb +//------------------------------------------------------------------------------ +static unsigned int OTGHS_WritePayload(const S_usb *pUsb, + unsigned char bEndpoint) +{ + AT91PS_OTGHS_EPTFIFO pInterfaceEPT = OTGHS_GetInterfaceEPT(pUsb); + S_usb_endpoint *pEndpoint = USB_GetEndpoint(pUsb, bEndpoint); + char *pfifo; + unsigned int dBytes; + unsigned int dCtr; + + pfifo = (char*)&(pInterfaceEPT->OTGHS_READEPT0[bEndpoint*16384]); + + // Get the number of bytes to send + dBytes = min(pEndpoint->wMaxPacketSize, pEndpoint->dBytesRemaining); + + // Transfer one packet in the FIFO buffer + for (dCtr = 0; dCtr < dBytes; dCtr++) { + + pfifo[dCtr] = *(pEndpoint->pData); + pEndpoint->pData++; + } + + pEndpoint->dBytesBuffered += dBytes; + pEndpoint->dBytesRemaining -= dBytes; + + return dBytes; +} + +//---------------------------------------------------------------------------- +// \brief Transfers a data payload from an endpoint FIFO to the current +// transfer buffer. +// \param pUsb Pointer to a S_usb instance +// \param bEndpoint Index of endpoint +// \param wPacketSize Size of received data packet +// \return Number of bytes transferred +// \see S_usb +//------------------------------------------------------------------------------ +static unsigned int OTGHS_GetPayload(const S_usb *pUsb, + unsigned char bEndpoint, + unsigned short wPacketSize) +{ + AT91PS_OTGHS_EPTFIFO pInterfaceEPT = OTGHS_GetInterfaceEPT(pUsb); + S_usb_endpoint *pEndpoint = USB_GetEndpoint(pUsb, bEndpoint); + char *pfifo; + unsigned int dBytes; + unsigned int dCtr; + + pfifo = (char*)&(pInterfaceEPT->OTGHS_READEPT0[bEndpoint*16384]); + + // Get number of bytes to retrieve + dBytes = min(pEndpoint->dBytesRemaining, wPacketSize); + + // Retrieve packet + for (dCtr = 0; dCtr < dBytes; dCtr++) { + + *(pEndpoint->pData) = pfifo[dCtr]; + pEndpoint->pData++; + } + + pEndpoint->dBytesRemaining -= dBytes; + pEndpoint->dBytesTransferred += dBytes; + pEndpoint->dBytesBuffered += wPacketSize - dBytes; + + return dBytes; +} + +//------------------------------------------------------------------------------ +// \brief Transfers a received SETUP packet from endpoint 0 FIFO to the +// S_usb_request structure of an USB driver +// \param pUsb Pointer to a S_usb instance +// \see S_usb +//------------------------------------------------------------------------------ +static void OTGHS_GetSetup(const S_usb *pUsb) +{ + unsigned int *pData = (unsigned int *) USB_GetSetup(pUsb); + AT91PS_OTGHS_EPTFIFO pInterfaceEPT = OTGHS_GetInterfaceEPT(pUsb); + + pData[0] = pInterfaceEPT->OTGHS_READEPT0[0]; + pData[1] = pInterfaceEPT->OTGHS_READEPT0[0]; +} + +//------------------------------------------------------------------------------ +// \brief This function reset all endpoint transfer descriptors +// \param pUsb Pointer to a S_usb instance +// \see S_usb +//------------------------------------------------------------------------------ +static void OTGHS_ResetEndpoints(const S_usb *pUsb) +{ + S_usb_endpoint *pEndpoint; + unsigned char bEndpoint; + + // Reset the transfer descriptor of every endpoint + for (bEndpoint = 0; bEndpoint < pUsb->dNumEndpoints; bEndpoint++) { + + pEndpoint = USB_GetEndpoint(pUsb, bEndpoint); + + // Reset endpoint transfer descriptor + pEndpoint->pData = 0; + pEndpoint->dBytesRemaining = 0; + pEndpoint->dBytesTransferred = 0; + pEndpoint->dBytesBuffered = 0; + pEndpoint->fCallback = 0; + pEndpoint->pArgument = 0; + + // Configure endpoint characteristics + pEndpoint->dState = endpointStateDisabled; + } +} + +//------------------------------------------------------------------------------ +// \brief Disable all endpoints (except control endpoint 0), aborting current +// transfers if necessary. +// \param pUsb Pointer to a S_usb instance +//------------------------------------------------------------------------------ +static void OTGHS_DisableEndpoints(const S_usb *pUsb) +{ + S_usb_endpoint *pEndpoint; + unsigned char bEndpoint; + + // Foreach endpoint, if it is enabled, disable it and invoke the callback + // Control endpoint 0 is not disabled + for (bEndpoint = 1; bEndpoint < pUsb->dNumEndpoints; bEndpoint++) { + + pEndpoint = USB_GetEndpoint(pUsb, bEndpoint); + OTGHS_EndOfTransfer(pEndpoint, USB_STATUS_RESET); + + pEndpoint->dState = endpointStateDisabled; + } +} + +//------------------------------------------------------------------------------ +// \brief Endpoint interrupt handler. +// +// Handle IN/OUT transfers, received SETUP packets and STALLing +// \param pUsb Pointer to a S_usb instance +// \param bEndpoint Index of endpoint +// \see S_usb +//------------------------------------------------------------------------------ +static void OTGHS_EndpointHandler(const S_usb *pUsb, unsigned char bEndpoint) +{ + S_usb_endpoint *pEndpoint = USB_GetEndpoint(pUsb, bEndpoint); + AT91PS_OTGHS pInterface = OTGHS_GetDriverInterface(pUsb); + unsigned int dStatus = pInterface->OTGHS_DEVEPTCSR[bEndpoint]; + unsigned short wPacketSize; + + TRACE_DEBUG_WP("Ept%d, 0x%X ", bEndpoint, dStatus); + + // Handle interrupts + // IN packet sent + if((ISSET(pInterface->OTGHS_DEVEPTCMR[bEndpoint], AT91C_OTGHS_TXINI)) + && (ISSET(dStatus, AT91C_OTGHS_TXINI ))) { + + TRACE_DEBUG_WP("Wr "); + + if (pEndpoint->dBytesBuffered > 0) { + + TRACE_DEBUG_WP("%d ", pEndpoint->dBytesBuffered); + + pEndpoint->dBytesTransferred += pEndpoint->dBytesBuffered; + pEndpoint->dBytesBuffered = 0; + } + + if ((!pEndpoint->isDataSent) || (pEndpoint->dBytesRemaining > 0)) { + + OTGHS_WritePayload(pUsb, bEndpoint); + pEndpoint->isDataSent = true; + + pInterface->OTGHS_DEVEPTCCR[bEndpoint] = AT91C_OTGHS_TXINI; + // For a non-control endpoint, the FIFOCON bit must be cleared + // to start the transfer + if ((AT91C_OTGHS_EPT_TYPE & pInterface->OTGHS_DEVEPTCFG[bEndpoint]) + != AT91C_OTGHS_EPT_TYPE_CTL_EPT) { + + pInterface->OTGHS_DEVEPTCDR[bEndpoint] = AT91C_OTGHS_FIFOCON; + } + } + else { + + pInterface->OTGHS_DEVEPTCDR[bEndpoint] = AT91C_OTGHS_TXINI; + + // Disable interrupt if this is not a control endpoint + if ((AT91C_OTGHS_EPT_TYPE & pInterface->OTGHS_DEVEPTCFG[bEndpoint]) + != AT91C_OTGHS_EPT_TYPE_CTL_EPT) { + + pInterface->OTGHS_DEVIDR = 1<dState != endpointStateRead) { + + // Endpoint is NOT in Read state + if (ISCLEARED(pInterface->OTGHS_DEVEPTCFG[bEndpoint], AT91C_OTGHS_EPT_TYPE) + && ISCLEARED(dStatus, (0x7FF<<20))) { // byte count + + // Control endpoint, 0 bytes received + // Acknowledge the data and finish the current transfer + TRACE_DEBUG_WP("Ack "); + pInterface->OTGHS_DEVEPTCCR[bEndpoint] = AT91C_OTGHS_RXOUT; + + OTGHS_EndOfTransfer(pEndpoint, USB_STATUS_SUCCESS); + } + else if (ISSET(dStatus, AT91C_OTGHS_STALL)) { + + // Non-control endpoint + // Discard stalled data + TRACE_DEBUG_WP("Disc "); + pInterface->OTGHS_DEVEPTCCR[bEndpoint] = AT91C_OTGHS_RXOUT; + } + else { + + // Non-control endpoint + // Nak data + TRACE_DEBUG_WP("Nak "); + pInterface->OTGHS_DEVIDR = 1<> 20) & 0x7FF); + + TRACE_DEBUG_WP("%d ", wPacketSize); + + OTGHS_GetPayload(pUsb, bEndpoint, wPacketSize); + + pInterface->OTGHS_DEVEPTCCR[bEndpoint] = AT91C_OTGHS_RXOUT; + pInterface->OTGHS_DEVEPTCDR[bEndpoint] = AT91C_OTGHS_FIFOCON; + + if ((pEndpoint->dBytesRemaining == 0) + || (wPacketSize < pEndpoint->wMaxPacketSize)) { + + pInterface->OTGHS_DEVEPTCDR[bEndpoint] = AT91C_OTGHS_RXOUT; + + // Disable interrupt if this is not a control endpoint + if ((AT91C_OTGHS_EPT_TYPE & pInterface->OTGHS_DEVEPTCFG[bEndpoint]) + != AT91C_OTGHS_EPT_TYPE_CTL_EPT) { + + pInterface->OTGHS_DEVIDR = 1<dState == endpointStateWrite) + || (pEndpoint->dState == endpointStateRead)) { + + OTGHS_EndOfTransfer(pEndpoint, USB_STATUS_SUCCESS); + } + + // Copy the setup packet in S_usb + OTGHS_GetSetup(pUsb); + + // Acknowledge setup packet + pInterface->OTGHS_DEVEPTCCR[bEndpoint] = AT91C_OTGHS_RXSTP; + + // Forward the request to the upper layer + USB_NewRequestCallback(pUsb); + } + + // STALL sent + if (ISSET(dStatus, AT91C_OTGHS_STALL)) { + + TRACE_WARNING("Sta 0x%X [%d] ", dStatus, bEndpoint); + + // Acknowledge STALL interrupt and disable it + pInterface->OTGHS_DEVEPTCCR[bEndpoint] = AT91C_OTGHS_STALL; + //pInterface->OTGHS_DEVEPTCDR[bEndpoint] = AT91C_OTGHS_STALL; + + // If the endpoint is not halted, clear the stall condition + if (pEndpoint->dState != endpointStateHalted) { + + TRACE_WARNING("_ " ); + // Acknowledge the stall RQ flag + pInterface->OTGHS_DEVEPTCDR[bEndpoint] = AT91C_OTGHS_STALLRQ; + } + + } + +} + + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// \brief Configure an endpoint with the provided endpoint descriptor +// \param pUsb Pointer to a S_usb instance +// \param pEpDesc Pointer to the endpoint descriptor +// \return true if the endpoint is now configured, false otherwise +// \see S_usb_endpoint_descriptor +// \see S_usb +//------------------------------------------------------------------------------ +static bool OTGHS_ConfigureEndpoint(const S_usb *pUsb, + const S_usb_endpoint_descriptor *pEpDesc) +{ + AT91PS_OTGHS pInterface = OTGHS_GetDriverInterface(pUsb); + S_usb_endpoint *pEndpoint; + unsigned char bEndpoint; + unsigned char bType; + unsigned char endpointDir; + unsigned short sizeEpt = 0; + + // Maximum packet size configuration value + if( pEpDesc->wMaxPacketSize == 8 ) { + sizeEpt = AT91C_OTGHS_EPT_SIZE_8; + } else if ( pEpDesc->wMaxPacketSize == 16 ) { + sizeEpt = AT91C_OTGHS_EPT_SIZE_16; + } else if ( pEpDesc->wMaxPacketSize == 32 ) { + sizeEpt = AT91C_OTGHS_EPT_SIZE_32; + } else if ( pEpDesc->wMaxPacketSize == 64 ) { + sizeEpt = AT91C_OTGHS_EPT_SIZE_64; + } else if ( pEpDesc->wMaxPacketSize == 128 ) { + sizeEpt = AT91C_OTGHS_EPT_SIZE_128; + } else if ( pEpDesc->wMaxPacketSize == 256 ) { + sizeEpt = AT91C_OTGHS_EPT_SIZE_256; + } else if ( pEpDesc->wMaxPacketSize == 512 ) { + sizeEpt = AT91C_OTGHS_EPT_SIZE_512; + } else if ( pEpDesc->wMaxPacketSize == 1024 ) { + sizeEpt = AT91C_OTGHS_EPT_SIZE_1024; + } //else { + // sizeEpt = 0; // control endpoint + //} + + // if pEpDesc == 0 then initialize the control endpoint + if (pEpDesc == (S_usb_endpoint_descriptor const *) 0) { + + bEndpoint = 0; + bType = 0; // Control endpoint + } + else { + // The endpoint number + bEndpoint = (unsigned char) (pEpDesc->bEndpointAddress & 0x7); + // Transfer type: Control, Isochronous, Bulk, Interrupt + bType = (unsigned char) (pEpDesc->bmAttributes & 0x3); + // Direction, ignored for control endpoints + endpointDir = (unsigned char) (pEpDesc->bEndpointAddress & (1<<7)); + } + + // Get pointer on endpoint + pEndpoint = USB_GetEndpoint(pUsb, bEndpoint); + if (pEndpoint == 0) { + + return false; + } + + // Configure wMaxPacketSize + if (pEpDesc != 0) { + + pEndpoint->wMaxPacketSize = pEpDesc->wMaxPacketSize; + } + else { + + pEndpoint->wMaxPacketSize = USB_ENDPOINT0_MAXPACKETSIZE; + } + + // Abort the current transfer is the endpoint was configured and in + // Write or Read state + if ((pEndpoint->dState == endpointStateRead) + || (pEndpoint->dState == endpointStateWrite)) { + + OTGHS_EndOfTransfer(pEndpoint, USB_STATUS_RESET); + } + + // Enter in IDLE state + pEndpoint->dState = endpointStateIdle; + + // Reset Endpoint Fifos + pInterface->OTGHS_DEVEPT |= (1<OTGHS_DEVEPT &= ~(1<OTGHS_DEVEPT |= (1<OTGHS_DEVEPTCFG[bEndpoint] = AT91C_OTGHS_ALLOC | + AT91C_OTGHS_EPT_SIZE_64 | AT91C_OTGHS_EPT_DIR_OUT | AT91C_OTGHS_EPT_TYPE_CTL_EPT | AT91C_OTGHS_BK_NUMBER_1; + + // Enable RXSTP interrupt + pInterface->OTGHS_DEVEPTCER[bEndpoint] = AT91C_OTGHS_RXSTP; + + // Enable endpoint IT + pInterface->OTGHS_DEVIER = 1<OTGHS_DEVEPTCFG[bEndpoint] = AT91C_OTGHS_ALLOC | + sizeEpt | AT91C_OTGHS_EPT_DIR_IN | AT91C_OTGHS_EPT_TYPE_ISO_EPT | AT91C_OTGHS_BK_NUMBER_2; +#else + pInterface->OTGHS_DEVEPTCFG[bEndpoint] = AT91C_OTGHS_ALLOC | AT91C_OTGHS_AUTOSW | + sizeEpt | AT91C_OTGHS_EPT_DIR_IN | AT91C_OTGHS_EPT_TYPE_ISO_EPT | AT91C_OTGHS_BK_NUMBER_2; +#endif + + } + else { + TRACE_INFO("Iso Out[%d]\n\r",bEndpoint); + + //! Configure endpoint +#ifndef DMA + pInterface->OTGHS_DEVEPTCFG[bEndpoint] = AT91C_OTGHS_ALLOC | + sizeEpt | AT91C_OTGHS_EPT_DIR_OUT | AT91C_OTGHS_EPT_TYPE_ISO_EPT | AT91C_OTGHS_BK_NUMBER_2; +#else + pInterface->OTGHS_DEVEPTCFG[bEndpoint] = AT91C_OTGHS_ALLOC | AT91C_OTGHS_AUTOSW | + sizeEpt | AT91C_OTGHS_EPT_DIR_OUT | AT91C_OTGHS_EPT_TYPE_ISO_EPT | AT91C_OTGHS_BK_NUMBER_2; +#endif + + } + break; + + //---------------------- + case ENDPOINT_TYPE_BULK: + //---------------------- + if (endpointDir) { + TRACE_INFO("Bulk In(%d)[%d] ",bEndpoint, pEpDesc->wMaxPacketSize); + //! Configure endpoint +#ifndef DMA + pInterface->OTGHS_DEVEPTCFG[bEndpoint] = AT91C_OTGHS_ALLOC | + sizeEpt | AT91C_OTGHS_EPT_DIR_IN | AT91C_OTGHS_EPT_TYPE_BUL_EPT | AT91C_OTGHS_BK_NUMBER_2; +#else + pInterface->OTGHS_DEVEPTCFG[bEndpoint] = AT91C_OTGHS_ALLOC | AT91C_OTGHS_AUTOSW | + sizeEpt | AT91C_OTGHS_EPT_DIR_IN | AT91C_OTGHS_EPT_TYPE_BUL_EPT | AT91C_OTGHS_BK_NUMBER_2; +#endif + + } + else { + TRACE_INFO("Bulk Out(%d)[%d]\n\r",bEndpoint, pEpDesc->wMaxPacketSize); + //! Configure endpoint +#ifndef DMA + pInterface->OTGHS_DEVEPTCFG[bEndpoint] = AT91C_OTGHS_ALLOC | + sizeEpt | AT91C_OTGHS_EPT_DIR_OUT | AT91C_OTGHS_EPT_TYPE_BUL_EPT | AT91C_OTGHS_BK_NUMBER_2; +#else + pInterface->OTGHS_DEVEPTCFG[bEndpoint] = AT91C_OTGHS_ALLOC | AT91C_OTGHS_AUTOSW | + sizeEpt | AT91C_OTGHS_EPT_DIR_OUT | AT91C_OTGHS_EPT_TYPE_BUL_EPT | AT91C_OTGHS_BK_NUMBER_2; +#endif + } + break; + + //--------------------------- + case ENDPOINT_TYPE_INTERRUPT: + //--------------------------- + if (endpointDir) { + TRACE_INFO("Interrupt In[%d]\n\r",bEndpoint); + //! Configure endpoint +#ifndef DMA + pInterface->OTGHS_DEVEPTCFG[bEndpoint] = AT91C_OTGHS_ALLOC | + sizeEpt | AT91C_OTGHS_EPT_DIR_IN | AT91C_OTGHS_EPT_TYPE_INT_EPT | AT91C_OTGHS_BK_NUMBER_2; +#else + pInterface->OTGHS_DEVEPTCFG[bEndpoint] = AT91C_OTGHS_ALLOC | AT91C_OTGHS_AUTOSW | + sizeEpt | AT91C_OTGHS_EPT_DIR_IN | AT91C_OTGHS_EPT_TYPE_INT_EPT | AT91C_OTGHS_BK_NUMBER_2; +#endif + + } + else { + TRACE_INFO("Interrupt Out[%d]\n\r",bEndpoint); + //! Configure endpoint +#ifndef DMA + pInterface->OTGHS_DEVEPTCFG[bEndpoint] = AT91C_OTGHS_ALLOC | + sizeEpt | AT91C_OTGHS_EPT_DIR_OUT | AT91C_OTGHS_EPT_TYPE_INT_EPT | AT91C_OTGHS_BK_NUMBER_2; +#else + pInterface->OTGHS_DEVEPTCFG[bEndpoint] = AT91C_OTGHS_ALLOC | AT91C_OTGHS_AUTOSW | + sizeEpt | AT91C_OTGHS_EPT_DIR_OUT | AT91C_OTGHS_EPT_TYPE_INT_EPT | AT91C_OTGHS_BK_NUMBER_2; +#endif + + } + break; + + //------ + default: + //------ + TRACE_ERROR(" unknown endpoint type\n\r"); + return false; + } + + // Check if the configuration is ok + if (ISCLEARED(pInterface->OTGHS_DEVEPTCSR[bEndpoint], AT91C_OTGHS_CFGOK)) { + + TRACE_FATAL("OTGHS_ConfigureEndpoint: Cannot configure endpoint\n\r"); + return false; + } + + return true; +} + + +//------------------------------------------------------------------------------ +// Interrupt service routine +//------------------------------------------------------------------------------ +#ifdef DMA +//---------------------------------------------------------------------------- +//! \fn OTGHS_DmaHandler +//! \brief This function (ISR) handles DMA interrupts +//---------------------------------------------------------------------------- +static void OTGHS_DmaHandler(const S_usb *pUsb, unsigned char endpoint) +{ + AT91PS_OTGHS pInterface = OTGHS_GetDriverInterface(pUsb); + S_usb_endpoint *pEndpoint = USB_GetEndpoint(pUsb, endpoint); + unsigned int csr; + + csr = pInterface->OTGHS_DEVDMA[endpoint].OTGHS_DEVDMASTATUS; + pInterface->OTGHS_DEVIDR = (1<dBytesTransferred = pEndpoint->dBytesBuffered; + pEndpoint->dBytesBuffered = 0; + + TRACE_DEBUG_M("dBytesBuffered: 0x%x\n\r",pEndpoint->dBytesBuffered); + TRACE_DEBUG_M("dBytesRemaining: 0x%x\n\r",pEndpoint->dBytesRemaining); + TRACE_DEBUG_M("dBytesTransferred: 0x%x\n\r",pEndpoint->dBytesTransferred); + + OTGHS_EndOfTransfer(pEndpoint, USB_STATUS_SUCCESS); + pEndpoint->dState = endpointStateIdle; + } + else { + TRACE_FATAL("Probleme IT DMA\n\r"); + } +} +#endif + + +//------------------------------------------------------------------------------ +// \brief OTGHS interrupt handler +// +// Manages device resume, suspend, end of bus reset. Forwards endpoint +// interrupts to the appropriate handler. +// \param pUsb Pointer to a S_usb instance +//------------------------------------------------------------------------------ +static void OTGHS_Handler(const S_usb *pUsb) +{ + AT91PS_OTGHS pInterface = OTGHS_GetDriverInterface(pUsb); + unsigned int dStatus; + unsigned char numIT; + + if ( (!ISSET(USB_GetState(pUsb), USB_STATE_SUSPENDED)) + && (ISSET(USB_GetState(pUsb), USB_STATE_POWERED))){ + + LED_TOGGLE(LED_USB); + } + + TRACE_DEBUG_H("Hlr "); + + // Get General interrupts status + dStatus = pInterface->OTGHS_SR & pInterface->OTGHS_CTRL & 0xFF; + while (dStatus != 0) { + + if(ISSET(dStatus, AT91C_OTGHS_VBUSTI)) + { + TRACE_DEBUG_M("__VBus\n\r"); + + USB_Attach(pUsb); + + // Acknowledge the interrupt + pInterface->OTGHS_SCR = AT91C_OTGHS_VBUSTI; + } + + // Don't treat others interrupt for this time + pInterface->OTGHS_SCR = AT91C_OTGHS_IDT | AT91C_OTGHS_SRP + | AT91C_OTGHS_VBERR | AT91C_OTGHS_BCERR + | AT91C_OTGHS_ROLEEX | AT91C_OTGHS_HNPERR + | AT91C_OTGHS_STO; + + dStatus = pInterface->OTGHS_SR & pInterface->OTGHS_CTRL & 0xFF; + } + + + // Get OTG Device interrupts status + dStatus = pInterface->OTGHS_DEVISR & pInterface->OTGHS_DEVIMR; + TRACE_DEBUG_H("OTGHS_DEVISR:0x%X\n\r", pInterface->OTGHS_DEVISR); + while (dStatus != 0) { + + // Start Of Frame (SOF) + if (ISSET(dStatus, AT91C_OTGHS_SOF)) { + TRACE_DEBUG_WP("SOF "); + + // Invoke the SOF callback + USB_StartOfFrameCallback(pUsb); + + // Acknowledge interrupt + SET(pInterface->OTGHS_DEVICR, AT91C_OTGHS_SOF); + CLEAR(dStatus, AT91C_OTGHS_SOF); + } + + // Suspend + else if (dStatus & AT91C_OTGHS_SUSP) { + + TRACE_DEBUG_M("S "); + + if (!ISSET(USB_GetState(pUsb), USB_STATE_SUSPENDED)) { + + // The device enters the Suspended state + // MCK + UDPCK must be off + // Pull-Up must be connected + // Transceiver must be disabled + + // Enable wakeup + SET(pInterface->OTGHS_DEVIER, AT91C_OTGHS_EORST | AT91C_OTGHS_WAKEUP | AT91C_OTGHS_EORSM); + + // Acknowledge interrupt + pInterface->OTGHS_DEVICR = AT91C_OTGHS_SUSP; + SET(*(pUsb->pState), USB_STATE_SUSPENDED); + OTGHS_DisableTransceiver(pUsb); + OTGHS_DisableMCK(pUsb); + OTGHS_DisableOTGHSCK(pUsb); + + // Invoke the Suspend callback + + USB_SuspendCallback(pUsb); + } + } + + // Resume + else if (ISSET(dStatus, AT91C_OTGHS_WAKEUP) + || ISSET(dStatus, AT91C_OTGHS_EORSM)) { + + // Invoke the Resume callback + USB_ResumeCallback(pUsb); + + TRACE_DEBUG_M("R "); + + // The device enters Configured state + // MCK + UDPCK must be on + // Pull-Up must be connected + // Transceiver must be enabled + + if (ISSET(USB_GetState(pUsb), USB_STATE_SUSPENDED)) { + + // Powered state + OTGHS_EnableMCK(pUsb); + OTGHS_EnableOTGHSCK(pUsb); + + // Default state + if (ISSET(USB_GetState(pUsb), USB_STATE_DEFAULT)) { + + OTGHS_EnableTransceiver(pUsb); + } + + CLEAR(*(pUsb->pState), USB_STATE_SUSPENDED); + } + pInterface->OTGHS_DEVICR = + (AT91C_OTGHS_WAKEUP | AT91C_OTGHS_EORSM | AT91C_OTGHS_SUSP); + + pInterface->OTGHS_DEVIER = (AT91C_OTGHS_EORST | AT91C_OTGHS_SUSP); + pInterface->OTGHS_DEVICR = (AT91C_OTGHS_WAKEUP | AT91C_OTGHS_EORSM); + pInterface->OTGHS_DEVIDR = AT91C_OTGHS_WAKEUP; + + } + + // End of bus reset + else if (dStatus & AT91C_OTGHS_EORST) { + + TRACE_DEBUG_M("EoB "); + // The device enters the Default state + // MCK + UDPCK are already enabled + // Pull-Up is already connected + // Transceiver must be enabled + // Endpoint 0 must be enabled + SET(*(pUsb->pState), USB_STATE_DEFAULT); + + OTGHS_EnableTransceiver(pUsb); + + // The device leaves the Address & Configured states + CLEAR(*(pUsb->pState), USB_STATE_ADDRESS | USB_STATE_CONFIGURED); + OTGHS_ResetEndpoints(pUsb); + OTGHS_DisableEndpoints(pUsb); + OTGHS_ConfigureEndpoint(pUsb, 0); + + // Flush and enable the Suspend interrupt + SET(pInterface->OTGHS_DEVICR, AT91C_OTGHS_WAKEUP | AT91C_OTGHS_SUSP); + + // Enable the Start Of Frame (SOF) interrupt if needed + if (pUsb->pCallbacks->startOfFrame != 0) { + + SET(pInterface->OTGHS_DEVIER, AT91C_OTGHS_SOF); + } + + // Invoke the Reset callback + USB_ResetCallback(pUsb); + + // Acknowledge end of bus reset interrupt + pInterface->OTGHS_DEVICR = AT91C_OTGHS_EORST; + } + + // Handle upstream resume interrupt + else if (dStatus & AT91C_OTGHS_UPRSM) { + + TRACE_DEBUG_WP(" External resume interrupt\n\r"); + + // - Acknowledge the IT + pInterface->OTGHS_DEVICR = AT91C_OTGHS_UPRSM; + } + + // Endpoint interrupts + else { +#ifndef DMA + // Handle endpoint interrupts + for (numIT = 0; numIT < NUM_IT_MAX; numIT++) { + if( dStatus & (1<OTGHS_DEVISR) & (pInterface->OTGHS_DEVIMR); + + // Mask unneeded interrupts + if (!ISSET(USB_GetState(pUsb), USB_STATE_DEFAULT)) { + + dStatus &= AT91C_OTGHS_EORST | AT91C_OTGHS_SOF; + } + + TRACE_DEBUG_H("\n\r"); + + if (dStatus != 0) { + + TRACE_DEBUG_WP(" - "); + } + } + + if ( (!ISSET(USB_GetState(pUsb), USB_STATE_SUSPENDED)) + && (ISSET(USB_GetState(pUsb), USB_STATE_POWERED))){ + + LED_TOGGLE(LED_USB); + } +} + +//------------------------------------------------------------------------------ +// \brief Sends data through an USB endpoint +// +// Sets up the transfer descriptor, write one or two data payloads +// (depending on the number of FIFO banks for the endpoint) and then +// starts the actual transfer. The operation is complete when all +// the data has been sent. +// \param pUsb Pointer to a S_usb instance +// \param bEndpoint Index of endpoint +// \param pData Pointer to a buffer containing the data to send +// \param dLength Length of the data buffer +// \param fCallback Optional function to invoke when the transfer finishes +// \param pArgument Optional argument for the callback function +// \return Operation result code +// \see Operation result codes +// \see Callback_f +// \see S_usb +//------------------------------------------------------------------------------ +static char OTGHS_Write(const S_usb *pUsb, + unsigned char bEndpoint, + const void *pData, + unsigned int dLength, + Callback_f fCallback, + void *pArgument) +{ + S_usb_endpoint *pEndpoint = USB_GetEndpoint(pUsb, bEndpoint); + AT91PS_OTGHS pInterface = OTGHS_GetDriverInterface(pUsb); + + // Check that the endpoint is in Idle state + if (pEndpoint->dState != endpointStateIdle) { + + return USB_STATUS_LOCKED; + } + + TRACE_DEBUG_WP("Write%d(%d) ", bEndpoint, dLength); + + // Setup the transfer descriptor + pEndpoint->pData = (char *) pData; + pEndpoint->dBytesRemaining = dLength; + pEndpoint->dBytesBuffered = 0; + pEndpoint->dBytesTransferred = 0; + pEndpoint->fCallback = fCallback; + pEndpoint->pArgument = pArgument; + pEndpoint->isDataSent = false; + + // Send one packet + pEndpoint->dState = endpointStateWrite; + +#ifdef DMA + // Test if endpoint type control + if (AT91C_OTGHS_EPT_TYPE_CTL_EPT == (AT91C_OTGHS_EPT_TYPE & pInterface->OTGHS_DEVEPTCFG[bEndpoint])) { +#endif + // Enable endpoint IT + pInterface->OTGHS_DEVIER = (1<OTGHS_DEVEPTCER[bEndpoint] = AT91C_OTGHS_TXINI; + +#ifdef DMA + } + else { + + // others endoint (not control) + pEndpoint->dBytesBuffered = pEndpoint->dBytesRemaining; + pEndpoint->dBytesRemaining = 0; + + pInterface->OTGHS_DEVDMA[bEndpoint].OTGHS_DEVDMAADDRESS = (unsigned int) pEndpoint->pData; + + // Enable IT DMA + pInterface->OTGHS_DEVIER = (1<OTGHS_DEVDMA[bEndpoint].OTGHS_DEVDMACONTROL = + (((pEndpoint->dBytesBuffered<<16)&AT91C_OTGHS_BUFF_LENGTH) + | AT91C_OTGHS_END_B_EN + | AT91C_OTGHS_END_BUFFIT + | AT91C_OTGHS_CHANN_ENB); + + } +#endif + + return USB_STATUS_SUCCESS; +} + +//------------------------------------------------------------------------------ +// \brief Reads incoming data on an USB endpoint +// +// This methods sets the transfer descriptor and activate the endpoint +// interrupt. The actual transfer is then carried out by the endpoint +// interrupt handler. The Read operation finishes either when the +// buffer is full, or a short packet (inferior to endpoint maximum +// packet size) is received. +// \param pUsb Pointer to a S_usb instance +// \param bEndpoint Index of endpoint +// \param pData Pointer to a buffer to store the received data +// \param dLength Length of the receive buffer +// \param fCallback Optional callback function +// \param pArgument Optional callback argument +// \return Operation result code +// \see Callback_f +// \see S_usb +//------------------------------------------------------------------------------ +static char OTGHS_Read(const S_usb *pUsb, + unsigned char bEndpoint, + void *pData, + unsigned int dLength, + Callback_f fCallback, + void *pArgument) +{ + AT91PS_OTGHS pInterface = OTGHS_GetDriverInterface(pUsb); + S_usb_endpoint *pEndpoint = USB_GetEndpoint(pUsb, bEndpoint); + + //! Return if the endpoint is not in IDLE state + if (pEndpoint->dState != endpointStateIdle) { + + return USB_STATUS_LOCKED; + } + + TRACE_DEBUG_M("Read%d(%d) ", bEndpoint, dLength); + + // Endpoint enters Read state + pEndpoint->dState = endpointStateRead; + + //! Set the transfer descriptor + pEndpoint->pData = (char *) pData; + pEndpoint->dBytesRemaining = dLength; + pEndpoint->dBytesBuffered = 0; + pEndpoint->dBytesTransferred = 0; + pEndpoint->fCallback = fCallback; + pEndpoint->pArgument = pArgument; + +#ifdef DMA + // Test if endpoint type control + if (AT91C_OTGHS_EPT_TYPE_CTL_EPT == (AT91C_OTGHS_EPT_TYPE & pInterface->OTGHS_DEVEPTCFG[bEndpoint])) { +#endif + // Control endpoint + // Enable endpoint IT + pInterface->OTGHS_DEVIER = (1<OTGHS_DEVEPTCER[bEndpoint] = AT91C_OTGHS_RXOUT; +#ifdef DMA + } + else { + + // others endoint (not control) + pEndpoint->dBytesBuffered = pEndpoint->dBytesRemaining; + pEndpoint->dBytesRemaining = 0; + + // Enable IT DMA + pInterface->OTGHS_DEVIER = (1<OTGHS_DEVDMA[bEndpoint].OTGHS_DEVDMAADDRESS = (unsigned int) pEndpoint->pData; + + pInterface->OTGHS_DEVDMA[bEndpoint].OTGHS_DEVDMACONTROL = \ + ( (pEndpoint->dBytesBuffered<<16) + | AT91C_OTGHS_END_TR_EN + | AT91C_OTGHS_END_TR_IT + | AT91C_OTGHS_END_B_EN + | AT91C_OTGHS_END_BUFFIT + | AT91C_OTGHS_CHANN_ENB); + } +#endif + + return USB_STATUS_SUCCESS; +} + +//------------------------------------------------------------------------------ +// \brief Clears, sets or returns the Halt state on specified endpoint +// +// When in Halt state, an endpoint acknowledges every received packet +// with a STALL handshake. This continues until the endpoint is +// manually put out of the Halt state by calling this function. +// \param pUsb Pointer to a S_usb instance +// \param bEndpoint Index of endpoint +// \param bRequest Request to perform +// -> USB_SET_FEATURE, USB_CLEAR_FEATURE, USB_GET_STATUS +// \return true if the endpoint is currently Halted, false otherwise +// \see S_usb +//------------------------------------------------------------------------------ +static bool OTGHS_Halt(const S_usb *pUsb, + unsigned char bEndpoint, + unsigned char bRequest) +{ + AT91PS_OTGHS pInterface = OTGHS_GetDriverInterface(pUsb); + S_usb_endpoint *pEndpoint = USB_GetEndpoint(pUsb, bEndpoint); + + // Clear the Halt feature of the endpoint if it is enabled + if (bRequest == USB_CLEAR_FEATURE) { + + TRACE_DEBUG_WP("Unhalt%d ", bEndpoint); + + // Return endpoint to Idle state + pEndpoint->dState = endpointStateIdle; + + // Clear FORCESTALL flag + + // Disable stall on endpoint + pInterface->OTGHS_DEVEPTCDR[bEndpoint] = AT91C_OTGHS_STALLRQ; + pEndpoint->dState = endpointStateIdle; + + // Reset data-toggle + pInterface->OTGHS_DEVEPTCER[bEndpoint] = AT91C_OTGHS_RSTDT; + } + // Set the Halt feature on the endpoint if it is not already enabled + // and the endpoint is not disabled + else if ((bRequest == USB_SET_FEATURE) + && (pEndpoint->dState != endpointStateHalted) + && (pEndpoint->dState != endpointStateDisabled)) { + + TRACE_DEBUG_WP("Halt%d ", bEndpoint); + + // Abort the current transfer if necessary + OTGHS_EndOfTransfer(pEndpoint, USB_STATUS_ABORTED); + + // Put endpoint into Halt state + pInterface->OTGHS_DEVEPTCER[bEndpoint] = AT91C_OTGHS_STALLRQ; + pEndpoint->dState = endpointStateHalted; + + // Enable the endpoint interrupt + pInterface->OTGHS_DEVIER = (1<dState == endpointStateHalted) { + + return true; + } + else { + + return false; + } +} + +//------------------------------------------------------------------------------ +// \brief Causes the endpoint to acknowledge the next received packet with +// a STALL handshake. +// +// Further packets are then handled normally. +// \param pUsb Pointer to a S_usb instance +// \param bEndpoint Index of endpoint +// \return Operation result code +// \see S_usb +//------------------------------------------------------------------------------ +static char OTGHS_Stall(const S_usb *pUsb, + unsigned char bEndpoint) +{ + AT91PS_OTGHS pInterface = OTGHS_GetDriverInterface(pUsb); + S_usb_endpoint *pEndpoint = USB_GetEndpoint(pUsb, bEndpoint); + + // Check that endpoint is in Idle state + if (pEndpoint->dState != endpointStateIdle) { + + TRACE_WARNING("UDP_Stall: Endpoint%d locked\n\r", bEndpoint); + return USB_STATUS_LOCKED; + } + + TRACE_DEBUG_WP("Stall%d ", bEndpoint); + + pInterface->OTGHS_DEVEPTCER[bEndpoint] = AT91C_OTGHS_STALL; + pInterface->OTGHS_DEVEPTCER[bEndpoint] = AT91C_OTGHS_STALLRQ; + + return USB_STATUS_SUCCESS; +} + +//------------------------------------------------------------------------------ +// \brief Activates a remote wakeup procedure +// \param pUsb Pointer to a S_usb instance +// \see S_usb +//------------------------------------------------------------------------------ +static void OTGHS_RemoteWakeUp(const S_usb *pUsb) +{ + AT91PS_OTGHS pInterface = OTGHS_GetDriverInterface(pUsb); + + OTGHS_EnableMCK(pUsb); + OTGHS_EnableOTGHSCK(pUsb); + OTGHS_EnableTransceiver(pUsb); + + TRACE_DEBUG_WP("Remote WakeUp "); + + //! Enable wakeup interrupt + //pInterface->OTGHS_DEVIER = AT91C_OTGHS_UPRSM; + + // Activates a remote wakeup + pInterface->OTGHS_DEVCTRL |= AT91C_OTGHS_RMWKUP; +} + +//------------------------------------------------------------------------------ +// \brief Handles attachment or detachment from the USB when the VBus power +// line status changes. +// \param pUsb Pointer to a S_usb instance +// \return true if VBus is present, false otherwise +// \see S_usb +//------------------------------------------------------------------------------ +static bool OTGHS_Attach(const S_usb *pUsb) +{ + AT91PS_OTGHS pInterface = OTGHS_GetDriverInterface(pUsb); + + TRACE_DEBUG_WP("Attach("); + + // Check if VBus is present + if (!ISSET(USB_GetState(pUsb), USB_STATE_POWERED) + && BRD_IsVBusConnected(pInterface)) { + + // Powered state: + // MCK + UDPCK must be on + // Pull-Up must be connected + // Transceiver must be disabled + + // Invoke the Resume callback + USB_ResumeCallback(pUsb); + + OTGHS_EnableMCK(pUsb); + OTGHS_EnableOTGHSCK(pUsb); + + // Enable the transceiver + OTGHS_EnableTransceiver(pUsb); + + // Reconnect the pull-up if needed + if (ISSET(*(pUsb->pState), USB_STATE_SHOULD_RECONNECT)) { + + USB_Connect(pUsb); + CLEAR(*(pUsb->pState), USB_STATE_SHOULD_RECONNECT); + } + + // Clear the Suspend and Resume interrupts + pInterface->OTGHS_DEVICR = \ + AT91C_OTGHS_WAKEUP | AT91C_OTGHS_EORSM | AT91C_OTGHS_SUSP; + + // Enable interrupt + pInterface->OTGHS_DEVIER = AT91C_OTGHS_EORST | AT91C_OTGHS_WAKEUP | AT91C_OTGHS_EORSM; + + // The device is in Powered state + SET(*(pUsb->pState), USB_STATE_POWERED); + + } + else if (ISSET(USB_GetState(pUsb), USB_STATE_POWERED) + && !BRD_IsVBusConnected(pInterface)) { + + // Attached state: + // MCK + UDPCK off + // Pull-Up must be disconnected + // Transceiver must be disabled + + // Warning: MCK must be enabled to be able to write in UDP registers + // It may have been disabled by the Suspend interrupt, so re-enable it + OTGHS_EnableMCK(pUsb); + + // Disable interrupts + pInterface->OTGHS_DEVIDR &= ~(AT91C_OTGHS_WAKEUP | AT91C_OTGHS_EORSM + | AT91C_OTGHS_SUSP | AT91C_OTGHS_SOF); + + OTGHS_DisableEndpoints(pUsb); + + // Disconnect the pull-up if needed + if (ISSET(USB_GetState(pUsb), USB_STATE_DEFAULT)) { + + USB_Disconnect(pUsb); + SET(*(pUsb->pState), USB_STATE_SHOULD_RECONNECT); + } + + OTGHS_DisableTransceiver(pUsb); + OTGHS_DisableMCK(pUsb); + OTGHS_DisableOTGHSCK(pUsb); + + // The device leaves the all states except Attached + CLEAR(*(pUsb->pState), USB_STATE_POWERED | USB_STATE_DEFAULT + | USB_STATE_ADDRESS | USB_STATE_CONFIGURED | USB_STATE_SUSPENDED); + + // Invoke the Suspend callback + USB_SuspendCallback(pUsb); + + } + + TRACE_DEBUG_WP("%d) ", ISSET(USB_GetState(pUsb), USB_STATE_POWERED)); + + return ISSET(USB_GetState(pUsb), USB_STATE_POWERED); +} + +//------------------------------------------------------------------------------ +// \brief Sets the device address +// +// This function directly accesses the S_usb_request instance located +// in the S_usb structure to extract its new address. +// \param pUsb Pointer to a S_usb instance +// \see S_usb +//------------------------------------------------------------------------------ +static void OTGHS_SetAddress(S_usb const *pUsb) +{ + unsigned short wAddress = USB_GetSetup(pUsb)->wValue; + AT91PS_OTGHS pInterface = OTGHS_GetDriverInterface(pUsb); + + TRACE_DEBUG_WP("SetAddr(%d) ", wAddress); + + // Set address + pInterface->OTGHS_DEVCTRL = wAddress & AT91C_OTGHS_UADD; + pInterface->OTGHS_DEVCTRL |= AT91C_OTGHS_ADDEN; + +} + +//------------------------------------------------------------------------------ +// \brief Changes the device state from Address to Configured, or from +// Configured to Address. +// +// This method directly access the last received SETUP packet to +// decide on what to do. +// \see S_usb +//------------------------------------------------------------------------------ +static void OTGHS_SetConfiguration(S_usb const *pUsb) +{ + unsigned short wValue = USB_GetSetup(pUsb)->wValue; + + TRACE_DEBUG_WP("SetCfg() "); + + // Check the request + if (wValue != 0) { + // Enter Configured state + SET(*(pUsb->pState), USB_STATE_CONFIGURED); + + } + else { + + // Go back to Address state + CLEAR(*(pUsb->pState), USB_STATE_CONFIGURED); + + // Abort all transfers + OTGHS_DisableEndpoints(pUsb); + } +} + +//------------------------------------------------------------------------------ +// \brief Enables the pull-up on the D+ line to connect the device to the USB. +// \param pUsb Pointer to a S_usb instance +// \see S_usb +//------------------------------------------------------------------------------ +static void OTGHS_Connect(const S_usb *pUsb) +{ +#if defined(INTERNAL_PULLUP) + CLEAR(OTGHS_GetDriverInterface(pUsb)->OTGHS_DEVCTRL, AT91C_OTGHS_DETACH); + +#elif defined(INTERNAL_PULLUP_MATRIX) + TRACE_DEBUG_WP("PUON 1\n\r"); + AT91C_BASE_MATRIX->MATRIX_USBPCR |= AT91C_MATRIX_USBPCR_PUON; + +#else + BRD_ConnectPullUp(UDP_GetDriverInterface(pUsb)); + +#endif +} + +//------------------------------------------------------------------------------ +// \brief Disables the pull-up on the D+ line to disconnect the device from +// the bus. +// \param pUsb Pointer to a S_usb instance +// \see S_usb +//------------------------------------------------------------------------------ +static void OTGHS_Disconnect(const S_usb *pUsb) +{ +#if defined(INTERNAL_PULLUP) + SET(OTGHS_GetDriverInterface(pUsb)->OTGHS_DEVCTRL, AT91C_OTGHS_DETACH); + +#elif defined(INTERNAL_PULLUP_MATRIX) + TRACE_DEBUG_WP("PUON 0\n\r"); + AT91C_BASE_MATRIX->MATRIX_USBPCR &= ~AT91C_MATRIX_USBPCR_PUON; + +#else + BRD_DisconnectPullUp(UDP_GetDriverInterface(pUsb)); + +#endif + // Device leaves the Default state + CLEAR(*(pUsb->pState), USB_STATE_DEFAULT); +} + +//------------------------------------------------------------------------------ +// \brief Certification test for High Speed device. +// \param pUsb Pointer to a S_usb instance +// \param bIndex char for the test choice +// \see S_usb +//------------------------------------------------------------------------------ +static void OTGHS_Test(const S_usb *pUsb, unsigned char bIndex) +{ + AT91PS_OTGHS pInterface = OTGHS_GetDriverInterface(pUsb); + + pInterface->OTGHS_DEVIDR &= ~AT91C_OTGHS_SUSP; + pInterface->OTGHS_DEVCTRL |= AT91C_OTGHS_SPDCONF_HS; // remove suspend ? + + switch( bIndex ) { + case TEST_PACKET: + TRACE_DEBUG_M("TEST_PACKET "); + pInterface->OTGHS_DEVCTRL |= AT91C_OTGHS_TSTPCKT; + break; + + case TEST_J: + TRACE_DEBUG_M("TEST_J "); + pInterface->OTGHS_DEVCTRL |= AT91C_OTGHS_TSTJ; + break; + + case TEST_K: + TRACE_DEBUG_M("TEST_K "); + pInterface->OTGHS_DEVCTRL |= AT91C_OTGHS_TSTK; + break; + + case TEST_SEO_NAK: + TRACE_DEBUG_M("TEST_SEO_NAK "); + pInterface->OTGHS_DEVIDR = 0xFFFFFFFF; + break; + + case TEST_SEND_ZLP: + pInterface->OTGHS_DEVEPTCCR[0] = AT91C_OTGHS_TXINI; + TRACE_DEBUG_M("SEND_ZLP "); + break; + + TRACE_DEBUG_M("\n\r"); + } +} + +//------------------------------------------------------------------------------ +// \brief Certification test for High Speed device. +// \param pUsb Pointer to a S_usb instance +// \see S_usb +//------------------------------------------------------------------------------ +static bool OTGHS_IsHighSpeed(const S_usb *pUsb) +{ + AT91PS_OTGHS pInterface = OTGHS_GetDriverInterface(pUsb); + bool status = false; + + if(AT91C_OTGHS_SPEED_SR_HS == (pInterface->OTGHS_SR & (0x03<<12))) { + // High Speed + status = true; + } + + return status; +} + +//------------------------------------------------------------------------------ +// \brief Initializes the specified USB driver +// +// This function initializes the current FIFO bank of endpoints, +// configures the pull-up and VBus lines, disconnects the pull-up and +// then trigger the Init callback. +// \param pUsb Pointer to a S_usb instance +// \see S_usb +//------------------------------------------------------------------------------ +static void OTGHS_Init(const S_usb *pUsb) +{ + AT91PS_OTGHS pInterface = OTGHS_GetDriverInterface(pUsb); + unsigned char i; + + TRACE_DEBUG_WP("Init()\n\r"); + + // Enable USB macro + SET(OTGHS_GetDriverInterface(pUsb)->OTGHS_CTRL, AT91C_OTGHS_USBECTRL); + + pInterface->OTGHS_DEVCTRL &=~ AT91C_OTGHS_DETACH; // detach + + //// Force FS (for debug or test) +// pDriver->OTGHS_DEVCTRL |= AT91C_OTGHS_SPDCONF_FS; + pInterface->OTGHS_DEVCTRL &= ~AT91C_OTGHS_SPDCONF_FS; // Normal mode + pInterface->OTGHS_DEVCTRL &= ~( AT91C_OTGHS_LS | AT91C_OTGHS_TSTJ + | AT91C_OTGHS_TSTK | AT91C_OTGHS_TSTPCKT + | AT91C_OTGHS_OPMODE2 ); // Normal mode + + + // With OR without DMA !!! + // Initialization of DMA + for( i=1; i<=((AT91C_BASE_OTGHS->OTGHS_IPFEATURES & AT91C_OTGHS_DMA_CHANNEL_NBR)>>4); i++ ) { + + // RESET endpoint canal DMA: + // DMA stop channel command + AT91C_BASE_OTGHS->OTGHS_DEVDMA[i].OTGHS_DEVDMACONTROL = 0; // STOP command + + // Disable endpoint + AT91C_BASE_OTGHS->OTGHS_DEVEPTCDR[i] = 0XFFFFFFFF; + + // Reset endpoint config + AT91C_BASE_OTGHS->OTGHS_DEVEPTCFG[i] = 0; + + // Reset DMA channel (Buff count and Control field) + AT91C_BASE_OTGHS->OTGHS_DEVDMA[i].OTGHS_DEVDMACONTROL = 0x02; // NON STOP command + + // Reset DMA channel 0 (STOP) + AT91C_BASE_OTGHS->OTGHS_DEVDMA[i].OTGHS_DEVDMACONTROL = 0; // STOP command + + // Clear DMA channel status (read the register for clear it) + AT91C_BASE_OTGHS->OTGHS_DEVDMA[i].OTGHS_DEVDMASTATUS = AT91C_BASE_OTGHS->OTGHS_DEVDMA[i].OTGHS_DEVDMASTATUS; + + } + + // Enable clock OTG pad + pInterface->OTGHS_CTRL &= ~AT91C_OTGHS_FRZCLKCTRL; + + // Clear General IT + pInterface->OTGHS_SCR = 0x01FF; + + // Clear OTG Device IT + pInterface->OTGHS_DEVICR = 0xFF; + + // Clear OTG Host IT + pInterface->OTGHS_HSTICR = 0x7F; + + // Reset all Endpoints Fifos + pInterface->OTGHS_DEVEPT |= (0x7F<<16); + pInterface->OTGHS_DEVEPT &= ~(0x7F<<16); + + // Disable all endpoints + pInterface->OTGHS_DEVEPT &= ~0x7F; + + // Bypass UTMI problems // jcb to be removed with new version of UTMI + // pInterface->OTGHS_TSTA2 = (1<<6)|(1<<7)|(1<<8); + // pInterface->OTGHS_TSTA2 = (1<<8); + pInterface->OTGHS_TSTA2 = 0; + + // External pull-up on D+ + // Configure + BRD_ConfigurePullUp(pInterface); + + // Detach + OTGHS_Disconnect(pUsb); + + // Device is in the Attached state + *(pUsb->pState) = USB_STATE_ATTACHED; + + // Disable the UDP transceiver and interrupts + OTGHS_EnableMCK(pUsb); + SET(pInterface->OTGHS_DEVIER, AT91C_OTGHS_EORSM); + + OTGHS_DisableMCK(pUsb); + OTGHS_Disconnect(pUsb); + + // Test ID + if( 0 != (pInterface->OTGHS_SR & AT91C_OTGHS_ID) ) { + TRACE_INFO("ID=1: PERIPHERAL\n\r"); + } + else { + TRACE_INFO("ID=0: HOST\n\r"); + } + + // Test VBUS + if( 0 != (pInterface->OTGHS_SR & AT91C_OTGHS_VBUSSR) ) { + TRACE_INFO("VBUS = 1\n\r"); + } + else { + TRACE_INFO("VBUS = 0\n\r"); + } + + // Test SPEED + if(AT91C_OTGHS_SPEED_SR_HS == (pInterface->OTGHS_SR & (0x03<<12))) { + TRACE_INFO("HIGH SPEED\n\r"); + } + else if(AT91C_OTGHS_SPEED_SR_LS == (pInterface->OTGHS_SR & (0x03<<12))) { + TRACE_INFO("LOW SPEED\n\r"); + } + else { + TRACE_INFO("FULL SPEED\n\r"); + } + + // Configure interrupts + USB_InitCallback(pUsb); + + pInterface->OTGHS_CTRL |= AT91C_OTGHS_VBUSTI; +} + +//------------------------------------------------------------------------------ +// Global variables +//------------------------------------------------------------------------------ + +// \brief Low-level driver methods to use with the OTGHS USB controller +// \see S_driver_methods +const S_driver_methods sOTGHSMethods = { + + OTGHS_Init, + OTGHS_Write, + OTGHS_Read, + OTGHS_Stall, + OTGHS_Halt, + OTGHS_RemoteWakeUp, + OTGHS_ConfigureEndpoint, + OTGHS_Attach, + OTGHS_SetAddress, + OTGHS_SetConfiguration, + OTGHS_Handler, + OTGHS_Connect, + OTGHS_Disconnect, + OTGHS_Test, + OTGHS_IsHighSpeed +}; + +// \brief Default driver when an UDP controller is present on a chip +const S_usb_driver sDefaultDriver = { + + AT91C_BASE_OTGHS, + AT91C_BASE_OTGHS_EPTFIFO, + 0, + AT91C_ID_OTGHS, + AT91C_PMC_OTG, + &sOTGHSMethods +}; + +#endif //#ifdef CHIP_OTGHS + diff --git a/at91lib/usb/device/core/USBD_UDP.c b/at91lib/usb/device/core/USBD_UDP.c new file mode 100644 index 0000000..1438441 --- /dev/null +++ b/at91lib/usb/device/core/USBD_UDP.c @@ -0,0 +1,1224 @@ +/* ---------------------------------------------------------------------------- + * 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. + * ---------------------------------------------------------------------------- + */ + +/** + \unit + + !!!Purpose + + Implementation of USB device functions on a UDP controller. + + See "USBD API Methods". +*/ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include "USBD.h" +#include "USBDCallbacks.h" +#include +#include +#include +#include +#include +#include + +#if defined(BOARD_USB_UDP) + +//------------------------------------------------------------------------------ +// Definitions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "UDP register field values" +/// +/// This page lists the initialize values of UDP registers. +/// +/// !Values +/// - UDP_RXDATA + +/// Bit mask for both banks of the UDP_CSR register. +#define UDP_RXDATA (AT91C_UDP_RX_DATA_BK0 | AT91C_UDP_RX_DATA_BK1) +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "Endpoint states" +/// +/// This page lists the endpoint states. +/// +/// !States +// - UDP_ENDPOINT_DISABLED +// - UDP_ENDPOINT_HALTED +// - UDP_ENDPOINT_IDLE +// - UDP_ENDPOINT_SENDING +// - UDP_ENDPOINT_RECEIVING + +/// Endpoint states: Endpoint is disabled +#define UDP_ENDPOINT_DISABLED 0 +/// Endpoint states: Endpoint is halted (i.e. STALLs every request) +#define UDP_ENDPOINT_HALTED 1 +/// Endpoint states: Endpoint is idle (i.e. ready for transmission) +#define UDP_ENDPOINT_IDLE 2 +/// Endpoint states: Endpoint is sending data +#define UDP_ENDPOINT_SENDING 3 +/// Endpoint states: Endpoint is receiving data +#define UDP_ENDPOINT_RECEIVING 4 +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// \page "UDP_CSR register access" +/// +/// This page lists the macroes to access UDP CSR register. +/// +/// !Macros +/// - CLEAR_CSR +/// - SET_CSR + +/// Bitmap for all status bits in CSR. +#define REG_NO_EFFECT_1_ALL AT91C_UDP_RX_DATA_BK0 | AT91C_UDP_RX_DATA_BK1 \ + |AT91C_UDP_STALLSENT | AT91C_UDP_RXSETUP \ + |AT91C_UDP_TXCOMP + +/// Clears the specified bit(s) in the UDP_CSR register. +/// \param endpoint The endpoint number of the CSR to process. +/// \param flags The bitmap to set to 1. +#define SET_CSR(endpoint, flags) \ + { \ + volatile unsigned int reg; \ + reg = AT91C_BASE_UDP->UDP_CSR[endpoint] ; \ + reg |= REG_NO_EFFECT_1_ALL; \ + reg |= (flags); \ + AT91C_BASE_UDP->UDP_CSR[endpoint] = reg; \ + while ( (AT91C_BASE_UDP->UDP_CSR[endpoint] & (flags)) != (flags)); \ + } + +/// Sets the specified bit(s) in the UDP_CSR register. +/// \param endpoint The endpoint number of the CSR to process. +/// \param flags The bitmap to clear to 0. +#define CLEAR_CSR(endpoint, flags) \ + { \ + volatile unsigned int reg; \ + reg = AT91C_BASE_UDP->UDP_CSR[endpoint]; \ + reg |= REG_NO_EFFECT_1_ALL; \ + reg &= ~(flags); \ + AT91C_BASE_UDP->UDP_CSR[endpoint] = reg; \ + while ( (AT91C_BASE_UDP->UDP_CSR[endpoint] & (flags)) == (flags)); \ + } +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// Types +//------------------------------------------------------------------------------ + +/// Describes an ongoing transfer on a UDP endpoint. +typedef struct { + + /// Pointer to a data buffer used for emission/reception. + char *pData; + /// Number of bytes which have been written into the UDP internal FIFO + /// buffers. + volatile int buffered; + /// Number of bytes which have been sent/received. + volatile int transferred; + /// Number of bytes which have not been buffered/transferred yet. + volatile int remaining; + /// Optional callback to invoke when the transfer completes. + volatile TransferCallback fCallback; + /// Optional argument to the callback function. + void *pArgument; +} Transfer; + +//------------------------------------------------------------------------------ +/// Describes the state of an endpoint of the UDP controller. +//------------------------------------------------------------------------------ +typedef struct { + + /// Current endpoint state. + volatile unsigned char state; + /// Current reception bank (0 or 1). + volatile unsigned char bank; + /// Maximum packet size for the endpoint. + volatile unsigned short size; + /// Describes an ongoing transfer (if current state is either + /// or ) + Transfer transfer; +} Endpoint; + +//------------------------------------------------------------------------------ +// Internal variables +//------------------------------------------------------------------------------ + +/// Holds the internal state for each endpoint of the UDP. +static Endpoint endpoints[BOARD_USB_NUMENDPOINTS]; + +/// Device current state. +static unsigned char deviceState; +/// Indicates the previous device state +static unsigned char previousDeviceState; + +//------------------------------------------------------------------------------ +// Internal Functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Enables the clock of the UDP peripheral. +//------------------------------------------------------------------------------ +static inline void UDP_EnablePeripheralClock(void) +{ + AT91C_BASE_PMC->PMC_PCER = 1 << AT91C_ID_UDP; +} + +//------------------------------------------------------------------------------ +/// Disables the UDP peripheral clock. +//------------------------------------------------------------------------------ +static inline void UDP_DisablePeripheralClock(void) +{ + AT91C_BASE_PMC->PMC_PCDR = 1 << AT91C_ID_UDP; +} + +//------------------------------------------------------------------------------ +/// Enables the 48MHz USB clock. +//------------------------------------------------------------------------------ +static inline void UDP_EnableUsbClock(void) +{ + AT91C_BASE_PMC->PMC_SCER = AT91C_PMC_UDP; +} + +//------------------------------------------------------------------------------ +/// Disables the 48MHz USB clock. +//------------------------------------------------------------------------------ +static inline void UDP_DisableUsbClock(void) +{ + AT91C_BASE_PMC->PMC_SCDR = AT91C_PMC_UDP; +} + +//------------------------------------------------------------------------------ +/// Enables the UDP transceiver. +//------------------------------------------------------------------------------ +static inline void UDP_EnableTransceiver(void) +{ + AT91C_BASE_UDP->UDP_TXVC &= ~AT91C_UDP_TXVDIS; +} + +//------------------------------------------------------------------------------ +/// Disables the UDP transceiver. +//------------------------------------------------------------------------------ +static inline void UDP_DisableTransceiver(void) +{ + AT91C_BASE_UDP->UDP_TXVC |= AT91C_UDP_TXVDIS; +} + +//------------------------------------------------------------------------------ +/// Handles a completed transfer on the given endpoint, invoking the +/// configured callback if any. +/// \param bEndpoint Number of the endpoint for which the transfer has completed. +/// \param bStatus Status code returned by the transfer operation +//------------------------------------------------------------------------------ +static void UDP_EndOfTransfer(unsigned char bEndpoint, char bStatus) +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + Transfer *pTransfer = &(pEndpoint->transfer); + + // Check that endpoint was sending or receiving data + if( (pEndpoint->state == UDP_ENDPOINT_RECEIVING) + || (pEndpoint->state == UDP_ENDPOINT_SENDING)) { + + TRACE_DEBUG_WP("Eo"); + + // Endpoint returns in Idle state + pEndpoint->state = UDP_ENDPOINT_IDLE; + + // Invoke callback is present + if (pTransfer->fCallback != 0) { + + ((TransferCallback) pTransfer->fCallback) + (pTransfer->pArgument, + bStatus, + pTransfer->transferred, + pTransfer->remaining + pTransfer->buffered); + } + else { + TRACE_DEBUG_WP("No callBack\n\r"); + } + } +} + +//------------------------------------------------------------------------------ +/// Clears the correct reception flag (bank 0 or bank 1) of an endpoint +/// \param bEndpoint Index of endpoint +//------------------------------------------------------------------------------ +static void UDP_ClearRxFlag(unsigned char bEndpoint) +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + + // Clear flag and change banks + if (pEndpoint->bank == 0) { + + CLEAR_CSR(bEndpoint, AT91C_UDP_RX_DATA_BK0); + // Swap bank if in dual-fifo mode + if (BOARD_USB_ENDPOINTS_BANKS(bEndpoint) > 1) { + + pEndpoint->bank = 1; + } + } + else { + + CLEAR_CSR(bEndpoint, AT91C_UDP_RX_DATA_BK1); + pEndpoint->bank = 0; + } +} + +//------------------------------------------------------------------------------ +/// Transfers a data payload from the current tranfer buffer to the endpoint +/// FIFO +/// \param bEndpoint Number of the endpoint which is sending data. +//------------------------------------------------------------------------------ +static void UDP_WritePayload(unsigned char bEndpoint) +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + Transfer *pTransfer = &(pEndpoint->transfer); + signed int size; + + // Get the number of bytes to send + size = pEndpoint->size; + if (size > pTransfer->remaining) { + + size = pTransfer->remaining; + } + + // Update transfer descriptor information + pTransfer->buffered += size; + pTransfer->remaining -= size; + + // Write packet in the FIFO buffer + while (size > 0) { + + AT91C_BASE_UDP->UDP_FDR[bEndpoint] = *(pTransfer->pData); + pTransfer->pData++; + size--; + } +} + + +//------------------------------------------------------------------------------ +/// Transfers a data payload from an endpoint FIFO to the current transfer buffer +/// \param bEndpoint Endpoint number. +/// \param wPacketSize Size of received data packet +//------------------------------------------------------------------------------ +static void UDP_ReadPayload(unsigned char bEndpoint, int wPacketSize) +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + Transfer *pTransfer = &(pEndpoint->transfer); + + // Check that the requested size is not bigger than the remaining transfer + if (wPacketSize > pTransfer->remaining) { + + pTransfer->buffered += wPacketSize - pTransfer->remaining; + wPacketSize = pTransfer->remaining; + } + + // Update transfer descriptor information + pTransfer->remaining -= wPacketSize; + pTransfer->transferred += wPacketSize; + + // Retrieve packet + while (wPacketSize > 0) { + + *(pTransfer->pData) = (char) AT91C_BASE_UDP->UDP_FDR[bEndpoint]; + pTransfer->pData++; + wPacketSize--; + } +} + +//------------------------------------------------------------------------------ +/// Received SETUP packet from endpoint 0 FIFO +/// \param pRequest Generic USB SETUP request sent over Control endpoints +//------------------------------------------------------------------------------ +static void UDP_ReadRequest(USBGenericRequest *pRequest) +{ + unsigned char *pData = (unsigned char *)pRequest; + unsigned int i; + + // Copy packet + for (i = 0; i < 8; i++) { + + *pData = (unsigned char) AT91C_BASE_UDP->UDP_FDR[0]; + pData++; + } +} + +//------------------------------------------------------------------------------ +/// Reset all endpoint transfer descriptors +//------------------------------------------------------------------------------ +static void UDP_ResetEndpoints( void ) +{ + Endpoint *pEndpoint; + Transfer *pTransfer; + unsigned char bEndpoint; + + // Reset the transfer descriptor of every endpoint + for (bEndpoint = 0; bEndpoint < BOARD_USB_NUMENDPOINTS; bEndpoint++) { + + pEndpoint = &(endpoints[bEndpoint]); + pTransfer = &(pEndpoint->transfer); + + // Reset endpoint transfer descriptor + pTransfer->pData = 0; + pTransfer->transferred = -1; + pTransfer->buffered = -1; + pTransfer->remaining = -1; + pTransfer->fCallback = 0; + pTransfer->pArgument = 0; + + // Reset endpoint state + pEndpoint->bank = 0; + pEndpoint->state = UDP_ENDPOINT_DISABLED; + } +} + +//------------------------------------------------------------------------------ +/// Disable all endpoints (except control endpoint 0), aborting current +/// transfers if necessary +//------------------------------------------------------------------------------ +static void UDP_DisableEndpoints( void ) + +{ + unsigned char bEndpoint; + + // Disable each endpoint, terminating any pending transfer + // Control endpoint 0 is not disabled + for (bEndpoint = 1; bEndpoint < BOARD_USB_NUMENDPOINTS; bEndpoint++) { + + UDP_EndOfTransfer(bEndpoint, USBD_STATUS_ABORTED); + endpoints[bEndpoint].state = UDP_ENDPOINT_DISABLED; + } +} + +//------------------------------------------------------------------------------ +/// Checks if an ongoing transfer on an endpoint has been completed. +/// \param bEndpoint Endpoint number. +/// \return 1 if the current transfer on the given endpoint is complete; +/// otherwise 0. +//------------------------------------------------------------------------------ +static unsigned char UDP_IsTransferFinished(unsigned char bEndpoint) +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + Transfer *pTransfer = &(pEndpoint->transfer); + + // Check if it is a Control endpoint + // -> Control endpoint must always finish their transfer with a zero-length + // packet + if ((AT91C_BASE_UDP->UDP_CSR[bEndpoint] & AT91C_UDP_EPTYPE) + == AT91C_UDP_EPTYPE_CTRL) { + + return (pTransfer->buffered < pEndpoint->size); + } + // Other endpoints only need to transfer all the data + else { + + return (pTransfer->buffered <= pEndpoint->size) + && (pTransfer->remaining == 0); + } +} + +//------------------------------------------------------------------------------ +/// Endpoint interrupt handler. +/// Handle IN/OUT transfers, received SETUP packets and STALLing +/// \param bEndpoint Index of endpoint +//------------------------------------------------------------------------------ +static void UDP_EndpointHandler(unsigned char bEndpoint) +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + Transfer *pTransfer = &(pEndpoint->transfer); + unsigned int status = AT91C_BASE_UDP->UDP_CSR[bEndpoint]; + unsigned short wPacketSize; + USBGenericRequest request; + + TRACE_DEBUG_WP("E%d ", bEndpoint); + TRACE_DEBUG_WP("st:0x%X ", status); + + // Handle interrupts + // IN packet sent + if ((status & AT91C_UDP_TXCOMP) != 0) { + + TRACE_DEBUG_WP("Wr "); + + // Check that endpoint was in Sending state + if (pEndpoint->state == UDP_ENDPOINT_SENDING) { + + // End of transfer ? + if (UDP_IsTransferFinished(bEndpoint)) { + + pTransfer->transferred += pTransfer->buffered; + pTransfer->buffered = 0; + + // Disable interrupt if this is not a control endpoint + if ((status & AT91C_UDP_EPTYPE) != AT91C_UDP_EPTYPE_CTRL) { + + AT91C_BASE_UDP->UDP_IDR = 1 << bEndpoint; + } + + UDP_EndOfTransfer(bEndpoint, USBD_STATUS_SUCCESS); + CLEAR_CSR(bEndpoint, AT91C_UDP_TXCOMP); + } + else { + + // Transfer remaining data + TRACE_DEBUG_WP(" %d ", pEndpoint->size); + + pTransfer->transferred += pEndpoint->size; + pTransfer->buffered -= pEndpoint->size; + + // Send next packet + if (BOARD_USB_ENDPOINTS_BANKS(bEndpoint) == 1) { + + // No double buffering + UDP_WritePayload(bEndpoint); + SET_CSR(bEndpoint, AT91C_UDP_TXPKTRDY); + CLEAR_CSR(bEndpoint, AT91C_UDP_TXCOMP); + } + else { + // Double buffering + SET_CSR(bEndpoint, AT91C_UDP_TXPKTRDY); + CLEAR_CSR(bEndpoint, AT91C_UDP_TXCOMP); + UDP_WritePayload(bEndpoint); + } + } + } + else { + // Acknowledge interrupt + TRACE_ERROR("Error Wr"); + CLEAR_CSR(bEndpoint, AT91C_UDP_TXCOMP); + } + } + + // OUT packet received + if ((status & UDP_RXDATA) != 0) { + + TRACE_DEBUG_WP("Rd "); + + // Check that the endpoint is in Receiving state + if (pEndpoint->state != UDP_ENDPOINT_RECEIVING) { + + // Check if an ACK has been received on a Control endpoint + if (((status & AT91C_UDP_EPTYPE) == AT91C_UDP_EPTYPE_CTRL) + && ((status & AT91C_UDP_RXBYTECNT) == 0)) { + + // Acknowledge the data and finish the current transfer + UDP_ClearRxFlag(bEndpoint); + UDP_EndOfTransfer(bEndpoint, USBD_STATUS_SUCCESS); + } + // Check if the data has been STALLed + else if ((status & AT91C_UDP_FORCESTALL) != 0) { + + // Discard STALLed data + TRACE_DEBUG_WP("Discard "); + UDP_ClearRxFlag(bEndpoint); + } + // NAK the data + else { + + TRACE_DEBUG_WP("Nak "); + AT91C_BASE_UDP->UDP_IDR = 1 << bEndpoint; + } + } + // Endpoint is in Read state + else { + + // Retrieve data and store it into the current transfer buffer + wPacketSize = (unsigned short) (status >> 16); + TRACE_DEBUG_WP("%d ", wPacketSize); + UDP_ReadPayload(bEndpoint, wPacketSize); + UDP_ClearRxFlag(bEndpoint); + + // Check if the transfer is finished + if ((pTransfer->remaining == 0) || (wPacketSize < pEndpoint->size)) { + + // Disable interrupt if this is not a control endpoint + if ((status & AT91C_UDP_EPTYPE) != AT91C_UDP_EPTYPE_CTRL) { + + AT91C_BASE_UDP->UDP_IDR = 1 << bEndpoint; + } + UDP_EndOfTransfer(bEndpoint, USBD_STATUS_SUCCESS); + } + } + } + + // STALL sent + if ((status & AT91C_UDP_STALLSENT) != 0) { + + TRACE_WARNING( "Sta 0x%X [%d] ", status, bEndpoint); + + // If the endpoint is not halted, clear the STALL condition + CLEAR_CSR(bEndpoint, AT91C_UDP_STALLSENT); + if (pEndpoint->state != UDP_ENDPOINT_HALTED) { + + TRACE_WARNING( "_ " ); + CLEAR_CSR(bEndpoint, AT91C_UDP_FORCESTALL); + } + } + + // SETUP packet received + if ((status & AT91C_UDP_RXSETUP) != 0) { + + TRACE_DEBUG_WP("Stp "); + + // If a transfer was pending, complete it + // Handles the case where during the status phase of a control write + // transfer, the host receives the device ZLP and ack it, but the ack + // is not received by the device + if ((pEndpoint->state == UDP_ENDPOINT_RECEIVING) + || (pEndpoint->state == UDP_ENDPOINT_SENDING)) { + + UDP_EndOfTransfer(bEndpoint, USBD_STATUS_SUCCESS); + } + // Copy the setup packet + UDP_ReadRequest(&request); + + // Set the DIR bit before clearing RXSETUP in Control IN sequence + if (USBGenericRequest_GetDirection(&request) == USBGenericRequest_IN) { + + SET_CSR(bEndpoint, AT91C_UDP_DIR); + } + // Acknowledge setup packet + CLEAR_CSR(bEndpoint, AT91C_UDP_RXSETUP); + + // Forward the request to the upper layer + USBDCallbacks_RequestReceived(&request); + } + +} + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +/// USB interrupt handler +/// Manages device resume, suspend, end of bus reset. +/// Forwards endpoint interrupts to the appropriate handler. +//------------------------------------------------------------------------------ +void USBD_InterruptHandler(void) +{ + unsigned int status; + int eptnum = 0; + + // Get interrupt status + // Some interrupts may get masked depending on the device state + status = AT91C_BASE_UDP->UDP_ISR; + status &= AT91C_BASE_UDP->UDP_IMR; + + if (deviceState < USBD_STATE_POWERED) { + + status &= AT91C_UDP_WAKEUP | AT91C_UDP_RXRSM; + AT91C_BASE_UDP->UDP_ICR = ~status; + } + + // Return immediately if there is no interrupt to service + if (status == 0) { + + return; + } + + // Toggle USB LED if the device is active + if (deviceState >= USBD_STATE_POWERED) { + + LED_Set(USBD_LEDUSB); + } + + // Service interrupts + + //// Start Of Frame (SOF) + //if (ISSET(dStatus, AT91C_UDP_SOFINT)) { + // + // TRACE_DEBUG("SOF"); + // + // // Invoke the SOF callback + // USB_StartOfFrameCallback(pUsb); + // + // // Acknowledge interrupt + // AT91C_BASE_UDP->UDP_ICR = AT91C_UDP_SOFINT; + // dStatus &= ~AT91C_UDP_SOFINT; + //} + + // Suspend + // This interrupt is always treated last (hence the '==') + if (status == AT91C_UDP_RXSUSP) { + + TRACE_INFO_WP("Susp "); + + // Don't do anything if the device is already suspended + if (deviceState != USBD_STATE_SUSPENDED) { + + // The device enters the Suspended state + // Enable wakeup + AT91C_BASE_UDP->UDP_IER = AT91C_UDP_WAKEUP | AT91C_UDP_RXRSM; + + // Acknowledge interrupt + AT91C_BASE_UDP->UDP_ICR = AT91C_UDP_RXSUSP; + + // Switch to the Suspended state + previousDeviceState = deviceState; + deviceState = USBD_STATE_SUSPENDED; + // Invoke the Suspended callback + USBDCallbacks_Suspended(); + UDP_DisableTransceiver(); + UDP_DisablePeripheralClock(); + UDP_DisableUsbClock(); + } + } + // Resume + else if ((status & (AT91C_UDP_WAKEUP | AT91C_UDP_RXRSM)) != 0) { + + TRACE_INFO_WP("Res "); + + // Don't do anything if the device was not suspended + if (deviceState == USBD_STATE_SUSPENDED) { + + // The device enters its previous state + UDP_EnablePeripheralClock(); + UDP_EnableUsbClock(); + + // Enable the transceiver if the device was past the Default + // state + deviceState = previousDeviceState; + if (deviceState >= USBD_STATE_DEFAULT) { + + UDP_EnableTransceiver(); + + // Invoke the Resume callback + USBDCallbacks_Resumed(); + } + } + + // Clear and disable resume interrupts + AT91C_BASE_UDP->UDP_ICR = AT91C_UDP_WAKEUP + | AT91C_UDP_RXRSM + | AT91C_UDP_RXSUSP; + AT91C_BASE_UDP->UDP_IDR = AT91C_UDP_WAKEUP | AT91C_UDP_RXRSM; + } + // End of bus reset + else if ((status & AT91C_UDP_ENDBUSRES) != 0) { + + TRACE_INFO_WP("EoBRes "); + + // The device enters the Default state + deviceState = USBD_STATE_DEFAULT; + UDP_EnableTransceiver(); + UDP_ResetEndpoints(); + UDP_DisableEndpoints(); + USBD_ConfigureEndpoint(0); + + // Flush and enable the Suspend interrupt + AT91C_BASE_UDP->UDP_ICR = AT91C_UDP_WAKEUP + | AT91C_UDP_RXRSM + | AT91C_UDP_RXSUSP; + AT91C_BASE_UDP->UDP_IER = AT91C_UDP_RXSUSP; + + //// Enable the Start Of Frame (SOF) interrupt if needed + //if (pUsb->pCallbacks->startOfFrame != 0) { + // + // AT91C_BASE_UDP->UDP_IER = AT91C_UDP_SOFINT; + //} + + // Invoke the Reset callback + USBDCallbacks_Reset(); + + // Acknowledge end of bus reset interrupt + AT91C_BASE_UDP->UDP_ICR = AT91C_UDP_ENDBUSRES; + } + // Endpoint interrupts + else { + + while (status != 0) { + + // Check if endpoint has a pending interrupt + if ((status & (1 << eptnum)) != 0) { + + UDP_EndpointHandler(eptnum); + status &= ~(1 << eptnum); + + if (status != 0) { + + TRACE_INFO_WP("\n\r - "); + } + } + eptnum++; + } + } + + // Toggle LED back to its previous state + TRACE_INFO_WP("\n\r"); + if (deviceState >= USBD_STATE_POWERED) { + + LED_Clear(USBD_LEDUSB); + } +} + +//------------------------------------------------------------------------------ +/// Configures an endpoint according to its Endpoint Descriptor. +/// \param pDescriptor Pointer to an Endpoint descriptor. +//------------------------------------------------------------------------------ +void USBD_ConfigureEndpoint(const USBEndpointDescriptor *pDescriptor) +{ + Endpoint *pEndpoint; + unsigned char bEndpoint; + unsigned char bType; + unsigned char bEndpointDir; + + // NULL descriptor -> Control endpoint 0 + if (pDescriptor == 0) { + + bEndpoint = 0; + pEndpoint = &(endpoints[bEndpoint]); + bType= USBEndpointDescriptor_CONTROL; + bEndpointDir = 0; + pEndpoint->size = BOARD_USB_ENDPOINTS_MAXPACKETSIZE(0); + } + else { + + bEndpoint = USBEndpointDescriptor_GetNumber(pDescriptor); + pEndpoint = &(endpoints[bEndpoint]); + bType = USBEndpointDescriptor_GetType(pDescriptor); + bEndpointDir = USBEndpointDescriptor_GetDirection(pDescriptor); + pEndpoint->size = USBEndpointDescriptor_GetMaxPacketSize(pDescriptor); + } + + // Abort the current transfer is the endpoint was configured and in + // Write or Read state + if ((pEndpoint->state == UDP_ENDPOINT_RECEIVING) + || (pEndpoint->state == UDP_ENDPOINT_SENDING)) { + + UDP_EndOfTransfer(bEndpoint, USBD_STATUS_RESET); + } + pEndpoint->state = UDP_ENDPOINT_IDLE; + + // Reset Endpoint Fifos + AT91C_BASE_UDP->UDP_RSTEP |= (1 << bEndpoint); + AT91C_BASE_UDP->UDP_RSTEP &= ~(1 << bEndpoint); + + // Configure endpoint + SET_CSR(bEndpoint, (unsigned int)AT91C_UDP_EPEDS | (bType << 8) | (bEndpointDir << 10)); + if (bType == USBEndpointDescriptor_CONTROL) { + + AT91C_BASE_UDP->UDP_IER = (1 << bEndpoint); + } + + TRACE_INFO_WP("CfgEpt%d ", bEndpoint); +} + +//------------------------------------------------------------------------------ +/// Sends data through a USB endpoint. Sets up the transfer descriptor, +/// writes one or two data payloads (depending on the number of FIFO bank +/// for the endpoint) and then starts the actual transfer. The operation is +/// complete when all the data has been sent. +/// +/// *If the size of the buffer is greater than the size of the endpoint +/// (or twice the size if the endpoint has two FIFO banks), then the buffer +/// must be kept allocated until the transfer is finished*. This means that +/// it is not possible to declare it on the stack (i.e. as a local variable +/// of a function which returns after starting a transfer). +/// +/// \param bEndpoint Endpoint number. +/// \param pData Pointer to a buffer with the data to send. +/// \param dLength Size of the data buffer. +/// \param fCallback Optional callback function to invoke when the transfer is +/// complete. +/// \param pArgument Optional argument to the callback function. +/// \return USBD_STATUS_SUCCESS if the transfer has been started; +/// otherwise, the corresponding error status code. +//------------------------------------------------------------------------------ +char USBD_Write( unsigned char bEndpoint, + const void *pData, + unsigned int dLength, + TransferCallback fCallback, + void *pArgument ) +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + Transfer *pTransfer = &(pEndpoint->transfer); + + // Check that the endpoint is in Idle state + if (pEndpoint->state != UDP_ENDPOINT_IDLE) { + + return USBD_STATUS_LOCKED; + } + TRACE_DEBUG_WP("Write%d(%d) ", bEndpoint, dLength); + + // Setup the transfer descriptor + pTransfer->pData = (void *) pData; + pTransfer->remaining = dLength; + pTransfer->buffered = 0; + pTransfer->transferred = 0; + pTransfer->fCallback = fCallback; + pTransfer->pArgument = pArgument; + + // Send the first packet + pEndpoint->state = UDP_ENDPOINT_SENDING; + while((AT91C_BASE_UDP->UDP_CSR[bEndpoint]&AT91C_UDP_TXPKTRDY)==AT91C_UDP_TXPKTRDY); + UDP_WritePayload(bEndpoint); + SET_CSR(bEndpoint, AT91C_UDP_TXPKTRDY); + + // If double buffering is enabled and there is data remaining, + // prepare another packet + if ((BOARD_USB_ENDPOINTS_BANKS(bEndpoint) > 1) && (pTransfer->remaining > 0)) { + + UDP_WritePayload(bEndpoint); + } + + // Enable interrupt on endpoint + AT91C_BASE_UDP->UDP_IER = 1 << bEndpoint; + + return USBD_STATUS_SUCCESS; +} + + +//------------------------------------------------------------------------------ +/// Reads incoming data on an USB endpoint This methods sets the transfer +/// descriptor and activate the endpoint interrupt. The actual transfer is +/// then carried out by the endpoint interrupt handler. The Read operation +/// finishes either when the buffer is full, or a short packet (inferior to +/// endpoint maximum size) is received. +/// +/// *The buffer must be kept allocated until the transfer is finished*. +/// \param bEndpoint Endpoint number. +/// \param pData Pointer to a data buffer. +/// \param dLength Size of the data buffer in bytes. +/// \param fCallback Optional end-of-transfer callback function. +/// \param pArgument Optional argument to the callback function. +/// \return USBD_STATUS_SUCCESS if the read operation has been started; +/// otherwise, the corresponding error code. +//------------------------------------------------------------------------------ +char USBD_Read(unsigned char bEndpoint, + void *pData, + unsigned int dLength, + TransferCallback fCallback, + void *pArgument) +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + Transfer *pTransfer = &(pEndpoint->transfer); + + // Return if the endpoint is not in IDLE state + if (pEndpoint->state != UDP_ENDPOINT_IDLE) { + + return USBD_STATUS_LOCKED; + } + + // Endpoint enters Receiving state + pEndpoint->state = UDP_ENDPOINT_RECEIVING; + TRACE_DEBUG_WP("Read%d(%d) ", bEndpoint, dLength); + + // Set the transfer descriptor + pTransfer->pData = pData; + pTransfer->remaining = dLength; + pTransfer->buffered = 0; + pTransfer->transferred = 0; + pTransfer->fCallback = fCallback; + pTransfer->pArgument = pArgument; + + // Enable interrupt on endpoint + AT91C_BASE_UDP->UDP_IER = 1 << bEndpoint; + + return USBD_STATUS_SUCCESS; +} + +//------------------------------------------------------------------------------ +/// Sets the HALT feature on the given endpoint (if not already in this state). +/// \param bEndpoint Endpoint number. +//------------------------------------------------------------------------------ +void USBD_Halt(unsigned char bEndpoint) +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + + // Check that endpoint is enabled and not already in Halt state + if ((pEndpoint->state != UDP_ENDPOINT_DISABLED) + && (pEndpoint->state != UDP_ENDPOINT_HALTED)) { + + TRACE_DEBUG_WP("Halt%d ", bEndpoint); + + // Abort the current transfer if necessary + UDP_EndOfTransfer(bEndpoint, USBD_STATUS_ABORTED); + + // Put endpoint into Halt state + SET_CSR(bEndpoint, AT91C_UDP_FORCESTALL); + pEndpoint->state = UDP_ENDPOINT_HALTED; + + // Enable the endpoint interrupt + AT91C_BASE_UDP->UDP_IER = 1 << bEndpoint; + } +} + +//------------------------------------------------------------------------------ +/// Clears the Halt feature on the given endpoint. +/// \param bEndpoint Index of endpoint +//------------------------------------------------------------------------------ +void USBD_Unhalt(unsigned char bEndpoint) +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + + // Check if the endpoint is enabled + if (pEndpoint->state != UDP_ENDPOINT_DISABLED) { + + TRACE_DEBUG_WP("Unhalt%d ", bEndpoint); + + // Return endpoint to Idle state + pEndpoint->state = UDP_ENDPOINT_IDLE; + + // Clear FORCESTALL flag + CLEAR_CSR(bEndpoint, AT91C_UDP_FORCESTALL); + + // Reset Endpoint Fifos, beware this is a 2 steps operation + AT91C_BASE_UDP->UDP_RSTEP |= 1 << bEndpoint; + AT91C_BASE_UDP->UDP_RSTEP &= ~(1 << bEndpoint); + } +} + +//------------------------------------------------------------------------------ +/// Returns the current Halt status of an endpoint. +/// \param bEndpoint Index of endpoint +/// \return 1 if the endpoint is currently halted; otherwise 0 +//------------------------------------------------------------------------------ +unsigned char USBD_IsHalted(unsigned char bEndpoint) +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + unsigned char status = 0; + + if (pEndpoint->state == UDP_ENDPOINT_HALTED) { + + status = 1; + } + return( status ); +} + +//------------------------------------------------------------------------------ +/// Indicates if the device is running in high or full-speed. Always returns 0 +/// since UDP does not support high-speed mode. +//------------------------------------------------------------------------------ +unsigned char USBD_IsHighSpeed(void) +{ + return 0; +} + +//------------------------------------------------------------------------------ +/// Causes the given endpoint to acknowledge the next packet it receives +/// with a STALL handshake. +/// \param bEndpoint Endpoint number. +/// \return USBD_STATUS_SUCCESS or USBD_STATUS_LOCKED. +//------------------------------------------------------------------------------ +unsigned char USBD_Stall(unsigned char bEndpoint) + +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + + // Check that endpoint is in Idle state + if (pEndpoint->state != UDP_ENDPOINT_IDLE) { + + TRACE_WARNING("UDP_Stall: Endpoint%d locked\n\r", bEndpoint); + return USBD_STATUS_LOCKED; + } + + TRACE_DEBUG_WP("Stall%d ", bEndpoint); + SET_CSR(bEndpoint, AT91C_UDP_FORCESTALL); + + return USBD_STATUS_SUCCESS; +} + +//------------------------------------------------------------------------------ +/// Starts a remote wake-up procedure. +//------------------------------------------------------------------------------ +void USBD_RemoteWakeUp(void) +{ + UDP_EnablePeripheralClock(); + UDP_EnableUsbClock(); + UDP_EnableTransceiver(); + + TRACE_INFO_WP("RWUp "); + + // Activates a remote wakeup (edge on ESR), then clear ESR + AT91C_BASE_UDP->UDP_GLBSTATE |= AT91C_UDP_ESR; + AT91C_BASE_UDP->UDP_GLBSTATE &= ~AT91C_UDP_ESR; +} + +//------------------------------------------------------------------------------ +/// Sets the device address to the given value. +/// \param address New device address. +//------------------------------------------------------------------------------ +void USBD_SetAddress(unsigned char address) +{ + TRACE_INFO_WP("SetAddr(%d) ", address); + + // Set address + AT91C_BASE_UDP->UDP_FADDR = AT91C_UDP_FEN | address; + + // If the address is 0, the device returns to the Default state + if (address == 0) { + + AT91C_BASE_UDP->UDP_GLBSTATE = 0; + deviceState = USBD_STATE_DEFAULT; + } + // If the address is non-zero, the device enters the Address state + else { + + AT91C_BASE_UDP->UDP_GLBSTATE = AT91C_UDP_FADDEN; + deviceState = USBD_STATE_ADDRESS; + } +} + +//------------------------------------------------------------------------------ +/// Sets the current device configuration. +/// \param cfgnum - Configuration number to set. +//------------------------------------------------------------------------------ +void USBD_SetConfiguration(unsigned char cfgnum) +{ + TRACE_INFO_WP("SetCfg(%d) ", cfgnum); + + // If the configuration number if non-zero, the device enters the + // Configured state + if (cfgnum != 0) { + + // Enter Configured state + deviceState = USBD_STATE_CONFIGURED; + AT91C_BASE_UDP->UDP_GLBSTATE |= AT91C_UDP_CONFG; + } + // If the configuration number is zero, the device goes back to the Address + // state + else { + + deviceState = USBD_STATE_ADDRESS; + AT91C_BASE_UDP->UDP_GLBSTATE = AT91C_UDP_FADDEN; + + // Abort all transfers + UDP_DisableEndpoints(); + } +} + +//------------------------------------------------------------------------------ +/// Connects the pull-up on the D+ line of the USB. +//------------------------------------------------------------------------------ +void USBD_Connect(void) +{ + TRACE_DEBUG("Conn "); + +#if defined(BOARD_USB_PULLUP_EXTERNAL) + const Pin pinPullUp = PIN_USB_PULLUP; + if (pinPullUp.attribute == PIO_OUTPUT_0) { + + PIO_Set(&pinPullUp); + } + else { + + PIO_Clear(&pinPullUp); + } +#elif defined(BOARD_USB_PULLUP_INTERNAL) + AT91C_BASE_UDP->UDP_TXVC |= AT91C_UDP_PUON; +#elif defined(BOARD_USB_PULLUP_MATRIX) + AT91C_BASE_MATRIX->MATRIX_USBPCR |= AT91C_MATRIX_USBPCR_PUON; +#elif !defined(BOARD_USB_PULLUP_ALWAYSON) + #error Unsupported pull-up type. +#endif +} + +//------------------------------------------------------------------------------ +/// Disconnects the pull-up from the D+ line of the USB. +//------------------------------------------------------------------------------ +void USBD_Disconnect(void) +{ + TRACE_DEBUG("Disc "); + +#if defined(BOARD_USB_PULLUP_EXTERNAL) + const Pin pinPullUp = PIN_USB_PULLUP; + if (pinPullUp.attribute == PIO_OUTPUT_0) { + + PIO_Clear(&pinPullUp); + } + else { + + PIO_Set(&pinPullUp); + } +#elif defined(BOARD_USB_PULLUP_INTERNAL) + AT91C_BASE_UDP->UDP_TXVC &= ~AT91C_UDP_PUON; +#elif defined(BOARD_USB_PULLUP_MATRIX) + AT91C_BASE_MATRIX->MATRIX_USBPCR &= ~AT91C_MATRIX_USBPCR_PUON; +#elif !defined(BOARD_USB_PULLUP_ALWAYSON) + #error Unsupported pull-up type. +#endif + + // Device returns to the Powered state + if (deviceState > USBD_STATE_POWERED) { + + deviceState = USBD_STATE_POWERED; + } +} + +//------------------------------------------------------------------------------ +/// Initializes the USB driver. +//------------------------------------------------------------------------------ +void USBD_Init(void) +{ + TRACE_INFO_WP("USBD_Init\n\r"); + + // Reset endpoint structures + UDP_ResetEndpoints(); + + // Configure the pull-up on D+ and disconnect it +#if defined(BOARD_USB_PULLUP_EXTERNAL) + const Pin pinPullUp = PIN_USB_PULLUP; + PIO_Configure(&pinPullUp, 1); +#elif defined(BOARD_USB_PULLUP_INTERNAL) + AT91C_BASE_UDP->UDP_TXVC &= ~AT91C_UDP_PUON; +#elif defined(BOARD_USB_PULLUP_MATRIX) + AT91C_BASE_MATRIX->MATRIX_USBPCR &= ~AT91C_MATRIX_USBPCR_PUON; +#elif !defined(BOARD_USB_PULLUP_ALWAYSON) + #error Missing pull-up definition. +#endif + + // Device is in the Attached state + deviceState = USBD_STATE_SUSPENDED; + previousDeviceState = USBD_STATE_POWERED; + UDP_EnablePeripheralClock(); + UDP_EnableUsbClock(); + + AT91C_BASE_UDP->UDP_IDR = 0xFE; + + AT91C_BASE_UDP->UDP_IER = AT91C_UDP_WAKEUP; + + // Configure interrupts + USBDCallbacks_Initialized(); +} + +//------------------------------------------------------------------------------ +/// Returns the current state of the USB device. +/// \return Device current state. +//------------------------------------------------------------------------------ +unsigned char USBD_GetState(void) +{ + return deviceState; +} + +#endif // BOARD_USB_UDP + diff --git a/at91lib/usb/device/core/USBD_UDPHS.c b/at91lib/usb/device/core/USBD_UDPHS.c new file mode 100644 index 0000000..f7c0684 --- /dev/null +++ b/at91lib/usb/device/core/USBD_UDPHS.c @@ -0,0 +1,1680 @@ +/* ---------------------------------------------------------------------------- + * 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 "USBD.h" +#include "USBDCallbacks.h" +#include "USBDDriver.h" +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef BOARD_USB_UDPHS + +//------------------------------------------------------------------------------ +// Definitions +//------------------------------------------------------------------------------ + +/// Maximum number of endpoints interrupts. +#define NUM_IT_MAX \ + (AT91C_BASE_UDPHS->UDPHS_IPFEATURES & AT91C_UDPHS_EPT_NBR_MAX) +/// Maximum number of endpoint DMA interrupts +#define NUM_IT_MAX_DMA \ + ((AT91C_BASE_UDPHS->UDPHS_IPFEATURES & AT91C_UDPHS_DMA_CHANNEL_NBR)>>4) +/// Bits that should be shifted to access DMA control bits. +#define SHIFT_DMA 24 +/// Bits that should be shifted to access interrupt bits. +#define SHIFT_INTERUPT 8 + +/// Compile option, use DMA. Remove this define for not use DMA. +#define DMA + +/// Max size of the FMA FIFO +#define DMA_MAX_FIFO_SIZE 65536 + +//------------------------------------------------------------------------------ +/// \page "Endpoint states" +/// This page lists the endpoint states. +/// !States +// - UDP_ENDPOINT_DISABLED +// - UDP_ENDPOINT_HALTED +// - UDP_ENDPOINT_IDLE +// - UDP_ENDPOINT_SENDING +// - UDP_ENDPOINT_RECEIVING + +/// Endpoint states: Endpoint is disabled +#define UDP_ENDPOINT_DISABLED 0 +/// Endpoint states: Endpoint is halted (i.e. STALLs every request) +#define UDP_ENDPOINT_HALTED 1 +/// Endpoint states: Endpoint is idle (i.e. ready for transmission) +#define UDP_ENDPOINT_IDLE 2 +/// Endpoint states: Endpoint is sending data +#define UDP_ENDPOINT_SENDING 3 +/// Endpoint states: Endpoint is receiving data +#define UDP_ENDPOINT_RECEIVING 4 +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// Structures +//------------------------------------------------------------------------------ + +/// Describes an ongoing transfer on a UDP endpoint. +typedef struct +{ + /// Pointer to a data buffer used for emission/reception. + char *pData; + /// Number of bytes which have been written into the UDP internal FIFO + /// buffers. + volatile int buffered; + /// Number of bytes which have been sent/received. + volatile int transferred; + /// Number of bytes which have not been buffered/transferred yet. + volatile int remaining; + /// Optional callback to invoke when the transfer completes. + volatile TransferCallback fCallback; + /// Optional argument to the callback function. + void *pArgument; +} Transfer; + +//------------------------------------------------------------------------------ +/// Describes the state of an endpoint of the UDP controller. +//------------------------------------------------------------------------------ +typedef struct +{ + /// Current endpoint state. + volatile unsigned char state; + /// Current reception bank (0 or 1). + unsigned char bank; + /// Maximum packet size for the endpoint. + unsigned short size; + /// Describes an ongoing transfer (if current state is either + /// or ) + Transfer transfer; +} Endpoint; + +//------------------------------------------------------------------------------ +// Internal variables +//------------------------------------------------------------------------------ + +/// Holds the internal state for each endpoint of the UDP. +static Endpoint endpoints[BOARD_USB_NUMENDPOINTS]; +/// Device current state. +static unsigned char deviceState; +/// Indicates the previous device state +static unsigned char previousDeviceState; +/// Special case for send a ZLP +static unsigned char sendZLP = 0; + +/// 7.1.20 Test Mode Support +/// Test codes for the USB HS test mode. +static const char test_packet_buffer[] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // JKJKJKJK * 9 + 0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA,0xAA, // JJKKJJKK * 8 + 0xEE,0xEE,0xEE,0xEE,0xEE,0xEE,0xEE,0xEE, // JJJJKKKK * 8 + 0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, // JJJJJJJKKKKKKK * 8 + 0x7F,0xBF,0xDF,0xEF,0xF7,0xFB,0xFD, // JJJJJJJK * 8 + 0xFC,0x7E,0xBF,0xDF,0xEF,0xF7,0xFB,0xFD,0x7E // {JKKKKKKK * 10}, JK +}; + +//------------------------------------------------------------------------------ +// Internal Functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Disables the BIAS of the USB controller +//------------------------------------------------------------------------------ +static inline void UDPHS_DisableBIAS( void ) +{ + // For CAP9, SAM9RL, HS +#if !defined (BOARD_USB_NO_BIAS_COMMAND) + AT91C_BASE_PMC->PMC_UCKR &= ~AT91C_CKGR_BIASEN_ENABLED; +#endif +} + +//------------------------------------------------------------------------------ +/// Enables the BIAS of the USB controller +//------------------------------------------------------------------------------ +static inline void UDPHS_EnableBIAS( void ) +{ + // For CAP9, SAM9RL, HS +#if !defined (BOARD_USB_NO_BIAS_COMMAND) + UDPHS_DisableBIAS(); + AT91C_BASE_PMC->PMC_UCKR |= AT91C_CKGR_BIASEN_ENABLED; +#endif +} + +//------------------------------------------------------------------------------ +/// Enable UDPHS clock +//------------------------------------------------------------------------------ +static inline void UDPHS_EnableUsbClock( void ) +{ +#if !defined (PMC_BY_HARD) + AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_UDPHS); + // Enable 480MHZ + AT91C_BASE_CKGR->CKGR_UCKR |= (AT91C_CKGR_PLLCOUNT & (3 << 20)) | AT91C_CKGR_UPLLEN; + // Wait until UTMI PLL is locked + while ((AT91C_BASE_PMC->PMC_SR & AT91C_PMC_LOCKU) == 0); +#endif +} + +//------------------------------------------------------------------------------ +/// Disable UDPHS clock +//------------------------------------------------------------------------------ +static inline void UDPHS_DisableUsbClock( void ) +{ +#if !defined (PMC_BY_HARD) + AT91C_BASE_PMC->PMC_PCDR = (1 << AT91C_ID_UDPHS); + // 480MHZ + AT91C_BASE_CKGR->CKGR_UCKR &= ~AT91C_CKGR_UPLLEN; +#endif +} + +//------------------------------------------------------------------------------ +/// Handles a completed transfer on the given endpoint, invoking the +/// configured callback if any. +/// \param bEndpoint Number of the endpoint for which the transfer has completed. +/// \param bStatus Status code returned by the transfer operation +//------------------------------------------------------------------------------ +static void UDPHS_EndOfTransfer( unsigned char bEndpoint, char bStatus ) +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + Transfer *pTransfer = &(pEndpoint->transfer); + + // Check that endpoint was sending or receiving data + if( (pEndpoint->state == UDP_ENDPOINT_RECEIVING) + || (pEndpoint->state == UDP_ENDPOINT_SENDING) ) { + + TRACE_DEBUG_WP("Eo"); + + // Endpoint returns in Idle state + pEndpoint->state = UDP_ENDPOINT_IDLE; + + // Invoke callback is present + if (pTransfer->fCallback != 0) { + + ((TransferCallback) pTransfer->fCallback) + (pTransfer->pArgument, + bStatus, + pTransfer->transferred, + pTransfer->remaining + pTransfer->buffered); + } + else { + TRACE_DEBUG_WP("No callBack\n\r"); + } + } +} + +//------------------------------------------------------------------------------ +/// Clears the correct RX flag in endpoint status register +/// \param bEndpoint Index of endpoint +//------------------------------------------------------------------------------ +static void UDPHS_ClearRxFlag( unsigned char bEndpoint ) +{ + AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTCLRSTA = AT91C_UDPHS_RX_BK_RDY; +} + +//------------------------------------------------------------------------------ +/// Transfers a data payload from the current tranfer buffer to the endpoint +/// FIFO +/// \param bEndpoint Number of the endpoint which is sending data. +//------------------------------------------------------------------------------ +static void UDPHS_WritePayload( unsigned char bEndpoint ) +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + Transfer *pTransfer = &(pEndpoint->transfer); + char *pFifo; + signed int size; + unsigned int dCtr; + + pFifo = (char*)((unsigned int *)AT91C_BASE_UDPHS_EPTFIFO + (16384 * bEndpoint)); + + // Get the number of bytes to send + size = pEndpoint->size; + if (size > pTransfer->remaining) { + + size = pTransfer->remaining; + } + + // Update transfer descriptor information + pTransfer->buffered += size; + pTransfer->remaining -= size; + + // Write packet in the FIFO buffer + dCtr = 0; + while (size > 0) { + + pFifo[dCtr] = *(pTransfer->pData); + pTransfer->pData++; + size--; + dCtr++; + } +} + +//------------------------------------------------------------------------------ +/// Transfers a data payload from an endpoint FIFO to the current transfer buffer +/// \param bEndpoint Endpoint number. +/// \param wPacketSize Size of received data packet +//------------------------------------------------------------------------------ +static void UDPHS_ReadPayload( unsigned char bEndpoint, int wPacketSize ) +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + Transfer *pTransfer = &(pEndpoint->transfer); + char *pFifo; + unsigned char dBytes=0; + + pFifo = (char*)((unsigned int *)AT91C_BASE_UDPHS_EPTFIFO + (16384 * bEndpoint)); + + // Check that the requested size is not bigger than the remaining transfer + if (wPacketSize > pTransfer->remaining) { + + pTransfer->buffered += wPacketSize - pTransfer->remaining; + wPacketSize = pTransfer->remaining; + } + + // Update transfer descriptor information + pTransfer->remaining -= wPacketSize; + pTransfer->transferred += wPacketSize; + + // Retrieve packet + while (wPacketSize > 0) { + + *(pTransfer->pData) = pFifo[dBytes]; + pTransfer->pData++; + wPacketSize--; + dBytes++; + } +} + + +//------------------------------------------------------------------------------ +/// Received SETUP packet from endpoint 0 FIFO +/// \param pRequest Generic USB SETUP request sent over Control endpoints +//------------------------------------------------------------------------------ +static void UDPHS_ReadRequest( USBGenericRequest *pRequest ) +{ + unsigned int *pData = (unsigned int *)pRequest; + unsigned int fifo; + + fifo = (AT91C_BASE_UDPHS_EPTFIFO->UDPHS_READEPT0[0]); + *pData = fifo; + fifo = (AT91C_BASE_UDPHS_EPTFIFO->UDPHS_READEPT0[0]); + pData++; + *pData = fifo; + //TRACE_ERROR("SETUP: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n\r", pData[0],pData[1],pData[2],pData[3],pData[4],pData[5],pData[6],pData[7]); +} + +//------------------------------------------------------------------------------ +/// Reset all endpoint transfer descriptors +//------------------------------------------------------------------------------ +static void UDPHS_ResetEndpoints( void ) +{ + Endpoint *pEndpoint; + Transfer *pTransfer; + unsigned char bEndpoint; + + // Reset the transfer descriptor of every endpoint + for( bEndpoint = 0; bEndpoint < BOARD_USB_NUMENDPOINTS; bEndpoint++ ) { + + pEndpoint = &(endpoints[bEndpoint]); + pTransfer = &(pEndpoint->transfer); + + // Reset endpoint transfer descriptor + pTransfer->pData = 0; + pTransfer->transferred = -1; + pTransfer->buffered = -1; + pTransfer->remaining = -1; + pTransfer->fCallback = 0; + pTransfer->pArgument = 0; + + // Reset endpoint state + pEndpoint->bank = 0; + pEndpoint->state = UDP_ENDPOINT_DISABLED; + } +} + + +//------------------------------------------------------------------------------ +/// Disable all endpoints (except control endpoint 0), aborting current +/// transfers if necessary +//------------------------------------------------------------------------------ +static void UDPHS_DisableEndpoints( void ) +{ + unsigned char bEndpoint; + + // Disable each endpoint, terminating any pending transfer + // Control endpoint 0 is not disabled + for( bEndpoint = 1; bEndpoint < BOARD_USB_NUMENDPOINTS; bEndpoint++ ) { + + UDPHS_EndOfTransfer( bEndpoint, USBD_STATUS_ABORTED ); + endpoints[bEndpoint].state = UDP_ENDPOINT_DISABLED; + } +} + +//------------------------------------------------------------------------------ +/// Endpoint interrupt handler. +/// Handle IN/OUT transfers, received SETUP packets and STALLing +/// \param bEndpoint Index of endpoint +//------------------------------------------------------------------------------ +static void UDPHS_EndpointHandler( unsigned char bEndpoint ) +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + Transfer *pTransfer = &(pEndpoint->transfer); + unsigned int status = AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTSTA; + unsigned short wPacketSize; + USBGenericRequest request; + + TRACE_DEBUG_WP("E%d ", bEndpoint); + TRACE_DEBUG_WP("st:0x%X ", status); + + // Handle interrupts + // IN packet sent + if( (AT91C_UDPHS_TX_PK_RDY == (AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTCTL & AT91C_UDPHS_TX_PK_RDY)) + && (0 == (status & AT91C_UDPHS_TX_PK_RDY )) ) { + + TRACE_DEBUG_WP("Wr "); + + // Check that endpoint was in Sending state + if( pEndpoint->state == UDP_ENDPOINT_SENDING ) { + + if (pTransfer->buffered > 0) { + pTransfer->transferred += pTransfer->buffered; + pTransfer->buffered = 0; + } + + if( ((pTransfer->buffered)==0) + &&((pTransfer->transferred)==0) + &&((pTransfer->remaining)==0) + &&(sendZLP == 0)) { + sendZLP = 1; + } + + // End of transfer ? + if( (pTransfer->remaining > 0) + ||(sendZLP == 1)) { + TRACE_DEBUG_WP("\n\r1pTransfer->buffered %d \n\r", pTransfer->buffered); + TRACE_DEBUG_WP("1pTransfer->transferred %d \n\r", pTransfer->transferred); + TRACE_DEBUG_WP("1pTransfer->remaining %d \n\r", pTransfer->remaining); + + // Transfer remaining data + TRACE_DEBUG_WP(" %d ", pEndpoint->size); + + // Send next packet + UDPHS_WritePayload(bEndpoint); + AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTSETSTA = AT91C_UDPHS_TX_PK_RDY; + sendZLP = 2; + } + else { + TRACE_DEBUG_WP("\n\r0pTransfer->buffered %d \n\r", pTransfer->buffered); + TRACE_DEBUG_WP("0pTransfer->transferred %d \n\r", pTransfer->transferred); + TRACE_DEBUG_WP("0pTransfer->remaining %d \n\r", pTransfer->remaining); + + TRACE_DEBUG_WP(" %d ", pTransfer->transferred); + + // Disable interrupt if this is not a control endpoint + if( AT91C_UDPHS_EPT_TYPE_CTL_EPT != (AT91C_UDPHS_EPT_TYPE&(AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTCFG)) ) { + + AT91C_BASE_UDPHS->UDPHS_IEN &= ~(1<UDPHS_EPT[bEndpoint].UDPHS_EPTCTLDIS = AT91C_UDPHS_TX_PK_RDY; + + UDPHS_EndOfTransfer(bEndpoint, USBD_STATUS_SUCCESS); + sendZLP = 0; + } + } + else { + + TRACE_ERROR("Error Wr"); + } + } + + // OUT packet received + if( AT91C_UDPHS_RX_BK_RDY == (status & AT91C_UDPHS_RX_BK_RDY) ) { + + TRACE_DEBUG_WP("Rd "); + + // Check that the endpoint is in Receiving state + if (pEndpoint->state != UDP_ENDPOINT_RECEIVING) { + + // Check if an ACK has been received on a Control endpoint + if( (0 == (AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTCFG & AT91C_UDPHS_EPT_TYPE)) + && (0 == (status & AT91C_UDPHS_BYTE_COUNT)) ) { + + // Control endpoint, 0 bytes received + // Acknowledge the data and finish the current transfer + TRACE_DEBUG_WP("Ack "); + UDPHS_ClearRxFlag(bEndpoint); + UDPHS_EndOfTransfer(bEndpoint, USBD_STATUS_SUCCESS); + } + // Check if the data has been STALLed + else if( AT91C_UDPHS_FRCESTALL == (status & AT91C_UDPHS_FRCESTALL)) { + + // Discard STALLed data + TRACE_DEBUG_WP("Discard "); + UDPHS_ClearRxFlag(bEndpoint); + } + // NAK the data + else { + + TRACE_DEBUG_WP("Nak "); + AT91C_BASE_UDPHS->UDPHS_IEN &= ~(1<>20); + + TRACE_DEBUG_WP("%d ", wPacketSize); + UDPHS_ReadPayload(bEndpoint, wPacketSize); + UDPHS_ClearRxFlag(bEndpoint); + + // Check if the transfer is finished + if ((pTransfer->remaining == 0) || (wPacketSize < pEndpoint->size)) { + + AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTCTLDIS = AT91C_UDPHS_RX_BK_RDY; + + // Disable interrupt if this is not a control endpoint + if( AT91C_UDPHS_EPT_TYPE_CTL_EPT != (AT91C_UDPHS_EPT_TYPE & (AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTCFG)) ) { + + AT91C_BASE_UDPHS->UDPHS_IEN &= ~(1<UDPHS_EPT[bEndpoint].UDPHS_EPTCLRSTA = AT91C_UDPHS_STALL_SNT; + + // If the endpoint is not halted, clear the STALL condition + if (pEndpoint->state != UDP_ENDPOINT_HALTED) { + + TRACE_WARNING( "_ " ); + AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTCLRSTA = AT91C_UDPHS_FRCESTALL; + } + } + + // SETUP packet received + if( AT91C_UDPHS_RX_SETUP == (status & AT91C_UDPHS_RX_SETUP) ) { + + TRACE_DEBUG_WP("Stp "); + + // If a transfer was pending, complete it + // Handles the case where during the status phase of a control write + // transfer, the host receives the device ZLP and ack it, but the ack + // is not received by the device + if ((pEndpoint->state == UDP_ENDPOINT_RECEIVING) + || (pEndpoint->state == UDP_ENDPOINT_SENDING)) { + + UDPHS_EndOfTransfer(bEndpoint, USBD_STATUS_SUCCESS); + } + // Copy the setup packet + UDPHS_ReadRequest(&request); + + // Acknowledge setup packet + AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTCLRSTA = AT91C_UDPHS_RX_SETUP; + + // Forward the request to the upper layer + USBDCallbacks_RequestReceived(&request); + } + +} + +//------------------------------------------------------------------------------ +// Interrupt service routine +//------------------------------------------------------------------------------ +#ifdef DMA +//---------------------------------------------------------------------------- +/// Endpoint DMA interrupt handler. +/// This function (ISR) handles dma interrupts +/// \param bEndpoint Index of endpoint +//---------------------------------------------------------------------------- +static void UDPHS_DmaHandler( unsigned char bEndpoint ) +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + Transfer *pTransfer = &(pEndpoint->transfer); + int justTransferred; + unsigned int status; + unsigned char result = USBD_STATUS_SUCCESS; + + status = AT91C_BASE_UDPHS->UDPHS_DMA[bEndpoint].UDPHS_DMASTATUS; + TRACE_DEBUG_WP("Dma Ept%d ", bEndpoint); + + // Disable DMA interrupt to avoid receiving 2 interrupts (B_EN and TR_EN) + AT91C_BASE_UDPHS->UDPHS_DMA[bEndpoint].UDPHS_DMACONTROL &= + ~(AT91C_UDPHS_END_TR_EN | AT91C_UDPHS_END_B_EN); + + AT91C_BASE_UDPHS->UDPHS_IEN &= ~(1 << SHIFT_DMA << bEndpoint); + + if( AT91C_UDPHS_END_BF_ST == (status & AT91C_UDPHS_END_BF_ST) ) { + + TRACE_DEBUG_WP("EndBuffer "); + + // BUFF_COUNT holds the number of untransmitted bytes. + // BUFF_COUNT is equal to zero in case of good transfer + justTransferred = pTransfer->buffered + - ((status & AT91C_UDPHS_BUFF_COUNT) >> 16); + pTransfer->transferred += justTransferred; + + pTransfer->buffered = ((status & AT91C_UDPHS_BUFF_COUNT) >> 16); + + pTransfer->remaining -= justTransferred; + + TRACE_DEBUG_WP("\n\r1pTransfer->buffered %d \n\r", pTransfer->buffered); + TRACE_DEBUG_WP("1pTransfer->transferred %d \n\r", pTransfer->transferred); + TRACE_DEBUG_WP("1pTransfer->remaining %d \n\r", pTransfer->remaining); + + if( (pTransfer->remaining + pTransfer->buffered) > 0 ) { + + // Prepare an other transfer + if( pTransfer->remaining > DMA_MAX_FIFO_SIZE ) { + + pTransfer->buffered = DMA_MAX_FIFO_SIZE; + } + else { + pTransfer->buffered = pTransfer->remaining; + } + + AT91C_BASE_UDPHS->UDPHS_DMA[bEndpoint].UDPHS_DMAADDRESS = + (unsigned int)((pTransfer->pData) + (pTransfer->transferred)); + + // Clear unwanted interrupts + AT91C_BASE_UDPHS->UDPHS_DMA[bEndpoint].UDPHS_DMASTATUS; + + // Enable DMA endpoint interrupt + AT91C_BASE_UDPHS->UDPHS_IEN |= (1 << SHIFT_DMA << bEndpoint); + // DMA config for receive the good size of buffer, or an error buffer + + AT91C_BASE_UDPHS->UDPHS_DMA[bEndpoint].UDPHS_DMACONTROL = 0; // raz + AT91C_BASE_UDPHS->UDPHS_DMA[bEndpoint].UDPHS_DMACONTROL = + ( ((pTransfer->buffered << 16) & AT91C_UDPHS_BUFF_COUNT) + | AT91C_UDPHS_END_TR_EN + | AT91C_UDPHS_END_TR_IT + | AT91C_UDPHS_END_B_EN + | AT91C_UDPHS_END_BUFFIT + | AT91C_UDPHS_CHANN_ENB ); + } + } + else if( AT91C_UDPHS_END_TR_ST == (status & AT91C_UDPHS_END_TR_ST) ) { + + TRACE_DEBUG_WP("EndTransf "); + + pTransfer->transferred = pTransfer->buffered + - ((status & AT91C_UDPHS_BUFF_COUNT) >> 16); + pTransfer->remaining = 0; + TRACE_DEBUG_WP("\n\r0pTransfer->buffered %d \n\r", pTransfer->buffered); + TRACE_DEBUG_WP("0pTransfer->transferred %d \n\r", pTransfer->transferred); + TRACE_DEBUG_WP("0pTransfer->remaining %d \n\r", pTransfer->remaining); + } + else { + + TRACE_ERROR("UDPHS_DmaHandler: Error (0x%08X)\n\r", status); + result = USBD_STATUS_ABORTED; + } + + // Invoke callback + if( pTransfer->remaining == 0 ) { + + TRACE_DEBUG_WP("EOT "); + UDPHS_EndOfTransfer(bEndpoint, result); + } +} +#endif + + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// USB interrupt handler +/// Manages device resume, suspend, end of bus reset. +/// Forwards endpoint interrupts to the appropriate handler. +//------------------------------------------------------------------------------ +void USBD_InterruptHandler(void) +{ + unsigned int status; + unsigned char numIT; + + if (deviceState >= USBD_STATE_POWERED) { + + LED_Set(USBD_LEDUSB); + } + + // Get interrupts status + status = AT91C_BASE_UDPHS->UDPHS_INTSTA & AT91C_BASE_UDPHS->UDPHS_IEN; + + // Handle all UDPHS interrupts + TRACE_DEBUG_WP("H"); + while (status != 0) { + + // Start Of Frame (SOF) + if ((status & AT91C_UDPHS_IEN_SOF) != 0) { + + TRACE_DEBUG_WP("SOF "); + + // Invoke the SOF callback + //USB_StartOfFrameCallback(pUsb); + + // Acknowledge interrupt + AT91C_BASE_UDPHS->UDPHS_CLRINT = AT91C_UDPHS_IEN_SOF; + status &= ~AT91C_UDPHS_IEN_SOF; + } + // Suspend + // This interrupt is always treated last (hence the '==') + else if (status == AT91C_UDPHS_DET_SUSPD) { + + TRACE_DEBUG_WP("S"); + + // The device enters the Suspended state + // MCK + UDPCK must be off + // Pull-Up must be connected + // Transceiver must be disabled + + LED_Clear(USBD_LEDUSB); + + UDPHS_DisableBIAS(); + + // Enable wakeup + AT91C_BASE_UDPHS->UDPHS_IEN |= AT91C_UDPHS_WAKE_UP | AT91C_UDPHS_ENDOFRSM; + AT91C_BASE_UDPHS->UDPHS_IEN &= ~AT91C_UDPHS_DET_SUSPD; + + // Acknowledge interrupt + AT91C_BASE_UDPHS->UDPHS_CLRINT = AT91C_UDPHS_DET_SUSPD | AT91C_UDPHS_WAKE_UP; + previousDeviceState = deviceState; + deviceState = USBD_STATE_SUSPENDED; + UDPHS_DisableUsbClock(); + + // Invoke the Suspend callback + USBDCallbacks_Suspended(); + } + // Resume + else if( ((status & AT91C_UDPHS_WAKE_UP) != 0) // line activity + || ((status & AT91C_UDPHS_ENDOFRSM) != 0)) { // pc wakeup + +//JCB +#ifdef NOT_DEFINED +#if !defined(PIN_USB_VBUS) + // Configure PIO + PIO_Configure(&pinVbus, 1); + + // Check current level on VBus + if (PIO_Get(&pinVbus) == 1) // Protection +#endif +#endif + { + // Invoke the Resume callback + USBDCallbacks_Resumed(); + + TRACE_DEBUG_WP("R"); + + UDPHS_EnableUsbClock(); + UDPHS_EnableBIAS(); + + // The device enters Configured state + // MCK + UDPCK must be on + // Pull-Up must be connected + // Transceiver must be enabled + + deviceState = previousDeviceState; + + AT91C_BASE_UDPHS->UDPHS_CLRINT = AT91C_UDPHS_WAKE_UP | AT91C_UDPHS_ENDOFRSM | AT91C_UDPHS_DET_SUSPD; + + AT91C_BASE_UDPHS->UDPHS_IEN |= AT91C_UDPHS_ENDOFRSM | AT91C_UDPHS_DET_SUSPD; + AT91C_BASE_UDPHS->UDPHS_CLRINT = AT91C_UDPHS_WAKE_UP | AT91C_UDPHS_ENDOFRSM; + AT91C_BASE_UDPHS->UDPHS_IEN &= ~AT91C_UDPHS_WAKE_UP; + } +// jcb !!! +#ifdef NOT_DEFINED +#if !defined(PIN_USB_VBUS) + else { + + // No VBUS + // Disconnect the pull-up + USBD_Disconnect(); + AT91C_BASE_UDPHS->UDPHS_CLRINT = AT91C_UDPHS_WAKE_UP; + } +#endif +#endif + } + // End of bus reset + else if ((status & AT91C_UDPHS_ENDRESET) == AT91C_UDPHS_ENDRESET) { + +// TRACE_DEBUG_WP("EoB "); + + // The device enters the Default state + deviceState = USBD_STATE_DEFAULT; + // MCK + UDPCK are already enabled + // Pull-Up is already connected + // Transceiver must be enabled + // Endpoint 0 must be enabled + + UDPHS_ResetEndpoints(); + UDPHS_DisableEndpoints(); + USBD_ConfigureEndpoint(0); + + // Flush and enable the Suspend interrupt + AT91C_BASE_UDPHS->UDPHS_CLRINT = AT91C_UDPHS_WAKE_UP | AT91C_UDPHS_DET_SUSPD; + + //// Enable the Start Of Frame (SOF) interrupt if needed + //if (pCallbacks->startOfFrame != 0) + //{ + // AT91C_BASE_UDPHS->UDPHS_IEN |= AT91C_UDPHS_IEN_SOF; + //} + + // Invoke the Reset callback + USBDCallbacks_Reset(); + + // Acknowledge end of bus reset interrupt + AT91C_BASE_UDPHS->UDPHS_CLRINT = AT91C_UDPHS_ENDRESET; + + AT91C_BASE_UDPHS->UDPHS_IEN |= AT91C_UDPHS_DET_SUSPD; + } + // Handle upstream resume interrupt + else if (status & AT91C_UDPHS_UPSTR_RES) { + + TRACE_DEBUG_WP("ExtRes "); + + // - Acknowledge the IT + AT91C_BASE_UDPHS->UDPHS_CLRINT = AT91C_UDPHS_UPSTR_RES; + } + // Endpoint interrupts + else { +#ifndef DMA + // Handle endpoint interrupts + for (numIT = 0; numIT < NUM_IT_MAX; numIT++) { + + if ((status & (1 << SHIFT_INTERUPT << numIT)) != 0) { + + UDPHS_EndpointHandler(numIT); + } + } +#else + // Handle endpoint control interrupt + if ((status & (1 << SHIFT_INTERUPT << 0)) != 0) { + + UDPHS_EndpointHandler( 0 ); + } + else { + + numIT = 1; + while((status&(0x7E<UDPHS_INTSTA & AT91C_BASE_UDPHS->UDPHS_IEN; + + TRACE_DEBUG_WP("\n\r"); + if (status != 0) { + + TRACE_DEBUG_WP(" - "); + } + } + + if (deviceState >= USBD_STATE_POWERED) { + + LED_Clear(USBD_LEDUSB); + } +} + +//------------------------------------------------------------------------------ +/// Configure an endpoint with the provided endpoint descriptor +/// \param pDdescriptor Pointer to the endpoint descriptor +//------------------------------------------------------------------------------ +void USBD_ConfigureEndpoint(const USBEndpointDescriptor *pDescriptor) +{ + Endpoint *pEndpoint; + unsigned char bEndpoint; + unsigned char bType; + unsigned char bEndpointDir; + unsigned char bSizeEpt = 0; + + // NULL descriptor -> Control endpoint 0 + if (pDescriptor == 0) { + + bEndpoint = 0; + pEndpoint = &(endpoints[bEndpoint]); + bType = USBEndpointDescriptor_CONTROL; + bEndpointDir = 0; + pEndpoint->size = BOARD_USB_ENDPOINTS_MAXPACKETSIZE(0); + pEndpoint->bank = BOARD_USB_ENDPOINTS_BANKS(0); + } + else { + + // The endpoint number + bEndpoint = USBEndpointDescriptor_GetNumber(pDescriptor); + pEndpoint = &(endpoints[bEndpoint]); + // Transfer type: Control, Isochronous, Bulk, Interrupt + bType = USBEndpointDescriptor_GetType(pDescriptor); + // Direction, ignored for control endpoints + bEndpointDir = USBEndpointDescriptor_GetDirection(pDescriptor); + pEndpoint->size = USBEndpointDescriptor_GetMaxPacketSize(pDescriptor); + pEndpoint->bank = BOARD_USB_ENDPOINTS_BANKS(bEndpoint); + } + + // Abort the current transfer is the endpoint was configured and in + // Write or Read state + if( (pEndpoint->state == UDP_ENDPOINT_RECEIVING) + || (pEndpoint->state == UDP_ENDPOINT_SENDING) ) { + + UDPHS_EndOfTransfer(bEndpoint, USBD_STATUS_RESET); + } + pEndpoint->state = UDP_ENDPOINT_IDLE; + + // Disable endpoint + AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTCTLDIS = AT91C_UDPHS_SHRT_PCKT + | AT91C_UDPHS_BUSY_BANK + | AT91C_UDPHS_NAK_OUT + | AT91C_UDPHS_NAK_IN + | AT91C_UDPHS_STALL_SNT + | AT91C_UDPHS_RX_SETUP + | AT91C_UDPHS_TX_PK_RDY + | AT91C_UDPHS_TX_COMPLT + | AT91C_UDPHS_RX_BK_RDY + | AT91C_UDPHS_ERR_OVFLW + | AT91C_UDPHS_MDATA_RX + | AT91C_UDPHS_DATAX_RX + | AT91C_UDPHS_NYET_DIS + | AT91C_UDPHS_INTDIS_DMA + | AT91C_UDPHS_AUTO_VALID + | AT91C_UDPHS_EPT_DISABL; + + // Reset Endpoint Fifos + AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTCLRSTA = AT91C_UDPHS_TOGGLESQ | AT91C_UDPHS_FRCESTALL; + AT91C_BASE_UDPHS->UDPHS_EPTRST = 1<size <= 8 ) { + bSizeEpt = 0; + } + else if ( pEndpoint->size <= 16 ) { + bSizeEpt = 1; + } + else if ( pEndpoint->size <= 32 ) { + bSizeEpt = 2; + } + else if ( pEndpoint->size <= 64 ) { + bSizeEpt = 3; + } + else if ( pEndpoint->size <= 128 ) { + bSizeEpt = 4; + } + else if ( pEndpoint->size <= 256 ) { + bSizeEpt = 5; + } + else if ( pEndpoint->size <= 512 ) { + bSizeEpt = 6; + } + else if ( pEndpoint->size <= 1024 ) { + bSizeEpt = 7; + } //else { + // sizeEpt = 0; // control endpoint + //} + + // Configure endpoint + if (bType == USBEndpointDescriptor_CONTROL) { + + // Enable endpoint IT for control endpoint + AT91C_BASE_UDPHS->UDPHS_IEN |= (1<UDPHS_EPT[bEndpoint].UDPHS_EPTCFG = bSizeEpt + | (bEndpointDir << 3) + | (bType << 4) + | ((pEndpoint->bank) << 6); + + while( (signed int)AT91C_UDPHS_EPT_MAPD != (signed int)((AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTCFG) & AT91C_UDPHS_EPT_MAPD) ) { + + // resolved by clearing the reset IT in good place + TRACE_ERROR("PB bEndpoint: 0x%X\n\r", bEndpoint); + TRACE_ERROR("PB bSizeEpt: 0x%X\n\r", bSizeEpt); + TRACE_ERROR("PB bEndpointDir: 0x%X\n\r", bEndpointDir); + TRACE_ERROR("PB bType: 0x%X\n\r", bType); + TRACE_ERROR("PB pEndpoint->bank: 0x%X\n\r", pEndpoint->bank); + TRACE_ERROR("PB UDPHS_EPTCFG: 0x%X\n\r", AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTCFG); + for(;;); + } + + if (bType == USBEndpointDescriptor_CONTROL) { + + AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTCTLENB = AT91C_UDPHS_RX_BK_RDY + | AT91C_UDPHS_RX_SETUP + | AT91C_UDPHS_EPT_ENABL; + } + else { +#ifndef DMA + AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTCTLENB = AT91C_UDPHS_EPT_ENABL; +#else + AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTCTLENB = AT91C_UDPHS_AUTO_VALID + | AT91C_UDPHS_EPT_ENABL; +#endif + } + +} + +//------------------------------------------------------------------------------ +/// Sends data through an USB endpoint (IN) +/// Sets up the transfer descriptor, write one or two data payloads +/// (depending on the number of FIFO banks for the endpoint) and then +/// starts the actual transfer. The operation is complete when all +/// the data has been sent. +/// \param bEndpoint Index of endpoint +/// \param *pData Data to be written +/// \param dLength Data length to be send +/// \param fCallback Callback to be call after the success command +/// \param *pArgument Callback argument +/// \return USBD_STATUS_LOCKED or USBD_STATUS_SUCCESS +//------------------------------------------------------------------------------ +char USBD_Write( unsigned char bEndpoint, + const void *pData, + unsigned int dLength, + TransferCallback fCallback, + void *pArgument ) +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + Transfer *pTransfer = &(pEndpoint->transfer); + + // Return if the endpoint is not in IDLE state + if (pEndpoint->state != UDP_ENDPOINT_IDLE) { + + return USBD_STATUS_LOCKED; + } + + TRACE_DEBUG_WP("Write%d(%d) ", bEndpoint, dLength); + + // Setup the transfer descriptor + pTransfer->pData = (void *) pData; + pTransfer->remaining = dLength; + pTransfer->buffered = 0; + pTransfer->transferred = 0; + pTransfer->fCallback = fCallback; + pTransfer->pArgument = pArgument; + + // Send one packet + pEndpoint->state = UDP_ENDPOINT_SENDING; + +#ifdef DMA + // Test if endpoint type control + if(AT91C_UDPHS_EPT_TYPE_CTL_EPT == (AT91C_UDPHS_EPT_TYPE&(AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTCFG))) { +#endif + // Enable endpoint IT + AT91C_BASE_UDPHS->UDPHS_IEN |= (1 << SHIFT_INTERUPT << bEndpoint); + AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTCTLENB = AT91C_UDPHS_TX_PK_RDY; + +#ifdef DMA + } + else { + + if( pTransfer->remaining == 0 ) { + // DMA not handle ZLP + AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTSETSTA = AT91C_UDPHS_TX_PK_RDY; + // Enable endpoint IT + AT91C_BASE_UDPHS->UDPHS_IEN |= (1 << SHIFT_INTERUPT << bEndpoint); + AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTCTLENB = AT91C_UDPHS_TX_PK_RDY; + } + else { + // Others endpoints (not control) + if( pTransfer->remaining > DMA_MAX_FIFO_SIZE ) { + + // Transfer the max + pTransfer->buffered = DMA_MAX_FIFO_SIZE; + } + else { + // Transfer the good size + pTransfer->buffered = pTransfer->remaining; + } + + TRACE_DEBUG_WP("\n\r_WR:%d ", pTransfer->remaining ); + TRACE_DEBUG_WP("B:%d ", pTransfer->buffered ); + TRACE_DEBUG_WP("T:%d ", pTransfer->transferred ); + + AT91C_BASE_UDPHS->UDPHS_DMA[bEndpoint].UDPHS_DMAADDRESS = (unsigned int)(pTransfer->pData); + + // Clear unwanted interrupts + AT91C_BASE_UDPHS->UDPHS_DMA[bEndpoint].UDPHS_DMASTATUS; + // Enable DMA endpoint interrupt + AT91C_BASE_UDPHS->UDPHS_IEN |= (1 << SHIFT_DMA << bEndpoint); + // DMA config + AT91C_BASE_UDPHS->UDPHS_DMA[bEndpoint].UDPHS_DMACONTROL = 0; // raz + AT91C_BASE_UDPHS->UDPHS_DMA[bEndpoint].UDPHS_DMACONTROL = + ( ((pTransfer->buffered << 16) & AT91C_UDPHS_BUFF_COUNT) + | AT91C_UDPHS_END_B_EN + | AT91C_UDPHS_END_BUFFIT + | AT91C_UDPHS_CHANN_ENB ); + } + } +#endif + + return USBD_STATUS_SUCCESS; +} + +//------------------------------------------------------------------------------ +/// Reads incoming data on an USB endpoint (OUT) +/// \param bEndpoint Index of endpoint +/// \param *pData Data to be readen +/// \param dLength Data length to be receive +/// \param fCallback Callback to be call after the success command +/// \param *pArgument Callback argument +/// \return USBD_STATUS_LOCKED or USBD_STATUS_SUCCESS +//------------------------------------------------------------------------------ +char USBD_Read( unsigned char bEndpoint, + void *pData, + unsigned int dLength, + TransferCallback fCallback, + void *pArgument ) +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + Transfer *pTransfer = &(pEndpoint->transfer); + + // Return if the endpoint is not in IDLE state + if (pEndpoint->state != UDP_ENDPOINT_IDLE) { + + return USBD_STATUS_LOCKED; + } + + TRACE_DEBUG_WP("Read%d(%d) ", bEndpoint, dLength); + + // Endpoint enters Receiving state + pEndpoint->state = UDP_ENDPOINT_RECEIVING; + + // Set the transfer descriptor + pTransfer->pData = pData; + pTransfer->remaining = dLength; + pTransfer->buffered = 0; + pTransfer->transferred = 0; + pTransfer->fCallback = fCallback; + pTransfer->pArgument = pArgument; + +#ifdef DMA + // Test if endpoint type control + if(AT91C_UDPHS_EPT_TYPE_CTL_EPT == (AT91C_UDPHS_EPT_TYPE&(AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTCFG))) { +#endif + // Control endpoint + // Enable endpoint IT + AT91C_BASE_UDPHS->UDPHS_IEN |= (1 << SHIFT_INTERUPT << bEndpoint); + AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTCTLENB = AT91C_UDPHS_RX_BK_RDY; +#ifdef DMA + } + else { + + TRACE_DEBUG_WP("Read%d(%d) ", bEndpoint, dLength); + + // Others endpoints (not control) + if( pTransfer->remaining > DMA_MAX_FIFO_SIZE ) { + + // Transfer the max + pTransfer->buffered = DMA_MAX_FIFO_SIZE; + } + else { + // Transfer the good size + pTransfer->buffered = pTransfer->remaining; + } + + AT91C_BASE_UDPHS->UDPHS_DMA[bEndpoint].UDPHS_DMAADDRESS = (unsigned int)(pTransfer->pData); + + // Clear unwanted interrupts + AT91C_BASE_UDPHS->UDPHS_DMA[bEndpoint].UDPHS_DMASTATUS; + + // Enable DMA endpoint interrupt + AT91C_BASE_UDPHS->UDPHS_IEN |= (1 << SHIFT_DMA << bEndpoint); + + TRACE_DEBUG_WP("\n\r_RR:%d ", pTransfer->remaining ); + TRACE_DEBUG_WP("B:%d ", pTransfer->buffered ); + TRACE_DEBUG_WP("T:%d ", pTransfer->transferred ); + + // DMA config + AT91C_BASE_UDPHS->UDPHS_DMA[bEndpoint].UDPHS_DMACONTROL = 0; // raz + AT91C_BASE_UDPHS->UDPHS_DMA[bEndpoint].UDPHS_DMACONTROL = + ( ((pTransfer->buffered << 16) & AT91C_UDPHS_BUFF_COUNT) + | AT91C_UDPHS_END_TR_EN + | AT91C_UDPHS_END_TR_IT + | AT91C_UDPHS_END_B_EN + | AT91C_UDPHS_END_BUFFIT + | AT91C_UDPHS_CHANN_ENB ); + } +#endif + + return USBD_STATUS_SUCCESS; +} + +//------------------------------------------------------------------------------ +/// Put endpoint into Halt state +/// \param bEndpoint Index of endpoint +//------------------------------------------------------------------------------ +void USBD_Halt( unsigned char bEndpoint ) +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + + // Check that endpoint is enabled and not already in Halt state + if( (pEndpoint->state != UDP_ENDPOINT_DISABLED) + && (pEndpoint->state != UDP_ENDPOINT_HALTED) ) { + + TRACE_DEBUG_WP("Halt%d ", bEndpoint); + + // Abort the current transfer if necessary + UDPHS_EndOfTransfer(bEndpoint, USBD_STATUS_ABORTED); + + // Put endpoint into Halt state + AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTSETSTA = AT91C_UDPHS_FRCESTALL; + pEndpoint->state = UDP_ENDPOINT_HALTED; + +#ifdef DMA + // Test if endpoint type control + if(AT91C_UDPHS_EPT_TYPE_CTL_EPT == (AT91C_UDPHS_EPT_TYPE&(AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTCFG))) { +#endif + // Enable the endpoint interrupt + AT91C_BASE_UDPHS->UDPHS_IEN |= (1<UDPHS_IEN |= (1<state != UDP_ENDPOINT_DISABLED) { + + TRACE_DEBUG_WP("Unhalt%d ", bEndpoint); + + // Return endpoint to Idle state + pEndpoint->state = UDP_ENDPOINT_IDLE; + + // Clear FORCESTALL flag + AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTCLRSTA = AT91C_UDPHS_TOGGLESQ | AT91C_UDPHS_FRCESTALL; + + // Reset Endpoint Fifos + AT91C_BASE_UDPHS->UDPHS_EPTRST = (1<state == UDP_ENDPOINT_HALTED) { + status = 1; + } + return( status ); +} + +//------------------------------------------------------------------------------ +/// IS High Speed device working in High Speed ? +/// \return 1 if the device is in High Speed; otherwise 0 (Full Speed) +//------------------------------------------------------------------------------ +unsigned char USBD_IsHighSpeed( void ) +{ + unsigned char status = 0; + + if( AT91C_UDPHS_SPEED == (AT91C_BASE_UDPHS->UDPHS_INTSTA & AT91C_UDPHS_SPEED) ) + { + // High Speed + TRACE_DEBUG_WP("High Speed\n\r"); + status = 1; + } + else { + TRACE_DEBUG_WP("Full Speed\n\r"); + } + return( status ); +} + + +//------------------------------------------------------------------------------ +/// Causes the endpoint to acknowledge the next received packet with a STALL +/// handshake. +/// Further packets are then handled normally. +/// \param bEndpoint Index of endpoint +/// \return Operation result code: USBD_STATUS_LOCKED or USBD_STATUS_SUCCESS +//------------------------------------------------------------------------------ +unsigned char USBD_Stall( unsigned char bEndpoint ) +{ + Endpoint *pEndpoint = &(endpoints[bEndpoint]); + + // Check that endpoint is in Idle state + if (pEndpoint->state != UDP_ENDPOINT_IDLE) { + + TRACE_WARNING("UDP_Stall: Endpoint%d locked\n\r", bEndpoint); + return USBD_STATUS_LOCKED; + } + + TRACE_DEBUG_WP("Stall%d ", bEndpoint); + + AT91C_BASE_UDPHS->UDPHS_EPT[bEndpoint].UDPHS_EPTSETSTA = AT91C_UDPHS_FRCESTALL; + + return USBD_STATUS_SUCCESS; +} + +//------------------------------------------------------------------------------ +/// Activates a remote wakeup procedure +//------------------------------------------------------------------------------ +void USBD_RemoteWakeUp(void) +{ + TRACE_DEBUG_WP("Remote WakeUp\n\r"); + + // Device is currently suspended + if (deviceState == USBD_STATE_SUSPENDED) { + + TRACE_DEBUG_WP("RW\n\r"); + UDPHS_EnableUsbClock(); + + // Activates a remote wakeup + AT91C_BASE_UDPHS->UDPHS_CTRL |= AT91C_UDPHS_REWAKEUP; + + while ((AT91C_BASE_UDPHS->UDPHS_CTRL&AT91C_UDPHS_REWAKEUP) == AT91C_UDPHS_REWAKEUP) { + + TRACE_DEBUG_WP("W"); + } + UDPHS_EnableBIAS(); + } + // Device is NOT suspended + else { + + TRACE_WARNING("USBD_RemoteWakeUp: Device is not suspended\n\r"); + } +} + +//------------------------------------------------------------------------------ +/// Sets the device address +/// \param address Adress to be set +//------------------------------------------------------------------------------ +void USBD_SetAddress( unsigned char address ) +{ + TRACE_DEBUG_WP("SetAddr(%d) ", address); + + // Set address + AT91C_BASE_UDPHS->UDPHS_CTRL &= ~AT91C_UDPHS_DEV_ADDR; // RAZ Address + AT91C_BASE_UDPHS->UDPHS_CTRL |= address | AT91C_UDPHS_FADDR_EN; + + // If the address is 0, the device returns to the Default state + if (address == 0) { + deviceState = USBD_STATE_DEFAULT; + } + // If the address is non-zero, the device enters the Address state + else { + deviceState = USBD_STATE_ADDRESS; + } +} + +//------------------------------------------------------------------------------ +/// Changes the device state from Address to Configured, or from Configured +/// to Address. +/// This method directly access the last received SETUP packet to decide on +/// what to do. +/// \param cfgnum configuration number +//------------------------------------------------------------------------------ +void USBD_SetConfiguration( unsigned char cfgnum ) +{ + TRACE_DEBUG_WP("SetCfg(%d) ", cfgnum); + + // Check the request + if( cfgnum != 0 ) { + + // Enter Configured state + deviceState = USBD_STATE_CONFIGURED; + } + // If the configuration number is zero, the device goes back to the Address + // state + else { + + // Go back to Address state + deviceState = USBD_STATE_ADDRESS; + + // Abort all transfers + UDPHS_DisableEndpoints(); + } +} + +//------------------------------------------------------------------------------ +/// Enables the pull-up on the D+ line to connect the device to the USB. +//------------------------------------------------------------------------------ +void USBD_Connect( void ) +{ + TRACE_DEBUG_WP("Conn "); +#if defined(BOARD_USB_PULLUP_INTERNAL) + AT91C_BASE_UDPHS->UDPHS_CTRL &= ~AT91C_UDPHS_DETACH; // Pull Up on DP + AT91C_BASE_UDPHS->UDPHS_CTRL |= AT91C_UDPHS_PULLD_DIS; // Disable Pull Down + +#elif defined(BOARD_USB_PULLUP_INTERNAL_BY_MATRIX) + TRACE_DEBUG_WP("PUON 1\n\r"); + AT91C_BASE_MATRIX->MATRIX_USBPCR |= AT91C_MATRIX_USBPCR_PUON; + +#elif defined(BOARD_USB_PULLUP_EXTERNAL) + +#ifdef PIN_USB_PULLUP + const Pin pinPullUp = PIN_USB_PULLUP; + if( pinPullUp.attribute == PIO_OUTPUT_0 ) { + + PIO_Set(&pinPullUp); + } + else { + + PIO_Clear(&pinPullUp); + } +#else + #error unsupported now +#endif + +#elif !defined(BOARD_USB_PULLUP_ALWAYSON) + #error Unsupported pull-up type. + +#endif +} + +//------------------------------------------------------------------------------ +/// Disables the pull-up on the D+ line to disconnect the device from the bus. +//------------------------------------------------------------------------------ +void USBD_Disconnect( void ) +{ + TRACE_DEBUG_WP("Disc "); + +#if defined(BOARD_USB_PULLUP_INTERNAL) + AT91C_BASE_UDPHS->UDPHS_CTRL |= AT91C_UDPHS_DETACH; // detach + AT91C_BASE_UDPHS->UDPHS_CTRL &= ~AT91C_UDPHS_PULLD_DIS; // Enable Pull Down + +#elif defined(BOARD_USB_PULLUP_INTERNAL_BY_MATRIX) + AT91C_BASE_MATRIX->MATRIX_USBPCR &= ~AT91C_MATRIX_USBPCR_PUON; + +#elif defined(BOARD_USB_PULLUP_EXTERNAL) + +#ifdef PIN_USB_PULLUP + const Pin pinPullUp = PIN_USB_PULLUP; + if (pinPullUp.attribute == PIO_OUTPUT_0) { + + PIO_Clear(&pinPullUp); + } + else { + + PIO_Set(&pinPullUp); + } +#else + #error unsupported now +#endif + +#elif !defined(BOARD_USB_PULLUP_ALWAYSON) + #error Unsupported pull-up type. + +#endif + + // Device returns to the Powered state + if (deviceState > USBD_STATE_POWERED) { + + deviceState = USBD_STATE_POWERED; + } +} + +//------------------------------------------------------------------------------ +/// Certification test for High Speed device. +/// \param bIndex Test to be done +//------------------------------------------------------------------------------ +void USBD_Test( unsigned char bIndex ) +{ + char *pFifo; + unsigned char i; + + AT91C_BASE_UDPHS->UDPHS_IEN &= ~AT91C_UDPHS_DET_SUSPD; // remove suspend for TEST + AT91C_BASE_UDPHS->UDPHS_TST |= AT91C_UDPHS_SPEED_CFG_HS; // force High Speed (remove suspend) + + switch( bIndex ) { + + case USBFeatureRequest_TESTPACKET: + TRACE_DEBUG_WP("TEST_PACKET "); + + AT91C_BASE_UDPHS->UDPHS_DMA[1].UDPHS_DMACONTROL = 0; + AT91C_BASE_UDPHS->UDPHS_DMA[2].UDPHS_DMACONTROL = 0; + + // Configure endpoint 2, 64 bytes, direction IN, type BULK, 1 bank + AT91C_BASE_UDPHS->UDPHS_EPT[2].UDPHS_EPTCFG = AT91C_UDPHS_EPT_SIZE_64 | AT91C_UDPHS_EPT_DIR_IN | AT91C_UDPHS_EPT_TYPE_BUL_EPT | AT91C_UDPHS_BK_NUMBER_1; + while( (signed int)(AT91C_BASE_UDPHS->UDPHS_EPT[2].UDPHS_EPTCFG & AT91C_UDPHS_EPT_MAPD) != (signed int)AT91C_UDPHS_EPT_MAPD ) {} + + AT91C_BASE_UDPHS->UDPHS_EPT[2].UDPHS_EPTCTLENB = AT91C_UDPHS_EPT_ENABL; + + // Write FIFO + pFifo = (char*)((unsigned int *)(AT91C_BASE_UDPHS_EPTFIFO->UDPHS_READEPT0) + (16384 * 2)); + for( i=0; iUDPHS_TST |= AT91C_UDPHS_TST_PKT; + // Send packet + AT91C_BASE_UDPHS->UDPHS_EPT[2].UDPHS_EPTSETSTA = AT91C_UDPHS_TX_PK_RDY; + break; + + case USBFeatureRequest_TESTJ: + TRACE_DEBUG_WP("TEST_J "); + AT91C_BASE_UDPHS->UDPHS_TST = AT91C_UDPHS_TST_J; + break; + + case USBFeatureRequest_TESTK: + TRACE_DEBUG_WP("TEST_K "); + AT91C_BASE_UDPHS->UDPHS_TST = AT91C_UDPHS_TST_K; + break; + + case USBFeatureRequest_TESTSE0NAK: + TRACE_DEBUG_WP("TEST_SEO_NAK "); + AT91C_BASE_UDPHS->UDPHS_IEN = 0; // for test + break; + + case USBFeatureRequest_TESTSENDZLP: + //while( 0 != (AT91C_BASE_UDPHS->UDPHS_EPT[0].UDPHS_EPTSTA & AT91C_UDPHS_TX_PK_RDY ) ) {} + AT91C_BASE_UDPHS->UDPHS_EPT[0].UDPHS_EPTSETSTA = AT91C_UDPHS_TX_PK_RDY; + //while( 0 != (AT91C_BASE_UDPHS->UDPHS_EPT[0].UDPHS_EPTSTA & AT91C_UDPHS_TX_PK_RDY ) ) {} + TRACE_DEBUG_WP("SEND_ZLP "); + break; + } + TRACE_DEBUG_WP("\n\r"); +} + + +//------------------------------------------------------------------------------ +/// Initializes the specified USB driver +/// This function initializes the current FIFO bank of endpoints, +/// configures the pull-up and VBus lines, disconnects the pull-up and +/// then trigger the Init callback. +//------------------------------------------------------------------------------ +void USBD_Init( void ) +{ + unsigned char i; + + TRACE_DEBUG_WP("USBD Init()\n\r"); + + // Reset endpoint structures + UDPHS_ResetEndpoints(); + + // Enables the USB Clock + UDPHS_EnableUsbClock(); + + // Configure the pull-up on D+ and disconnect it +#if defined(BOARD_USB_PULLUP_INTERNAL) + AT91C_BASE_UDPHS->UDPHS_CTRL |= AT91C_UDPHS_DETACH; // detach + AT91C_BASE_UDPHS->UDPHS_CTRL |= AT91C_UDPHS_PULLD_DIS; // Disable Pull Down + +#elif defined(BOARD_USB_PULLUP_INTERNAL_BY_MATRIX) + TRACE_DEBUG_WP("PUON 0\n\r"); + AT91C_BASE_MATRIX->MATRIX_USBPCR &= ~AT91C_MATRIX_USBPCR_PUON; + +#elif defined(BOARD_USB_PULLUP_EXTERNAL) +#ifdef PIN_USB_PULLUP + const Pin pinPullUp = PIN_USB_PULLUP; + PIO_Configure(&pinPullUp, 1); + if (pinPullUp.attribute == PIO_OUTPUT_0) { + + PIO_Clear(&pinPullUp); + } + else { + + PIO_Set(&pinPullUp); + } +#else + #error unsupported now +#endif +#elif !defined(BOARD_USB_PULLUP_ALWAYSON) + #error Unsupported pull-up type. + +#endif + + // Reset and enable IP UDPHS + AT91C_BASE_UDPHS->UDPHS_CTRL &= ~AT91C_UDPHS_EN_UDPHS; + AT91C_BASE_UDPHS->UDPHS_CTRL |= AT91C_UDPHS_EN_UDPHS; + // Enable and disable of the transceiver is automaticaly done by the IP. + + // With OR without DMA !!! + // Initialization of DMA + for( i=1; i<=((AT91C_BASE_UDPHS->UDPHS_IPFEATURES & AT91C_UDPHS_DMA_CHANNEL_NBR)>>4); i++ ) { + + // RESET endpoint canal DMA: + // DMA stop channel command + AT91C_BASE_UDPHS->UDPHS_DMA[i].UDPHS_DMACONTROL = 0; // STOP command + + // Disable endpoint + AT91C_BASE_UDPHS->UDPHS_EPT[i].UDPHS_EPTCTLDIS = AT91C_UDPHS_SHRT_PCKT + | AT91C_UDPHS_BUSY_BANK + | AT91C_UDPHS_NAK_OUT + | AT91C_UDPHS_NAK_IN + | AT91C_UDPHS_STALL_SNT + | AT91C_UDPHS_RX_SETUP + | AT91C_UDPHS_TX_PK_RDY + | AT91C_UDPHS_TX_COMPLT + | AT91C_UDPHS_RX_BK_RDY + | AT91C_UDPHS_ERR_OVFLW + | AT91C_UDPHS_MDATA_RX + | AT91C_UDPHS_DATAX_RX + | AT91C_UDPHS_NYET_DIS + | AT91C_UDPHS_INTDIS_DMA + | AT91C_UDPHS_AUTO_VALID + | AT91C_UDPHS_EPT_DISABL; + + // Clear status endpoint + AT91C_BASE_UDPHS->UDPHS_EPT[i].UDPHS_EPTCLRSTA = AT91C_UDPHS_TOGGLESQ + | AT91C_UDPHS_FRCESTALL + | AT91C_UDPHS_RX_BK_RDY + | AT91C_UDPHS_TX_COMPLT + | AT91C_UDPHS_RX_SETUP + | AT91C_UDPHS_STALL_SNT + | AT91C_UDPHS_NAK_IN + | AT91C_UDPHS_NAK_OUT; + + // Reset endpoint config + AT91C_BASE_UDPHS->UDPHS_EPT[i].UDPHS_EPTCTLENB = 0; + + // Reset DMA channel (Buff count and Control field) + AT91C_BASE_UDPHS->UDPHS_DMA[i].UDPHS_DMACONTROL = AT91C_UDPHS_LDNXT_DSC; // NON STOP command + + // Reset DMA channel 0 (STOP) + AT91C_BASE_UDPHS->UDPHS_DMA[i].UDPHS_DMACONTROL = 0; // STOP command + + // Clear DMA channel status (read the register for clear it) + AT91C_BASE_UDPHS->UDPHS_DMA[i].UDPHS_DMASTATUS = AT91C_BASE_UDPHS->UDPHS_DMA[i].UDPHS_DMASTATUS; + + } + + AT91C_BASE_UDPHS->UDPHS_TST = 0; + AT91C_BASE_UDPHS->UDPHS_IEN = 0; + AT91C_BASE_UDPHS->UDPHS_CLRINT = AT91C_UDPHS_UPSTR_RES + | AT91C_UDPHS_ENDOFRSM + | AT91C_UDPHS_WAKE_UP + | AT91C_UDPHS_ENDRESET + | AT91C_UDPHS_IEN_SOF + | AT91C_UDPHS_MICRO_SOF + | AT91C_UDPHS_DET_SUSPD; + + // Device is in the Attached state + deviceState = USBD_STATE_SUSPENDED; + previousDeviceState = USBD_STATE_POWERED; + + // Disable interrupts + AT91C_BASE_UDPHS->UDPHS_IEN = AT91C_UDPHS_ENDOFRSM + | AT91C_UDPHS_WAKE_UP + | AT91C_UDPHS_DET_SUSPD; + + // Disable USB clocks + UDPHS_DisableUsbClock(); + + // Configure interrupts + USBDCallbacks_Initialized(); +} + +//------------------------------------------------------------------------------ +/// Returns the current state of the USB device. +/// \return Device current state. +//------------------------------------------------------------------------------ +unsigned char USBD_GetState( void ) +{ + return deviceState; +} + +#endif // BOARD_USB_UDPHS + -- cgit v1.2.3