From 98f9d442b44dbe2e3e4b3c8296be7e78d5d05450 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Sun, 24 Jul 2011 09:39:28 +0200 Subject: initial import of the usb ccid example for the sam7s --- at91lib/usb/device/core/USBD_UDP.c | 1224 ++++++++++++++++++++++++++++++++++++ 1 file changed, 1224 insertions(+) create mode 100644 at91lib/usb/device/core/USBD_UDP.c (limited to 'at91lib/usb/device/core/USBD_UDP.c') 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 + -- cgit v1.2.3