/* ---------------------------------------------------------------------------- * 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