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/ccid/cciddriver.c | 1503 +++++++++++++++++ at91lib/usb/device/ccid/cciddriver.h | 378 +++++ at91lib/usb/device/ccid/cciddriverdescriptors.h | 152 ++ 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 ++++++++++++++++++++ 16 files changed, 7998 insertions(+) create mode 100644 at91lib/usb/device/ccid/cciddriver.c create mode 100644 at91lib/usb/device/ccid/cciddriver.h create mode 100644 at91lib/usb/device/ccid/cciddriverdescriptors.h 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') diff --git a/at91lib/usb/device/ccid/cciddriver.c b/at91lib/usb/device/ccid/cciddriver.c new file mode 100644 index 0000000..a23fc21 --- /dev/null +++ b/at91lib/usb/device/ccid/cciddriver.c @@ -0,0 +1,1503 @@ +/* ---------------------------------------------------------------------------- + * 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 +/// +/// CCID driver +/// +/// !Usage +/// +/// Explanation on the usage of the code made available through the header file. +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// Headers +//------------------------------------------------------------------------------ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//------------------------------------------------------------------------------ +// Local definition +//------------------------------------------------------------------------------ + +/// Constants: IDs: Device product ID. +#define CCIDDriverDescriptors_PRODUCTID 0x6129 +/// Constants: IDs: Device vendor ID. +#define CCIDDriverDescriptors_VENDORID 0x03EB +/// Constants: IDs: Device release number. +#define CCIDDriverDescriptors_RELEASE 0x0100 + +/// Returns the minimum between two values. +#define MIN(a, b) ((a < b) ? a : b) + +//------------------------------------------------------------------------------ +// Types +//------------------------------------------------------------------------------ + +/// CCIDDriverConfiguration Descriptors +/// List of descriptors that make up the configuration descriptors of a +/// device using the CCID driver. +typedef struct { + + /// Configuration descriptor + USBConfigurationDescriptor configuration; + /// Interface descriptor + USBInterfaceDescriptor interface; + /// CCID descriptor + CCIDDescriptor ccid; + /// Bulk OUT endpoint descriptor + USBEndpointDescriptor bulkOut; + /// Bulk IN endpoint descriptor + USBEndpointDescriptor bulkIn; + /// Interrupt OUT endpoint descriptor + USBEndpointDescriptor interruptIn; + +} __attribute__ ((packed)) CCIDDriverConfigurationDescriptors; + +//------------------------------------------------------------------------------ +// Types +//------------------------------------------------------------------------------ + +/// Driver structure for an CCID device +typedef struct { + + /// Standard USB device driver instance + USBDDriver usbdDriver; + /// CCID message + S_ccid_bulk_in_header sCcidMessage; + /// CCID command + S_ccid_bulk_out_header sCcidCommand; + /// Interrupt message answer + unsigned char BufferINT[4]; + /// Buffer data of message + unsigned char ProtocolDataStructure[10]; + /// Protocol used + unsigned char bProtocol; + /// SlotStatus + /// Bit 0 = Slot 0 current state + /// Bit 1 = Slot 0 changed status + /// Bit 2 = Slot 1 current state + /// Bit 3 = Slot 1 changed status + /// Bit 4 = Slot 2 current state + /// Bit 5 = Slot 2 changed status + unsigned char SlotStatus; + +} CCIDDriver; + +//------------------------------------------------------------------------------ +// Local variables +//------------------------------------------------------------------------------ + +/// Static instance of the CCID device driver. +static CCIDDriver ccidDriver; + +/// Standard USB device descriptor. +static const USBDeviceDescriptor deviceDescriptor = { + + sizeof(USBDeviceDescriptor), + USBGenericDescriptor_DEVICE, + USBDeviceDescriptor_USB2_00, + 0, + 0, + 0, + BOARD_USB_ENDPOINTS_MAXPACKETSIZE(0), + CCIDDriverDescriptors_VENDORID, + CCIDDriverDescriptors_PRODUCTID, + CCIDDriverDescriptors_RELEASE, + 1, // Index of manufacturer description + 2, // Index of product description + 3, // Index of serial number description + 1 // One possible configuration +}; + + +/// List of configuration descriptors. +static const CCIDDriverConfigurationDescriptors configurationDescriptorsFS = { + + // Standard USB configuration descriptor + { + sizeof(USBConfigurationDescriptor), + USBGenericDescriptor_CONFIGURATION, + sizeof(CCIDDriverConfigurationDescriptors), + 1, // One interface in this configuration + 1, // This is configuration #1 + 0, // No associated string descriptor + BOARD_USB_BMATTRIBUTES, + USBConfigurationDescriptor_POWER(100) + }, + // CCID interface descriptor + // Table 4.3-1 Interface Descriptor + // Interface descriptor + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + 0, // Interface 0 + 0, // No alternate settings + 3, // uses bulk-IN, bulk-OUT and interrupt–IN + SMART_CARD_DEVICE_CLASS, + 0, // Subclass code + 0, // bulk transfers optional interrupt-IN + 0 // No associated string descriptor + }, + { + sizeof(CCIDDescriptor), // bLength: Size of this descriptor in bytes + CCID_DECRIPTOR_TYPE, // bDescriptorType:Functional descriptor type + CCID1_10, // bcdCCID: CCID version + 0, // bMaxSlotIndex: Value 0 indicates that one slot is supported + VOLTS_5_0, // bVoltageSupport + PROTOCOL_TO, // dwProtocols + 3580, // dwDefaultClock + 3580, // dwMaxClock + 0, // bNumClockSupported + 9600, // dwDataRate : 9600 bauds + 9600, // dwMaxDataRate : 9600 bauds + 0, // bNumDataRatesSupported + 0xfe, // dwMaxIFSD + 0, // dwSynchProtocols + 0, // dwMechanical + //0x00010042, // dwFeatures: Short APDU level exchanges + CCID_FEATURES_AUTO_PCONF | CCID_FEATURES_AUTO_PNEGO | CCID_FEATURES_EXC_TPDU, + 0x0000010F, // dwMaxCCIDMessageLength: For extended APDU level the value shall be between 261 + 10 + 0xFF, // bClassGetResponse: Echoes the class of the APDU + 0xFF, // bClassEnvelope: Echoes the class of the APDU + 0, // wLcdLayout: no LCD + 0, // bPINSupport: No PIN + 1 // bMaxCCIDBusySlot + }, + // Bulk-OUT endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( USBEndpointDescriptor_OUT, CCID_EPT_DATA_OUT ), + USBEndpointDescriptor_BULK, + MIN(BOARD_USB_ENDPOINTS_MAXPACKETSIZE(CCID_EPT_DATA_OUT), + USBEndpointDescriptor_MAXBULKSIZE_FS), + 0x00 // Does not apply to Bulk endpoints + }, + // Bulk-IN endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( USBEndpointDescriptor_IN, CCID_EPT_DATA_IN ), + USBEndpointDescriptor_BULK, + MIN(BOARD_USB_ENDPOINTS_MAXPACKETSIZE(CCID_EPT_DATA_IN), + USBEndpointDescriptor_MAXBULKSIZE_FS), + 0x00 // Does not apply to Bulk endpoints + }, + // Notification endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( USBEndpointDescriptor_IN, CCID_EPT_NOTIFICATION ), + USBEndpointDescriptor_INTERRUPT, + MIN(BOARD_USB_ENDPOINTS_MAXPACKETSIZE(CCID_EPT_NOTIFICATION), + USBEndpointDescriptor_MAXINTERRUPTSIZE_FS), + 0x10 + } +}; + +#ifdef BOARD_USB_UDPHS +static const CCIDDriverConfigurationDescriptors configurationDescriptorsHS = { + + // Standard USB configuration descriptor + { + sizeof(USBConfigurationDescriptor), + USBGenericDescriptor_CONFIGURATION, + sizeof(CCIDDriverConfigurationDescriptors), + 1, // One interface in this configuration + 1, // This is configuration #1 + 0, // No associated string descriptor + BOARD_USB_BMATTRIBUTES, + USBConfigurationDescriptor_POWER(100) + }, + // CCID interface descriptor + // Table 4.3-1 Interface Descriptor + // Interface descriptor + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + 0, // Interface 0 + 0, // No alternate settings + 3, // uses bulk-IN, bulk-OUT and interrupt–IN + SMART_CARD_DEVICE_CLASS, + 0, // Subclass code + 0, // bulk transfers optional interrupt-IN + 0 // No associated string descriptor + }, + { + sizeof(CCIDDescriptor), // bLength: Size of this descriptor in bytes + CCID_DECRIPTOR_TYPE, // bDescriptorType:Functional descriptor type + CCID1_10, // bcdCCID: CCID version + 0, // bMaxSlotIndex: Value 0 indicates that one slot is supported + VOLTS_5_0, // bVoltageSupport + PROTOCOL_TO, // dwProtocols + 3580, // dwDefaultClock + 3580, // dwMaxClock + 0, // bNumClockSupported + 9600, // dwDataRate : 9600 bauds + 9600, // dwMaxDataRate : 9600 bauds + 0, // bNumDataRatesSupported + 0xfe, // dwMaxIFSD + 0, // dwSynchProtocols + 0, // dwMechanical + //0x00010042, // dwFeatures: Short APDU level exchanges + CCID_FEATURES_AUTO_PCONF | CCID_FEATURES_AUTO_PNEGO | CCID_FEATURES_EXC_TPDU, + 0x0000010F, // dwMaxCCIDMessageLength: For extended APDU level the value shall be between 261 + 10 + 0xFF, // bClassGetResponse: Echoes the class of the APDU + 0xFF, // bClassEnvelope: Echoes the class of the APDU + 0, // wLcdLayout: no LCD + 0, // bPINSupport: No PIN + 1 // bMaxCCIDBusySlot + }, + // Bulk-OUT endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( USBEndpointDescriptor_OUT, CCID_EPT_DATA_OUT ), + USBEndpointDescriptor_BULK, + MIN(BOARD_USB_ENDPOINTS_MAXPACKETSIZE(CCID_EPT_DATA_OUT), + USBEndpointDescriptor_MAXBULKSIZE_HS), + 0x00 // Does not apply to Bulk endpoints + }, + // Bulk-IN endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( USBEndpointDescriptor_IN, CCID_EPT_DATA_IN ), + USBEndpointDescriptor_BULK, + MIN(BOARD_USB_ENDPOINTS_MAXPACKETSIZE(CCID_EPT_DATA_IN), + USBEndpointDescriptor_MAXBULKSIZE_HS), + 0x00 // Does not apply to Bulk endpoints + }, + // Notification endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( USBEndpointDescriptor_IN, CCID_EPT_NOTIFICATION ), + USBEndpointDescriptor_INTERRUPT, + MIN(BOARD_USB_ENDPOINTS_MAXPACKETSIZE(CCID_EPT_NOTIFICATION), + USBEndpointDescriptor_MAXINTERRUPTSIZE_HS), + 0x10 + } +}; + +/// Qualifier descriptor +const USBDeviceQualifierDescriptor deviceQualifierDescriptor = { + + sizeof(USBDeviceQualifierDescriptor), // Size of this descriptor in bytes + USBGenericDescriptor_DEVICEQUALIFIER, // Qualifier Descriptor Type + USBDeviceDescriptor_USB2_00, // USB specification 2.00 + 0x00, // Class is specified in interface + 0x00, // Subclass is specified in interface + 0x00, // Protocol is specified in interface + BOARD_USB_ENDPOINTS_MAXPACKETSIZE(0), + 0x01, // One possible configuration + 0x00 // Reserved for future use, must be zero +}; + +/// OtherSpeed configuration descriptor in Full Speed mode +static const CCIDDriverConfigurationDescriptors sOtherSpeedConfigurationFS = { + + // Standard USB configuration descriptor + { + sizeof(USBConfigurationDescriptor), + USBGenericDescriptor_OTHERSPEEDCONFIGURATION, + sizeof(CCIDDriverConfigurationDescriptors), + 1, // One interface in this configuration + 1, // This is configuration #1 + 0, // No associated string descriptor + BOARD_USB_BMATTRIBUTES, + USBConfigurationDescriptor_POWER(100) + }, + // CCID interface descriptor + // Table 4.3-1 Interface Descriptor + // Interface descriptor + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + 0, // Interface 0 + 0, // No alternate settings + 3, // uses bulk-IN, bulk-OUT and interrupt–IN + SMART_CARD_DEVICE_CLASS, + 0, // Subclass code + 0, // bulk transfers optional interrupt-IN + 0 // No associated string descriptor + }, + { + sizeof(CCIDDescriptor), // bLength: Size of this descriptor in bytes + CCID_DECRIPTOR_TYPE, // bDescriptorType:Functional descriptor type + CCID1_10, // bcdCCID: CCID version + 0, // bMaxSlotIndex: Value 0 indicates that one slot is supported + VOLTS_5_0, // bVoltageSupport + PROTOCOL_TO, // dwProtocols + 3580, // dwDefaultClock + 3580, // dwMaxClock + 0, // bNumClockSupported + 9600, // dwDataRate : 9600 bauds + 9600, // dwMaxDataRate : 9600 bauds + 0, // bNumDataRatesSupported + 0xfe, // dwMaxIFSD + 0, // dwSynchProtocols + 0, // dwMechanical + //0x00010042, // dwFeatures: Short APDU level exchanges + CCID_FEATURES_AUTO_PCONF | CCID_FEATURES_AUTO_PNEGO | CCID_FEATURES_EXC_TPDU, + 0x0000010F, // dwMaxCCIDMessageLength: For extended APDU level the value shall be between 261 + 10 + 0xFF, // bClassGetResponse: Echoes the class of the APDU + 0xFF, // bClassEnvelope: Echoes the class of the APDU + 0, // wLcdLayout: no LCD + 0, // bPINSupport: No PIN + 1 // bMaxCCIDBusySlot + }, + // Bulk-OUT endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( USBEndpointDescriptor_OUT, CCID_EPT_DATA_OUT ), + USBEndpointDescriptor_BULK, + MIN(BOARD_USB_ENDPOINTS_MAXPACKETSIZE(CCID_EPT_DATA_OUT), + USBEndpointDescriptor_MAXBULKSIZE_FS), + 0x00 // Does not apply to Bulk endpoints + }, + // Bulk-IN endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( USBEndpointDescriptor_IN, CCID_EPT_DATA_IN ), + USBEndpointDescriptor_BULK, + MIN(BOARD_USB_ENDPOINTS_MAXPACKETSIZE(CCID_EPT_DATA_IN), + USBEndpointDescriptor_MAXBULKSIZE_FS), + 0x00 // Does not apply to Bulk endpoints + }, + // Notification endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( USBEndpointDescriptor_IN, CCID_EPT_NOTIFICATION ), + USBEndpointDescriptor_INTERRUPT, + MIN(BOARD_USB_ENDPOINTS_MAXPACKETSIZE(CCID_EPT_NOTIFICATION), + USBEndpointDescriptor_MAXINTERRUPTSIZE_FS), + 0x10 + } +}; + +/// OtherSpeed configuration descriptor in High Speed mode +static const CCIDDriverConfigurationDescriptors sOtherSpeedConfigurationHS = { + + // Standard USB configuration descriptor + { + sizeof(USBConfigurationDescriptor), + USBGenericDescriptor_OTHERSPEEDCONFIGURATION, + sizeof(CCIDDriverConfigurationDescriptors), + 1, // One interface in this configuration + 1, // This is configuration #1 + 0, // No associated string descriptor + BOARD_USB_BMATTRIBUTES, + USBConfigurationDescriptor_POWER(100) + }, + // CCID interface descriptor + // Table 4.3-1 Interface Descriptor + // Interface descriptor + { + sizeof(USBInterfaceDescriptor), + USBGenericDescriptor_INTERFACE, + 0, // Interface 0 + 0, // No alternate settings + 3, // uses bulk-IN, bulk-OUT and interrupt–IN + SMART_CARD_DEVICE_CLASS, + 0, // Subclass code + 0, // bulk transfers optional interrupt-IN + 0 // No associated string descriptor + }, + { + sizeof(CCIDDescriptor), // bLength: Size of this descriptor in bytes + CCID_DECRIPTOR_TYPE, // bDescriptorType:Functional descriptor type + CCID1_10, // bcdCCID: CCID version + 0, // bMaxSlotIndex: Value 0 indicates that one slot is supported + VOLTS_5_0, // bVoltageSupport + PROTOCOL_TO, // dwProtocols + 3580, // dwDefaultClock + 3580, // dwMaxClock + 0, // bNumClockSupported + 9600, // dwDataRate : 9600 bauds + 9600, // dwMaxDataRate : 9600 bauds + 0, // bNumDataRatesSupported + 0xfe, // dwMaxIFSD + 0, // dwSynchProtocols + 0, // dwMechanical + //0x00010042, // dwFeatures: Short APDU level exchanges + CCID_FEATURES_AUTO_PCONF | CCID_FEATURES_AUTO_PNEGO | CCID_FEATURES_EXC_TPDU, + 0x0000010F, // dwMaxCCIDMessageLength: For extended APDU level the value shall be between 261 + 10 + 0xFF, // bClassGetResponse: Echoes the class of the APDU + 0xFF, // bClassEnvelope: Echoes the class of the APDU + 0, // wLcdLayout: no LCD + 0, // bPINSupport: No PIN + 1 // bMaxCCIDBusySlot + }, + // Bulk-OUT endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( USBEndpointDescriptor_OUT, CCID_EPT_DATA_OUT ), + USBEndpointDescriptor_BULK, + MIN(BOARD_USB_ENDPOINTS_MAXPACKETSIZE(CCID_EPT_DATA_OUT), + USBEndpointDescriptor_MAXBULKSIZE_HS), + 0x00 // Does not apply to Bulk endpoints + }, + // Bulk-IN endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( USBEndpointDescriptor_IN, CCID_EPT_DATA_IN ), + USBEndpointDescriptor_BULK, + MIN(BOARD_USB_ENDPOINTS_MAXPACKETSIZE(CCID_EPT_DATA_IN), + USBEndpointDescriptor_MAXBULKSIZE_HS), + 0x00 // Does not apply to Bulk endpoints + }, + // Notification endpoint descriptor + { + sizeof(USBEndpointDescriptor), + USBGenericDescriptor_ENDPOINT, + USBEndpointDescriptor_ADDRESS( USBEndpointDescriptor_IN, CCID_EPT_NOTIFICATION ), + USBEndpointDescriptor_INTERRUPT, + MIN(BOARD_USB_ENDPOINTS_MAXPACKETSIZE(CCID_EPT_NOTIFICATION), + USBEndpointDescriptor_MAXINTERRUPTSIZE_HS), + 0x10 + } +}; +#endif + +/// Language ID string descriptor. +static const unsigned char languageIdDescriptor[] = { + + USBStringDescriptor_LENGTH(1), + USBGenericDescriptor_STRING, + USBStringDescriptor_ENGLISH_US +}; + +/// Manufacturer name. +static const unsigned char manufacturerDescriptor[] = { + + USBStringDescriptor_LENGTH(5), + USBGenericDescriptor_STRING, + USBStringDescriptor_UNICODE('A'), + USBStringDescriptor_UNICODE('T'), + USBStringDescriptor_UNICODE('M'), + USBStringDescriptor_UNICODE('E'), + USBStringDescriptor_UNICODE('L') +}; + +/// Product name. +static const unsigned char productDescriptor[] = { + + USBStringDescriptor_LENGTH(23), + USBGenericDescriptor_STRING, + USBStringDescriptor_UNICODE('A'), + USBStringDescriptor_UNICODE('T'), + USBStringDescriptor_UNICODE('M'), + USBStringDescriptor_UNICODE('E'), + USBStringDescriptor_UNICODE('L'), + USBStringDescriptor_UNICODE(' '), + USBStringDescriptor_UNICODE('A'), + USBStringDescriptor_UNICODE('T'), + USBStringDescriptor_UNICODE('9'), + USBStringDescriptor_UNICODE('1'), + USBStringDescriptor_UNICODE(' '), + USBStringDescriptor_UNICODE('C'), + USBStringDescriptor_UNICODE('C'), + USBStringDescriptor_UNICODE('I'), + USBStringDescriptor_UNICODE('D'), + USBStringDescriptor_UNICODE(' '), + USBStringDescriptor_UNICODE('D'), + USBStringDescriptor_UNICODE('R'), + USBStringDescriptor_UNICODE('I'), + USBStringDescriptor_UNICODE('V'), + USBStringDescriptor_UNICODE('E'), + USBStringDescriptor_UNICODE('R'), + USBStringDescriptor_UNICODE(' ') +}; + +/// Product serial number. +static const unsigned char serialNumberDescriptor[] = { + + USBStringDescriptor_LENGTH(12), + USBGenericDescriptor_STRING, + USBStringDescriptor_UNICODE('0'), + USBStringDescriptor_UNICODE('1'), + USBStringDescriptor_UNICODE('2'), + USBStringDescriptor_UNICODE('3'), + USBStringDescriptor_UNICODE('4'), + USBStringDescriptor_UNICODE('5'), + USBStringDescriptor_UNICODE('6'), + USBStringDescriptor_UNICODE('7'), + USBStringDescriptor_UNICODE('8'), + USBStringDescriptor_UNICODE('9'), + USBStringDescriptor_UNICODE('A'), + USBStringDescriptor_UNICODE('F') +}; + +/// Array of pointers to string descriptors. +static const unsigned char *stringDescriptors[] = { + + languageIdDescriptor, + manufacturerDescriptor, + productDescriptor, + serialNumberDescriptor +}; + + +/// List of standard descriptors for the serial driver. +const USBDDriverDescriptors ccidDriverDescriptors = { + + &deviceDescriptor, // FS + (USBConfigurationDescriptor *) &configurationDescriptorsFS, +#ifdef BOARD_USB_UDPHS + (USBDeviceQualifierDescriptor *) &deviceQualifierDescriptor, // FS + (USBConfigurationDescriptor *) &sOtherSpeedConfigurationFS, + &deviceDescriptor, // HS + (USBConfigurationDescriptor *) &configurationDescriptorsHS, + (USBDeviceQualifierDescriptor *) &deviceQualifierDescriptor, // HS + (USBConfigurationDescriptor *) &sOtherSpeedConfigurationHS, +#else + 0, // No qualifier descriptor FS + 0, // No other-speed configuration FS + 0, // No device descriptor HS + 0, // No configuration HS + 0, // No qualifier descriptor HS + 0, // No other-speed configuration HS +#endif + stringDescriptors, + 4 // Four string descriptors in array +}; + +//------------------------------------------------------------------------------ +// Internal functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Response Pipe, Bulk-IN Messages +/// Return the Slot Status to the host +/// Answer to: +/// PC_to_RDR_IccPowerOff +/// PC_to_RDR_GetSlotStatus +/// PC_to_RDR_IccClock +/// PC_to_RDR_T0APDU +/// PC_to_RDR_Mechanical +/// PC_to_RDR_Abort and Class specific ABORT request +//------------------------------------------------------------------------------ +static void RDRtoPCSlotStatus( void ) +{ + TRACE_DEBUG("RDRtoPCSlotStatus\n\r"); + + // Header fields settings + ccidDriver.sCcidMessage.bMessageType = RDR_TO_PC_SLOTSTATUS; + ccidDriver.sCcidMessage.wLength = 0; + ccidDriver.sCcidMessage.bStatus = ccidDriver.SlotStatus; + ccidDriver.sCcidMessage.bError = 0; + // 00h Clock running + // 01h Clock stopped in state L + // 02h Clock stopped in state H + // 03h Clock stopped in an unknown state + // All other values are Reserved for Future Use. + ccidDriver.sCcidMessage.bSpecific = 0; +} + +//------------------------------------------------------------------------------ +/// Response Pipe, Bulk-IN Messages +/// Answer to PC_to_RDR_IccPowerOn +//------------------------------------------------------------------------------ +static void RDRtoPCDatablock_ATR( void ) +{ + unsigned char i; + unsigned char Atr[ATR_SIZE_MAX]; + unsigned char length; + + //TRACE_DEBUG("RDRtoPCDatablock\n\r"); + + ISO7816_Datablock_ATR( Atr, &length ); + + if( length > 5 ) { + ccidDriver.ProtocolDataStructure[1] = Atr[5]&0x0F; // TD(1) + ccidDriver.bProtocol = Atr[5]&0x0F; // TD(1) + } + + // S_ccid_protocol_t0 + // bmFindexDindex + ccidDriver.ProtocolDataStructure[0] = Atr[2]; // TA(1) + + // bmTCCKST0 + // For T=0 ,B0 – 0b, B7-2 – 000000b + // B1 – Convention used (b1=0 for direct, b1=1 for inverse) + + // bGuardTimeT0 + // Extra Guardtime between two characters. Add 0 to 254 etu to the normal + // guardtime of 12etu. FFh is the same as 00h. + ccidDriver.ProtocolDataStructure[2] = Atr[4]; // TC(1) + // AT91C_BASE_US0->US_TTGR = 0; // TC1 + + // bWaitingIntegerT0 + // WI for T=0 used to define WWT + ccidDriver.ProtocolDataStructure[3] = Atr[7]; // TC(2) + + // bClockStop + // ICC Clock Stop Support + // 00 = Stopping the Clock is not allowed + // 01 = Stop with Clock signal Low + // 02 = Stop with Clock signal High + // 03 = Stop with Clock either High or Low + ccidDriver.ProtocolDataStructure[4] = 0x00; // 0 to 3 + + // Header fields settings + ccidDriver.sCcidMessage.bMessageType = RDR_TO_PC_DATABLOCK; + ccidDriver.sCcidMessage.wLength = length; // Size of ATR + ccidDriver.sCcidMessage.bSizeToSend += length; // Size of ATR + // bChainParameter: 00 the response APDU begins and ends in this command + ccidDriver.sCcidMessage.bSpecific = 0; + + for( i=0; i (configurationDescriptorsFS.ccid.dwMaxCCIDMessageLength-10) ) { + + ccidDriver.sCcidMessage.bStatus = 1; + ccidDriver.sCcidMessage.bError = 0; + } + // check bBWI + else if ( 0 != ccidDriver.sCcidCommand.bSpecific_0 ) { + + TRACE_ERROR("Bad bBWI\n\r"); + } + else { + + // APDU or TPDU + switch(configurationDescriptorsFS.ccid.dwFeatures + & (CCID_FEATURES_EXC_TPDU|CCID_FEATURES_EXC_SAPDU|CCID_FEATURES_EXC_APDU)) { + + case CCID_FEATURES_EXC_TPDU: + if (ccidDriver.ProtocolDataStructure[1] == PROTOCOL_TO) { + + // Send commande APDU + indexMessage = ISO7816_XfrBlockTPDU_T0( ccidDriver.sCcidCommand.APDU , + ccidDriver.sCcidMessage.abData, + ccidDriver.sCcidCommand.wLength ); + } + else { + if (ccidDriver.ProtocolDataStructure[1] == PROTOCOL_T1) { + TRACE_INFO("Not supported T=1\n\r"); + } + else { + TRACE_INFO("Not supported\n\r"); + } + } + break; + + case CCID_FEATURES_EXC_APDU: + TRACE_INFO("Not supported\n\r"); + break; + + default: + break; + } + + } + + ccidDriver.sCcidMessage.wLength = indexMessage; + TRACE_DEBUG("USB: 0x%X, 0x%X, 0x%X, 0x%X, 0x%X\n\r", ccidDriver.sCcidMessage.abData[0], + ccidDriver.sCcidMessage.abData[1], + ccidDriver.sCcidMessage.abData[2], + ccidDriver.sCcidMessage.abData[3], + ccidDriver.sCcidMessage.abData[4] ); + RDRtoPCDatablock(); + +} + +//------------------------------------------------------------------------------ +/// Command Pipe, Bulk-OUT Messages +/// return parameters by the command: RDR_to_PC_Parameters +//------------------------------------------------------------------------------ +static void PCtoRDRGetParameters( void ) +{ + TRACE_DEBUG("PCtoRDRGetParameters\n\r"); + + // We support only one slot + + // bmIccStatus + if( ISO7816_StatusReset() ) { + // 0: An ICC is present and active (power is on and stable, RST is inactive + ccidDriver.sCcidMessage.bStatus = 0; + } + else { + // 1: An ICC is present and inactive (not activated or shut down by hardware error) + ccidDriver.sCcidMessage.bStatus = 1; + } + + RDRtoPCParameters(); +} + +//------------------------------------------------------------------------------ +/// Command Pipe, Bulk-OUT Messages +/// This command resets the slot parameters to their default values +//------------------------------------------------------------------------------ +static void PCtoRDRResetParameters( void ) +{ + TRACE_DEBUG("PCtoRDRResetParameters\n\r"); + + ccidDriver.SlotStatus = ICC_NOT_PRESENT; + ccidDriver.sCcidMessage.bStatus = ccidDriver.SlotStatus; + + RDRtoPCParameters(); +} + +//------------------------------------------------------------------------------ +/// Command Pipe, Bulk-OUT Messages +/// This command is used to change the parameters for a given slot. +//------------------------------------------------------------------------------ +static void PCtoRDRSetParameters( void ) +{ + TRACE_DEBUG("PCtoRDRSetParameters\n\r"); + + ccidDriver.SlotStatus = ccidDriver.sCcidCommand.bSlot; + ccidDriver.sCcidMessage.bStatus = ccidDriver.SlotStatus; + // Not all feature supported + + RDRtoPCParameters(); +} + +//------------------------------------------------------------------------------ +/// Command Pipe, Bulk-OUT Messages +/// This command allows the CCID manufacturer to define and access extended +/// features. +/// Information sent via this command is processed by the CCID control logic. +//------------------------------------------------------------------------------ +static void PCtoRDREscape( void ) +{ + TRACE_DEBUG("PCtoRDREscape\n\r"); + + // If needed by the user + ISO7816_Escape(); + + // stub, return all value send + RDRtoPCEscape( ccidDriver.sCcidCommand.wLength, ccidDriver.sCcidCommand.APDU); +} + +//------------------------------------------------------------------------------ +/// Command Pipe, Bulk-OUT Messages +/// This command stops or restarts the clock. +//------------------------------------------------------------------------------ +static void PCtoRDRICCClock( void ) +{ + TRACE_DEBUG("PCtoRDRICCClock\n\r"); + + if( 0 == ccidDriver.sCcidCommand.bSpecific_0 ) { + // restarts the clock + ISO7816_RestartClock(); + } + else { + // stop clock in the state shown in the bClockStop field + ISO7816_StopClock(); + } + + RDRtoPCSlotStatus( ); +} + +//------------------------------------------------------------------------------ +/// Command Pipe, Bulk-OUT Messages +/// This command changes the parameters used to perform the transportation of +/// APDU messages by the T=0 protocol. +//------------------------------------------------------------------------------ +static void PCtoRDRtoAPDU( void ) +{ + unsigned char bmChanges; + unsigned char bClassGetResponse; + unsigned char bClassEnvelope; + + TRACE_DEBUG("PCtoRDRtoAPDU\n\r"); + + if( configurationDescriptorsFS.ccid.dwFeatures == (CCID_FEATURES_EXC_SAPDU|CCID_FEATURES_EXC_APDU) ) { + + bmChanges = ccidDriver.sCcidCommand.bSpecific_0; + bClassGetResponse = ccidDriver.sCcidCommand.bSpecific_1; + bClassEnvelope = ccidDriver.sCcidCommand.bSpecific_2; + + ISO7816_toAPDU(); + } + + RDRtoPCSlotStatus(); +} + +//------------------------------------------------------------------------------ +/// Command Pipe, Bulk-OUT Messages +/// This is a command message to allow entering the PIN for verification or +/// modification. +//------------------------------------------------------------------------------ +static void PCtoRDRSecure( void ) +{ + TRACE_DEBUG("PCtoRDRSecure\n\r"); + + TRACE_DEBUG("For user\n\r"); +} + +//------------------------------------------------------------------------------ +/// Command Pipe, Bulk-OUT Messages +/// This command is used to manage motorized type CCID functionality. +/// The Lock Card function is used to hold the ICC. +/// This prevents an ICC from being easily removed from the CCID. +/// The Unlock Card function is used to remove the hold initiated by the Lock +/// Card function +//------------------------------------------------------------------------------ +static void PCtoRDRMechanical( void ) +{ + TRACE_DEBUG("PCtoRDRMechanical\n\r"); + TRACE_DEBUG("Not implemented\n\r"); + + RDRtoPCSlotStatus(); +} + +//------------------------------------------------------------------------------ +/// Command Pipe, Bulk-OUT Messages +/// This command is used with the Control pipe Abort request to tell the CCID +/// to stop any current transfer at the specified slot and return to a state +/// where the slot is ready to accept a new command pipe Bulk-OUT message. +//------------------------------------------------------------------------------ +static void PCtoRDRAbort( void ) +{ + TRACE_DEBUG("PCtoRDRAbort\n\r"); + + RDRtoPCSlotStatus(); +} + +//------------------------------------------------------------------------------ +/// Command Pipe, Bulk-OUT Messages +/// This command is used to manually set the data rate and clock frequency of +/// a specific slot. +//------------------------------------------------------------------------------ +static void PCtoRDRSetDataRateAndClockFrequency( void ) +{ + unsigned int dwClockFrequency; + unsigned int dwDataRate; + + TRACE_DEBUG("PCtoRDRSetDatarateandClockFrequency\n\r"); + + dwClockFrequency = ccidDriver.sCcidCommand.APDU[0] + + (ccidDriver.sCcidCommand.APDU[1]<<8) + + (ccidDriver.sCcidCommand.APDU[2]<<16) + + (ccidDriver.sCcidCommand.APDU[3]<<24); + + dwDataRate = ccidDriver.sCcidCommand.APDU[4] + + (ccidDriver.sCcidCommand.APDU[5]<<8) + + (ccidDriver.sCcidCommand.APDU[6]<<16) + + (ccidDriver.sCcidCommand.APDU[7]<<24); + + ISO7816_SetDataRateandClockFrequency( dwClockFrequency, dwDataRate ); + + RDRtoPCDataRateAndClockFrequency( dwClockFrequency, dwDataRate ); + +} + +//------------------------------------------------------------------------------ +/// Report the CMD_NOT_SUPPORTED error to the host +//------------------------------------------------------------------------------ +static void vCCIDCommandNotSupported( void ) +{ + // Command not supported + // vCCIDReportError(CMD_NOT_SUPPORTED); + + TRACE_DEBUG("CMD_NOT_SUPPORTED\n\r"); + + // Header fields settings + ccidDriver.sCcidMessage.bMessageType = RDR_TO_PC_SLOTSTATUS; + ccidDriver.sCcidMessage.wLength = 0; + ccidDriver.sCcidMessage.bSpecific = 0; + + ccidDriver.sCcidMessage.bStatus |= ICC_CS_FAILED; + + // Send the response to the host + //vCCIDSendResponse(); +} + +//------------------------------------------------------------------------------ +/// Sent CCID response on USB +//------------------------------------------------------------------------------ +static void vCCIDSendResponse( void ) +{ + unsigned char bStatus; + + do { + bStatus = USBD_Write( CCID_EPT_DATA_IN, (void*)&ccidDriver.sCcidMessage, + ccidDriver.sCcidMessage.bSizeToSend, 0, 0 ); + } + while (bStatus != USBD_STATUS_SUCCESS); +} + + +//------------------------------------------------------------------------------ +/// Description: CCID Command dispatcher +//------------------------------------------------------------------------------ +static void CCIDCommandDispatcher( void ) +{ + unsigned char MessageToSend = 0; + + //TRACE_DEBUG("Command: 0x%X 0x%x 0x%X 0x%X 0x%X 0x%X 0x%X\n\r\n\r", + // (unsigned int)ccidDriver.sCcidCommand.bMessageType, + // (unsigned int)ccidDriver.sCcidCommand.wLength, + // (unsigned int)ccidDriver.sCcidCommand.bSlot, + // (unsigned int)ccidDriver.sCcidCommand.bSeq, + // (unsigned int)ccidDriver.sCcidCommand.bSpecific_0, + // (unsigned int)ccidDriver.sCcidCommand.bSpecific_1, + // (unsigned int)ccidDriver.sCcidCommand.bSpecific_2); + + // Check the slot number + if ( ccidDriver.sCcidCommand.bSlot > 0 ) { + + TRACE_ERROR("BAD_SLOT_NUMBER\n\r"); + } + + TRACE_DEBUG("typ=0x%X\n\r", ccidDriver.sCcidCommand.bMessageType); + + ccidDriver.sCcidMessage.bStatus = 0; + + ccidDriver.sCcidMessage.bSeq = ccidDriver.sCcidCommand.bSeq; + ccidDriver.sCcidMessage.bSlot = ccidDriver.sCcidCommand.bSlot; + + ccidDriver.sCcidMessage.bSizeToSend = sizeof(S_ccid_bulk_in_header)-(ABDATA_SIZE+1); + + + // Command dispatcher + switch ( ccidDriver.sCcidCommand.bMessageType ) { + + case PC_TO_RDR_ICCPOWERON: + PCtoRDRIccPowerOn(); + MessageToSend = 1; + break; + + case PC_TO_RDR_ICCPOWEROFF: + PCtoRDRIccPowerOff(); + MessageToSend = 1; + break; + + case PC_TO_RDR_GETSLOTSTATUS: + PCtoRDRGetSlotStatus(); + MessageToSend = 1; + break; + + case PC_TO_RDR_XFRBLOCK: + PCtoRDRXfrBlock(); + MessageToSend = 1; + break; + + case PC_TO_RDR_GETPARAMETERS: + PCtoRDRGetParameters(); + MessageToSend = 1; + break; + + case PC_TO_RDR_RESETPARAMETERS: + PCtoRDRResetParameters(); + MessageToSend = 1; + break; + + case PC_TO_RDR_SETPARAMETERS: + PCtoRDRSetParameters(); + MessageToSend = 1; + break; + + case PC_TO_RDR_ESCAPE: + PCtoRDREscape(); + MessageToSend = 1; + break; + + case PC_TO_RDR_ICCCLOCK: + PCtoRDRICCClock(); + MessageToSend = 1; + break; + + case PC_TO_RDR_T0APDU: + // Only CCIDs reporting a short or extended APDU level in the dwFeatures + // field of the CCID class descriptor may take this command into account. + if( (CCID_FEATURES_EXC_SAPDU == (CCID_FEATURES_EXC_SAPDU&configurationDescriptorsFS.ccid.dwFeatures)) + || (CCID_FEATURES_EXC_APDU == (CCID_FEATURES_EXC_APDU &configurationDescriptorsFS.ccid.dwFeatures)) ) { + + // command supported + PCtoRDRtoAPDU(); + } + else { + // command not supported + TRACE_DEBUG("PC_TO_RDR_T0APDU\n\r"); + vCCIDCommandNotSupported(); + } + MessageToSend = 1; + break; + + case PC_TO_RDR_SECURE: + PCtoRDRSecure(); + MessageToSend = 1; + break; + + case PC_TO_RDR_MECHANICAL: + PCtoRDRMechanical(); + MessageToSend = 1; + break; + + case PC_TO_RDR_ABORT: + PCtoRDRAbort(); + MessageToSend = 1; + break; + + case PC_TO_RDR_SETDATARATEANDCLOCKFREQUENCY: + PCtoRDRSetDataRateAndClockFrequency(); + MessageToSend = 1; + break; + + default: + TRACE_DEBUG("default: 0x%X\n\r", ccidDriver.sCcidCommand.bMessageType); + vCCIDCommandNotSupported(); + MessageToSend = 1; + break; + + } + + if( MessageToSend == 1 ) { + vCCIDSendResponse(); + } +} + + +//------------------------------------------------------------------------------ +/// SETUP request handler for a CCID device +/// \param pRequest Pointer to a USBGenericRequest instance +//------------------------------------------------------------------------------ +static void CCID_RequestHandler(const USBGenericRequest *pRequest) +{ + TRACE_DEBUG("CCID_RHl\n\r"); + + // Check if this is a class request + if (USBGenericRequest_GetType(pRequest) == USBGenericRequest_CLASS) { + + // Check if the request is supported + switch (USBGenericRequest_GetRequest(pRequest)) { + + case CCIDGenericRequest_ABORT: + TRACE_DEBUG("CCIDGenericRequest_ABORT\n\r"); + break; + + case CCIDGenericRequest_GET_CLOCK_FREQUENCIES: + TRACE_DEBUG("Not supported\n\r"); + // A CCID with bNumClockSupported equal to 00h does not have + // to support this request + break; + + case CCIDGenericRequest_GET_DATA_RATES: + TRACE_DEBUG("Not supported\n\r"); + // A CCID with bNumDataRatesSupported equal to 00h does not have + // to support this request. + break; + + default: + TRACE_WARNING( "CCIDDriver_RequestHandler: Unsupported request (%d)\n\r", + USBGenericRequest_GetRequest(pRequest)); + USBD_Stall(0); + } + } + + else if (USBGenericRequest_GetType(pRequest) == USBGenericRequest_STANDARD) { + + // Forward request to the standard handler + USBDDriver_RequestHandler(&(ccidDriver.usbdDriver), pRequest); + } + else { + + // Unsupported request type + TRACE_WARNING( "CCIDDriver_RequestHandler: Unsupported request type (%d)\n\r", + USBGenericRequest_GetType(pRequest)); + USBD_Stall(0); + } +} + + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +/// Optional callback re-implementation +//------------------------------------------------------------------------------ +#if !defined(NOAUTOCALLBACK) +// not static function +void USBDCallbacks_RequestReceived(const USBGenericRequest *request) +{ + CCID_RequestHandler(request); +} +#endif + + +//------------------------------------------------------------------------------ +/// Handles SmartCart request +//------------------------------------------------------------------------------ +void CCID_SmartCardRequest( void ) +{ + unsigned char bStatus; + + do { + + bStatus = CCID_Read( (void*)&ccidDriver.sCcidCommand, + sizeof(S_ccid_bulk_out_header), + (TransferCallback)&CCIDCommandDispatcher, + (void*)0 ); + } + while (bStatus != USBD_STATUS_SUCCESS); + +} + +//------------------------------------------------------------------------------ +/// Initializes the CCID device driver. +//------------------------------------------------------------------------------ +void CCIDDriver_Initialize( void ) +{ + TRACE_DEBUG("CCID_Init\n\r"); + USBDDriver_Initialize(&(ccidDriver.usbdDriver), + &ccidDriverDescriptors, + 0); // Multiple interface settings not supported + USBD_Init(); +} + +//------------------------------------------------------------------------------ +/// Reads data from the Data OUT endpoint +/// \param pBuffer Buffer to store the received data +/// \param dLength data buffer length +/// \param fCallback Optional callback function +/// \param pArgument Optional parameter for the callback function +/// \return USBD_STATUS_LOCKED or USBD_STATUS_SUCCESS +//------------------------------------------------------------------------------ +unsigned char CCID_Read(void *pBuffer, + unsigned int dLength, + TransferCallback fCallback, + void *pArgument) +{ + return USBD_Read(CCID_EPT_DATA_OUT, pBuffer, dLength, fCallback, pArgument); +} + +//------------------------------------------------------------------------------ +/// Sends data through the Data IN endpoint +/// \param pBuffer Buffer holding the data to transmit +/// \param dLength Length of data buffer +/// \param fCallback Optional callback function +/// \param pArgument Optional parameter for the callback function +/// \return USBD_STATUS_LOCKED or USBD_STATUS_SUCCESS +//------------------------------------------------------------------------------ +unsigned char CCID_Write(void *pBuffer, + unsigned int dLength, + TransferCallback fCallback, + void *pArgument) +{ + return USBD_Write(CCID_EPT_DATA_IN, pBuffer, dLength, fCallback, pArgument); +} + +//------------------------------------------------------------------------------ +/// Sends data through the interrupt endpoint, ICC insertion event +/// RDR_to_PC_NotifySlotChange +/// \return USBD_STATUS_LOCKED or USBD_STATUS_SUCCESS +//------------------------------------------------------------------------------ +unsigned char CCID_Insertion( void ) +{ + TRACE_DEBUG("CCID_Insertion\n\r"); + + // Build the Interrupt-IN message + ccidDriver.BufferINT[0] = RDR_TO_PC_NOTIFYSLOTCHANGE; + ccidDriver.BufferINT[1] = ICC_INSERTED_EVENT; + ccidDriver.SlotStatus = ICC_INSERTED_EVENT; + + // Notify the host that a ICC is inserted + return USBD_Write( CCID_EPT_NOTIFICATION, ccidDriver.BufferINT, 2, 0, 0 ); +} + +//------------------------------------------------------------------------------ +/// Sends data through the interrupt endpoint, ICC removal event +/// RDR_to_PC_NotifySlotChange +/// \return USBD_STATUS_LOCKED or USBD_STATUS_SUCCESS +//------------------------------------------------------------------------------ +unsigned char CCID_Removal( void ) +{ + TRACE_DEBUG("CCID_Removal\n\r"); + + // Build the Interrupt-IN message + ccidDriver.BufferINT[0] = RDR_TO_PC_NOTIFYSLOTCHANGE; + ccidDriver.BufferINT[1] = ICC_NOT_PRESENT; + ccidDriver.SlotStatus = ICC_NOT_PRESENT; + + // Notify the host that a ICC is inserted + return USBD_Write( CCID_EPT_NOTIFICATION, ccidDriver.BufferINT, 2, 0, 0 ); +} + +//------------------------------------------------------------------------------ +/// Interrupt-IN Messages +/// This message is sent when any bit in the bHardwareErrorCode field is set. +/// If this message is sent when there is no “outstanding” command, the bSeq +/// field will be undefined. +/// \param bSlot ICC slot number +/// \param bSeq Sequence number of the bulk OUT command when the hardware error +/// occured +/// \param bHardwareErrorCode Hardware error code +/// \return USBD_STATUS_LOCKED or USBD_STATUS_SUCCESS +//------------------------------------------------------------------------------ +unsigned char RDRtoPCHardwareError( unsigned char bSlot, + unsigned char bSeq, + unsigned char bHardwareErrorCode ) +{ + TRACE_DEBUG("RDRtoPCHardwareError\n\r"); + + // Build the Interrupt-IN message + ccidDriver.BufferINT[0] = RDR_TO_PC_HARDWAREERROR; + ccidDriver.BufferINT[1] = bSlot; + ccidDriver.BufferINT[2] = bSeq; + ccidDriver.BufferINT[3] = bHardwareErrorCode; + + // Notify the host that a ICC is inserted + return USBD_Write( CCID_EPT_NOTIFICATION, ccidDriver.BufferINT, 4, 0, 0 ); +} + + diff --git a/at91lib/usb/device/ccid/cciddriver.h b/at91lib/usb/device/ccid/cciddriver.h new file mode 100644 index 0000000..8bac286 --- /dev/null +++ b/at91lib/usb/device/ccid/cciddriver.h @@ -0,0 +1,378 @@ +/* ---------------------------------------------------------------------------- + * 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 methods for using a CCID device driver. +/// +/// !Usage +/// +/// -# CCIDDriver_Initialize +/// -# CCID_Read +/// -# CCID_Write +/// -# CCID_SmartCardRequest +/// -# CCID_Insertion +/// -# CCID_Removal +/// -# RDRtoPCHardwareError +//------------------------------------------------------------------------------ + +#ifndef CCID_DRIVER_H +#define CCID_DRIVER_H + +/// For reference, the absolute maximum block size +/// for a TPDU T=0 block is 260 bytes (5 bytes command; 255 bytes data), or +/// for a TPDU T=1 block is 259 bytes, or +/// for a short APDU T=1 block is 261 bytes, or +/// for an extended APDU T=1 block is 65544 bytes. +#define ABDATA_SIZE 260 + +/// define protocol T=0 +#define PROTOCOL_TO 0 +/// define protocol T=1 +#define PROTOCOL_T1 1 + +/// define for dwFeatures see Table 5.1-1 Smart Card Device Class Descriptors +/// No special characteristics +#define CCID_FEATURES_NADA 0x00000000 +/// Automatic parameter configuration based on ATR data +#define CCID_FEATURES_AUTO_PCONF 0x00000002 +/// Automatic activation of ICC on inserting +#define CCID_FEATURES_AUTO_ACTIV 0x00000004 +/// Automatic ICC voltage selection +#define CCID_FEATURES_AUTO_VOLT 0x00000008 +/// Automatic ICC clock frequency change according to active parameters provided +/// by the Host or self determined +#define CCID_FEATURES_AUTO_CLOCK 0x00000010 +/// Automatic baud rate change according to active parameters provided by the +/// Host or self determined +#define CCID_FEATURES_AUTO_BAUD 0x00000020 +/// Automatic parameters negotiation made by the CCID (use of warm or cold +/// resets or PPS according to a manufacturer proprietary algorithm to select +/// the communication parameters with the ICC) +#define CCID_FEATURES_AUTO_PNEGO 0x00000040 +/// Automatic PPS made by the CCID according to the active parameters +#define CCID_FEATURES_AUTO_PPS 0x00000080 +/// CCID can set ICC in clock stop mode +#define CCID_FEATURES_ICCSTOP 0x00000100 +/// NAD value other than 00 accepted (T=1 protocol in use) +#define CCID_FEATURES_NAD 0x00000200 +/// Automatic IFSD exchange as first exchange (T=1 protocol in use) +#define CCID_FEATURES_AUTO_IFSD 0x00000400 +/// TPDU level exchanges with CCID +#define CCID_FEATURES_EXC_TPDU 0x00010000 +/// Short APDU level exchange with CCID +#define CCID_FEATURES_EXC_SAPDU 0x00020000 +/// Short and Extended APDU level exchange with CCID +#define CCID_FEATURES_EXC_APDU 0x00040000 +/// USB Wake up signaling supported on card insertion and removal +#define CCID_FEATURES_WAKEUP 0x00100000 + +//------------------------------------------------------------------------------ +// Types +//------------------------------------------------------------------------------ + +/// Bulk CCID Message header structure +typedef struct +{ + unsigned char bMessageType; + /// Message-specific data length + unsigned long wLength; + /// Identifies the slot number for this command + unsigned char bSlot; + /// Sequence number for command. + unsigned char bSeq; + /// Slot status register + unsigned char bStatus; + /// Slot error + unsigned char bError; + /// specific register + unsigned char bSpecific; + /// Data block sent to the CCID. + unsigned char abData[ABDATA_SIZE]; + unsigned char bSizeToSend; +} __attribute__ ((packed)) S_ccid_bulk_in_header; + +/// 6.1 Bulk Transfers +typedef struct +{ + unsigned char bMessageType; + /// Message-specific data length + unsigned long wLength; + /// Identifies the slot number for this command + unsigned char bSlot; + /// Sequence number for command. + unsigned char bSeq; + /// specific register + unsigned char bSpecific_0; + unsigned char bSpecific_1; + unsigned char bSpecific_2; + /// Application Protocol Data Unit + unsigned char APDU[ABDATA_SIZE]; +} __attribute__ ((packed)) S_ccid_bulk_out_header; + + +/// 6.1.11.2 PIN Verification Data Structure +typedef struct +{ + /// Number of seconds. + unsigned char bTimerOut; + /// Several parameters for the PIN format options + unsigned char bmFormatString; + /// Define the length of the PIN to present in the APDU command + unsigned char bmPINBlockString; + /// Allows the length PIN insertion in the APDU command + unsigned char bmPINLengthFormat; + /// Minimum PIN size in digit and Maximum PIN size in digit + unsigned char wPINMaxExtraDigit; + /// The value is a bit wise OR operation. + unsigned char bEntryValidationCondition; + /// Number of messages to display for the PIN modify command + unsigned char bNumberMessage; + /// Language used to display the messages. + unsigned char wLangId; + /// Message index in the Reader message table + unsigned char bMsgIndex; + /// T=1 I-block prologue field to use + unsigned char bTeoPrologue[3]; + /// APDU to send to the ICC + unsigned char abPINApdu[255]; +}__attribute__ ((packed)) S_ccid_PIN_Verification; + + +/// 6.1.11.7 PIN Modification Data Structure +typedef struct +{ + /// Number of seconds. If 00h then CCID default value is used. + unsigned char bTimeOut; + /// Several parameters for the PIN format options (defined in § 6.1.11.4) + unsigned char bmFormatString4; + /// Define the length of the PIN to present in the APDU command + unsigned char bmPINBlockString; + /// Allows the length PIN insertion in the APDU command (defined in § 6.1.11.6) + unsigned char bmPinLengthFormat; + /// Insertion position offset in byte for the current PIN + unsigned char bInsertionOffsetOld; + /// Insertion position offset in byte for the new PIN + unsigned char bInsertionOffsetNew; + /// XXYYh + /// XX: Minimum PIN size in digit + /// YY: Maximum PIN size in digit + unsigned char wPINMaxExtraDigit; + /// 00h,01h,02h,03h + /// Indicates if a confirmation is requested before acceptance of a new PIN (meaning that the user has to enter this new PIN twice before it is accepted) + /// Indicates if the current PIN must be entered and set in the same APDU field of not. + unsigned char bConfirmPIN; + /// The value is a bit wise OR operation. + /// 01h Max size reached + /// 02h Validation key pressed + /// 04h Timeout occurred + unsigned char bEntryValidationCondition; + /// 00h,01h,02h,03h,or FFh + /// Number of messages to display for the PIN modify command. + unsigned char bNumberMessage; + /// Language used to display the messages. The 16 bit + unsigned char wLangId; + /// Message index in the Reader message table (should be 00h or 01h). + unsigned char bMsgIndex1; + /// Message index in the Reader message table (should be 01h or 02h). + unsigned char bMsgIndex2; + /// Message index in the Reader message table (should be 02h). + unsigned char bMsgIndex3; + /// T=1 I-block prologue field to use. Significant only if protocol in use is T=1. + unsigned char bTeoPrologue[3]; + /// Byte array APDU to send to the ICC + unsigned char abPINApdu[255]; +}__attribute__ ((packed)) S_ccid_PIN_Modification; + +/// Protocol Data Structure for Protocol T=0 (bProtocolNum=0, dwLength=00000005h) +typedef struct +{ + /// B7-4 – FI – Index into the table 7 in ISO/IEC 7816-3:1997 selecting a + /// clock rate conversion factor + /// B3-0 – DI - Index into the table 8 in ISO/IEC 7816-3:1997 selecting a + /// baud rate conversion factor + unsigned char bmFindexDindex; + /// For T=0 ,B0 – 0b, B7-2 – 000000b + /// B1 – Convention used (b1=0 for direct, b1=1 for inverse) + unsigned char bmTCCKST0; // 0 to 2 + /// Extra Guardtime between two characters. Add 0 to 254 etu to the normal + /// guardtime of 12etu. FFh is the same as 00h. + unsigned char bGuardTimeT0; // 0 to FF + /// WI for T=0 used to define WWT + unsigned char bWaitingIntegerT0; // 0 to FF + /// ICC Clock Stop Support + /// 00 = Stopping the Clock is not allowed + /// 01 = Stop with Clock signal Low + /// 02 = Stop with Clock signal High + /// 03 = Stop with Clock either High or Low + unsigned char bClockStop; // 0 to 3 +} __attribute__ ((packed)) S_ccid_protocol_t0; + + +/// Protocol Data Structure for Protocol T=1 (bProtocolNum=1, dwLength=00000007h) +typedef struct +{ + /// B7-4 – FI – Index into the table 7 in ISO/IEC 7816-3:1997 selecting a + /// clock rate conversion factor + /// B3-0 – DI - Index into the table 8 in ISO/IEC 7816-3:1997 selecting a + /// baud rate conversion factor + unsigned char bmFindexDindex; + /// For T=1, B7-2 – 000100b + /// B0 – Checksum type (b0=0 for LRC, b0=1 for CRC + /// B1 – Convention used (b1=0 for direct, b1=1 for inverse) + unsigned char bmTCCKST1; // 10h, 11h, 12h, 13h + /// Extra Guardtime (0 to 254 etu between two characters). + /// If value is FFh, then guardtime is reduced by 1. + unsigned char bGuardTimeT1; // 0 to FF + /// B7-4 = BWI + /// B3-0 = CWI + unsigned char bmWaitingIntegersT1; // 0 to 9 + /// ICC Clock Stop Support + /// 00 = Stopping the Clock is not allowed + /// 01 = Stop with Clock signal Low + /// 02 = Stop with Clock signal High + /// 03 = Stop with Clock either High or Low + unsigned char bClockStop; // 0 to 3 + /// Size of negotiated IFSC + unsigned char bIFSC; // 0 to FE + /// Nad value used by CCID + unsigned char bNadValue; // 0 to FF +} __attribute__ ((packed)) S_ccid_protocol_t1; + + +/// Identifies the length of type of subordinate descriptors of a CCID device +/// Table 5.1-1 Smart Card Device Class descriptors +typedef struct +{ + /// Size of this descriptor, in bytes. + unsigned char bLength; + /// Functional Descriptor type + unsigned char bDescriptorType; + /// Integrated Circuit(s) Cards Interface Devices (CCID) Specification + /// Release Number + unsigned short bcdCCID; + /// Index of the highest available slot. An USB-ICC is regarded as a single + /// slot CCID. + unsigned char bMaxSlotIndex; + /// This value indicates what voltages the CCID can supply to its slots. + /// It is a bitwise OR operation performed on the following values: + /// - 01h 5.0V + /// - 02h 3.0V + /// - 04h 1.8V + /// Other bits are RFU. + unsigned char bVoltageSupport; + /// RRRR –Upper Word- is RFU = 0000h + /// PPPP –Lower Word- Encodes the supported protocol types. A ‘1’ in a given + /// bit position indicates support for the associated ISO protocol. + /// 0001h = Protocol T=0 + /// 0002h = Protocol T=1 + /// All other bits are reserved and must be set to zero. The field is + /// intended to correspond to the PCSC specification definitions. + unsigned long dwProtocols; + /// Default ICC clock frequency in KHz. This is an integer value. + unsigned long dwDefaultClock; + /// Maximum supported ICC clock frequency in KHz. This is an integer value. + unsigned long dwMaximumClock; + /// The number of clock frequencies that are supported by the CCID. If the + /// value is 00h, the supported clock frequencies are assumed to be the + /// default clock frequency defined by dwDefaultClock and the maximum clock + /// frequency defined by dwMaximumClock. + unsigned char bNumClockSupported; + /// Default ICC I/O data rate in bps. This is an integer value + unsigned long dwDataRate; + /// Maximum supported ICC I/O data rate in bps + unsigned long dwMaxDataRate; + /// The number of data rates that are supported by the CCID. + unsigned char bNumDataRatesSupported; + /// Indicates the maximum IFSD supported by CCID for protocol T=1. + unsigned long dwMaxIFSD; + /// - RRRR-Upper Word- is RFU = 0000h + /// - PPPP-Lower Word- encodes the supported protocol types. A ‘1’ in a given + /// bit position indicates support for the associated protocol. + /// 0001h indicates support for the 2-wire protocol 1 + /// 0002h indicates support for the 3-wire protocol 1 + /// 0004h indicates support for the I2C protocol 1 + /// All other values are outside of this specification, and must be handled + /// by vendor-supplied drivers. + unsigned long dwSynchProtocols; + /// The value is a bitwise OR operation performed on the following values: + /// - 00000000h No special characteristics + /// - 00000001h Card accept mechanism 2 + /// - 00000002h Card ejection mechanism 2 + /// - 00000004h Card capture mechanism 2 + /// - 00000008h Card lock/unlock mechanism + unsigned long dwMechanical; + /// This value indicates what intelligent features the CCID has. + unsigned long dwFeatures; + /// For extended APDU level the value shall be between 261 + 10 (header) and + /// 65544 +10, otherwise the minimum value is the wMaxPacketSize of the + /// Bulk-OUT endpoint. + unsigned long dwMaxCCIDMessageLength; + /// Significant only for CCID that offers an APDU level for exchanges. + unsigned char bClassGetResponse; + /// Significant only for CCID that offers an extended APDU level for exchanges. + unsigned char bClassEnvelope; + /// Number of lines and characters for the LCD display used to send messages for PIN entry. + unsigned short wLcdLayout; + /// This value indicates what PIN support features the CCID has. + unsigned char bPINSupport; + /// Maximum number of slots which can be simultaneously busy. + unsigned char bMaxCCIDBusySlots; + +} __attribute__ ((packed)) CCIDDescriptor; + +//------------------------------------------------------------------------------ +// Exported functions +//------------------------------------------------------------------------------ + +extern unsigned char RDRtoPCHardwareError( unsigned char bSlot, + unsigned char bSeq, + unsigned char bHardwareErrorCode ); + +#if !defined(NOAUTOCALLBACK) +extern void USBDCallbacks_RequestReceived(const USBGenericRequest *pRequest); +#endif +extern void CCID_SmartCardRequest( void ); +extern void CCIDDriver_Initialize( void ); +extern unsigned char CCID_Read(void *pBuffer, + unsigned int dLength, + TransferCallback fCallback, + void *pArgument); +extern unsigned char CCID_Write(void *pBuffer, + unsigned int dLength, + TransferCallback fCallback, + void *pArgument); +extern unsigned char CCID_Insertion( void ); +extern unsigned char CCID_Removal( void ); + +#endif //#ifndef CCID_DRIVER_H + diff --git a/at91lib/usb/device/ccid/cciddriverdescriptors.h b/at91lib/usb/device/ccid/cciddriverdescriptors.h new file mode 100644 index 0000000..2daea67 --- /dev/null +++ b/at91lib/usb/device/ccid/cciddriverdescriptors.h @@ -0,0 +1,152 @@ +/* ---------------------------------------------------------------------------- + * 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. + * ---------------------------------------------------------------------------- + */ + +//------------------------------------------------------------------------------ +// Title: cciddriverdescriptors.h +// +// About: Purpose +// Definitions of the descriptors required by the ccid device driver. +// DWG_Smart-Card_CCID_Rev110.pdf +//------------------------------------------------------------------------------ + +#ifndef CCID_DRIVER_DESCRIPTORS_H +#define CCID_DRIVER_DESCRIPTORS_H + +//------------------------------------------------------------------------------ +// Definitions +//------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------ +// Constants: Endpoints +// CCID_EPT_DATA_OUT endpoint data out bulk 1 +// CCID_EPT_DATA_IN endpoint data in bulk 2 +// CCID_EPT_NOTIFICATION endpoint data interupt 3 +//------------------------------------------------------------------------------ +#define CCID_EPT_DATA_OUT 1 +#define CCID_EPT_DATA_IN 2 +#define CCID_EPT_NOTIFICATION 3 + +//------------------------------------------------------------------------------ +// USB-ICC protocol +//------------------------------------------------------------------------------ +// CCID specification version 1.10 +#define CCID1_10 0x0110 + +#define SMART_CARD_DEVICE_CLASS 0x0B +// Smart Card Device Class Descriptor Type +#define CCID_DECRIPTOR_TYPE 0x21 + +// Table 5.3-1 Summary of CCID Class Specific Request +#define CCIDGenericRequest_ABORT 0x01 +#define CCIDGenericRequest_GET_CLOCK_FREQUENCIES 0x02 +#define CCIDGenericRequest_GET_DATA_RATES 0x03 + +// 6.1 Command Pipe, Bulk-OUT Messages +#define PC_TO_RDR_ICCPOWERON 0x62 +#define PC_TO_RDR_ICCPOWEROFF 0x63 +#define PC_TO_RDR_GETSLOTSTATUS 0x65 +#define PC_TO_RDR_XFRBLOCK 0x6F +#define PC_TO_RDR_GETPARAMETERS 0x6C +#define PC_TO_RDR_RESETPARAMETERS 0x6D +#define PC_TO_RDR_SETPARAMETERS 0x61 +#define PC_TO_RDR_ESCAPE 0x6B +#define PC_TO_RDR_ICCCLOCK 0x6E +#define PC_TO_RDR_T0APDU 0x6A +#define PC_TO_RDR_SECURE 0x69 +#define PC_TO_RDR_MECHANICAL 0x71 +#define PC_TO_RDR_ABORT 0x72 +#define PC_TO_RDR_SETDATARATEANDCLOCKFREQUENCY 0x73 + +// 6.2 Response Pipe, Bulk-IN Messages +#define RDR_TO_PC_DATABLOCK 0x80 +#define RDR_TO_PC_SLOTSTATUS 0x81 +#define RDR_TO_PC_PARAMETERS 0x82 +#define RDR_TO_PC_ESCAPE 0x83 +#define RDR_TO_PC_DATARATEANDCLOCKFREQUENCY 0x84 + +// 6.3 Interrupt-IN Messages +#define RDR_TO_PC_NOTIFYSLOTCHANGE 0x50 +#define RDR_TO_PC_HARDWAREERROR 0x51 + +// Table 6.2-2 Slot error register when bmCommandStatus = 1 +#define CMD_ABORTED 0xFF +#define ICC_MUTE 0xFE +#define XFR_PARITY_ERROR 0xFD +#define XFR_OVERRUN 0xFC +#define HW_ERROR 0xFB +#define BAD_ATR_TS 0xF8 +#define BAD_ATR_TCK 0xF7 +#define ICC_PROTOCOL_NOT_SUPPORTED 0xF6 +#define ICC_CLASS_NOT_SUPPORTED 0xF5 +#define PROCEDURE_BYTE_CONFLICT 0xF4 +#define DEACTIVATED_PROTOCOL 0xF3 +#define BUSY_WITH_AUTO_SEQUENCE 0xF2 +#define PIN_TIMEOUT 0xF0 +#define PIN_CANCELLED 0xEF +#define CMD_SLOT_BUSY 0xE0 +// User defined 0xC0 to 0x81 +// Reserved for futur use 0x80 +// not supported incorrect message parameter 0x7F to 0x01 +// Command not supported 0x00 + +// CCID rev 1.1, p.27 +#define VOLTS_AUTO 0x00 +#define VOLTS_5_0 0x01 +#define VOLTS_3_0 0x02 +#define VOLTS_1_8 0x03 + +// 6.3.1 RDR_to_PC_NotifySlotChange +#define ICC_NOT_PRESENT 0x00 +#define ICC_PRESENT 0x01 +#define ICC_CHANGE 0x02 +#define ICC_INSERTED_EVENT ICC_PRESENT+ICC_CHANGE + +// ICCD: Table 6.1-8 Bitmap for bStatus field +#define ICC_BS_PRESENT_ACTIVATED 0x00 // USB-ICC is present and activated +#define ICC_BS_PRESENT_NOTACTIVATED 0x01 // USB-ICC is present but not activated +#define ICC_BS_NOTPRESENT 0x02 // USB-ICC is virtually not present +#define ICC_BS_RFU 0x03 // RFU +#define ICC_CS_NO_ERROR (0x00<<6) // Processed without error +#define ICC_CS_FAILED (0x01<<6) // Failed, error condition given by bError +#define ICC_CS_TIME_EXT (0x02<<6) // Time extension is requested +#define ICC_CS_RFU (0x03<<6) // RFU + +/* +#define NO_ERROR 0x00 +#define NO_EXTRA_BYTES 0x00 +#define CCID_FLAG_INITIAL_VALUE 0x05 +#define CCID_EVENT_SIZE 0x02 +#define STATUS_MASK 0x41 +*/ +//------------------------------------------------------------------------------ +// Structures +//------------------------------------------------------------------------------ + +#endif //#ifndef CCID_DRIVER_DESCRIPTORS_H + 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