summaryrefslogtreecommitdiff
path: root/at91lib/usb/device/core/USBD_UDP.c
diff options
context:
space:
mode:
Diffstat (limited to 'at91lib/usb/device/core/USBD_UDP.c')
-rw-r--r--at91lib/usb/device/core/USBD_UDP.c1224
1 files changed, 1224 insertions, 0 deletions
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 <board.h>
+#include <pio/pio.h>
+#include <utility/trace.h>
+#include <utility/led.h>
+#include <usb/common/core/USBEndpointDescriptor.h>
+#include <usb/common/core/USBGenericRequest.h>
+
+#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
+ /// <UDP_ENDPOINT_SENDING> or <UDP_ENDPOINT_RECEIVING>)
+ 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
+
personal git repositories of Harald Welte. Your mileage may vary