/* ---------------------------------------------------------------------------- * 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. * ---------------------------------------------------------------------------- */ /** \file \section Purpose Implementation of USB device functions on a UDP controller. See \ref usbd_api_method USBD API Methods. */ /** \addtogroup usbd_hal *@{*/ /*--------------------------------------------------------------------------- * Headers *---------------------------------------------------------------------------*/ #include "chip.h" #include "USBD_HAL.h" #include #include #include #include /*--------------------------------------------------------------------------- * Definitions *---------------------------------------------------------------------------*/ /** Indicates chip has an UDP Full Speed. */ #define CHIP_USB_UDP /** Indicates chip has an internal pull-up. */ #define CHIP_USB_PULLUP_INTERNAL /** Number of USB endpoints */ #define CHIP_USB_NUMENDPOINTS 8 /** Endpoints max paxcket size */ #define CHIP_USB_ENDPOINTS_MAXPACKETSIZE(i) \ ((i == 0) ? 64 : \ ((i == 1) ? 64 : \ ((i == 2) ? 64 : \ ((i == 3) ? 64 : \ ((i == 4) ? 512 : \ ((i == 5) ? 512 : \ ((i == 6) ? 64 : \ ((i == 7) ? 64 : 0 )))))))) /** Endpoints Number of Bank */ #define CHIP_USB_ENDPOINTS_BANKS(i) \ ((i == 0) ? 1 : \ ((i == 1) ? 2 : \ ((i == 2) ? 2 : \ ((i == 3) ? 1 : \ ((i == 4) ? 2 : \ ((i == 5) ? 2 : \ ((i == 6) ? 2 : \ ((i == 7) ? 2 : 0 )))))))) /** * \section UDP_registers_sec "UDP Register field values" * * This section lists the initialize values of UDP registers. * * \subsection Values * - UDP_RXDATA */ /** Bit mask for both banks of the UDP_CSR register. */ #define UDP_CSR_RXDATA_BK (UDP_CSR_RX_DATA_BK0 | UDP_CSR_RX_DATA_BK1) /** * \section endpoint_states_sec "UDP Endpoint states" * * This page lists the endpoint states. * * \subsection States * - UDP_ENDPOINT_DISABLED * - UDP_ENDPOINT_HALTED * - UDP_ENDPOINT_IDLE * - UDP_ENDPOINT_SENDING * - UDP_ENDPOINT_RECEIVING * - UDP_ENDPOINT_SENDINGM * - UDP_ENDPOINT_RECEIVINGM */ /** 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 /** Endpoint states: Endpoint is sending MBL */ #define UDP_ENDPOINT_SENDINGM 5 /** Endpoint states: Endpoint is receiving MBL */ #define UDP_ENDPOINT_RECEIVINGM 6 /** * \section udp_csr_register_access_sec "UDP CSR register access" * * This page lists the macros to access UDP CSR register. * * \comment * In a preemptive environment, set or clear the flag and wait for a time of * 1 UDPCK clock cycle and 1 peripheral clock cycle. However, RX_DATA_BK0, * TXPKTRDY, RX_DATA_BK1 require wait times of 3 UDPCK clock cycles and * 5 peripheral clock cycles before accessing DPR. * See datasheet * * !Macros * - CLEAR_CSR * - SET_CSR */ #if defined ( __CC_ARM ) #define nop() {volatile int h; for(h=0;h<10;h++){}} #elif defined ( __ICCARM__ ) #include #define nop() (__no_operation()) #elif defined ( __GNUC__ ) #define nop() __asm__ __volatile__ ( "nop" ) #endif /** Bitmap for all status bits in CSR. */ #define REG_NO_EFFECT_1_ALL UDP_CSR_RX_DATA_BK0 | UDP_CSR_RX_DATA_BK1 \ |UDP_CSR_STALLSENTISOERROR | UDP_CSR_RXSETUP \ |UDP_CSR_TXCOMP /** * 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 set to 1. */ #define SET_CSR(endpoint, flags) \ { \ volatile uint32_t reg; \ int32_t nop_count ; \ reg = UDP->UDP_CSR[endpoint] ; \ reg |= REG_NO_EFFECT_1_ALL; \ reg |= (flags); \ UDP->UDP_CSR[endpoint] = reg; \ for( nop_count=0; nop_count<15; nop_count++ ) {\ nop();\ }\ } /** * 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 clear to 0. */ #define CLEAR_CSR(endpoint, flags) \ { \ volatile uint32_t reg; \ int32_t nop_count ; \ reg = UDP->UDP_CSR[endpoint]; \ reg |= REG_NO_EFFECT_1_ALL; \ reg &= ~((uint32_t)(flags)); \ UDP->UDP_CSR[endpoint] = reg; \ for( nop_count=0; nop_count<15; nop_count++ ) {\ nop();\ }\ } /** Get Number of buffer in Multi-Buffer-List * \param i input index * \param o output index * \param size list size */ #define MBL_NbBuffer(i, o, size) (((i)>(o))?((i)-(o)):((i)+(size)-(o))) /** Buffer list is full */ #define MBL_FULL 1 /** Buffer list is null */ #define MBL_NULL 2 /*--------------------------------------------------------------------------- * Types *---------------------------------------------------------------------------*/ /** Describes header for UDP endpoint transfer. */ typedef struct { /** Optional callback to invoke when the transfer completes. */ void* fCallback; /** Optional argument to the callback function. */ void* pArgument; /** Transfer type */ uint8_t transType; } TransferHeader; /** Describes a transfer on a UDP endpoint. */ typedef struct { /** Optional callback to invoke when the transfer completes. */ TransferCallback fCallback; /** Optional argument to the callback function. */ void *pArgument; /** Transfer type */ uint16_t transType; /** Number of bytes which have been written into the UDP internal FIFO * buffers. */ int16_t buffered; /** Pointer to a data buffer used for emission/reception. */ uint8_t *pData; /** Number of bytes which have been sent/received. */ int32_t transferred; /** Number of bytes which have not been buffered/transferred yet. */ int32_t remaining; } Transfer; /** Describes Multi Buffer List transfer on a UDP endpoint. */ typedef struct { /** Optional callback to invoke when the transfer completes. */ MblTransferCallback fCallback; /** Optional argument to the callback function. */ void *pArgument; /** Transfer type */ volatile uint8_t transType; /** List state (OK, FULL, NULL) (run time) */ uint8_t listState; /** Multi-Buffer List size */ uint16_t listSize; /** Pointer to multi-buffer list */ USBDTransferBuffer *pMbl; /** Offset number of buffers to start transfer */ uint16_t offsetSize; /** Current processing buffer index (run time) */ uint16_t outCurr; /** Loast loaded buffer index (run time) */ uint16_t outLast; /** Current buffer for input (run time) */ uint16_t inCurr; } MblTransfer; /** * Describes the state of an endpoint of the UDP controller. */ typedef struct { /* CSR */ //uint32_t CSR; /** Current endpoint state. */ volatile uint8_t state; /** Current reception bank (0 or 1). */ volatile uint8_t bank; /** Maximum packet size for the endpoint. */ volatile uint16_t size; /** Describes an ongoing transfer (if current state is either * UDP_ENDPOINT_SENDING or UDP_ENDPOINT_RECEIVING) */ union { TransferHeader transHdr; Transfer singleTransfer; MblTransfer mblTransfer; } transfer; } Endpoint; /*--------------------------------------------------------------------------- * Internal variables *---------------------------------------------------------------------------*/ /** Holds the internal state for each endpoint of the UDP. */ static Endpoint endpoints[CHIP_USB_NUMENDPOINTS]; /*--------------------------------------------------------------------------- * Internal Functions *---------------------------------------------------------------------------*/ /** * Enables the clock of the UDP peripheral. * \return 1 if peripheral status changed. */ static uint8_t UDP_EnablePeripheralClock(void) { if (!PMC_IsPeriphEnabled(ID_UDP)) { PMC_EnablePeripheral(ID_UDP); return 1; } return 0; } /** * Disables the UDP peripheral clock. */ static inline void UDP_DisablePeripheralClock(void) { PMC_DisablePeripheral(ID_UDP); } /** * Enables the 48MHz USB clock. */ static inline void UDP_EnableUsbClock(void) { REG_PMC_SCER = PMC_SCER_UDP; } /** * Disables the 48MHz USB clock. */ static inline void UDP_DisableUsbClock(void) { REG_PMC_SCDR = PMC_SCER_UDP; } /** * Enables the UDP transceiver. */ static inline void UDP_EnableTransceiver(void) { UDP->UDP_TXVC &= ~(uint32_t)UDP_TXVC_TXVDIS; } /** * Disables the UDP transceiver. */ static inline void UDP_DisableTransceiver(void) { UDP->UDP_TXVC |= UDP_TXVC_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(uint8_t bEndpoint, uint8_t bStatus) { Endpoint *pEndpoint = &(endpoints[bEndpoint]); // Check that endpoint was sending or receiving data if( (pEndpoint->state == UDP_ENDPOINT_RECEIVING) || (pEndpoint->state == UDP_ENDPOINT_SENDING)) { Transfer *pTransfer = (Transfer *)&(pEndpoint->transfer); uint32_t transferred = pTransfer->transferred; uint32_t remaining = pTransfer->remaining + pTransfer->buffered; TRACE_DEBUG_WP("EoT "); /* Endpoint returns in Idle state */ pEndpoint->state = UDP_ENDPOINT_IDLE; /* Reset descriptor values */ pTransfer->pData = 0; pTransfer->transferred = -1; pTransfer->buffered = -1; pTransfer->remaining = -1; // Invoke callback is present if (pTransfer->fCallback != 0) { ((TransferCallback) pTransfer->fCallback) (pTransfer->pArgument, bStatus, transferred, remaining); } else { TRACE_DEBUG_WP("NoCB "); } } else if ( (pEndpoint->state == UDP_ENDPOINT_RECEIVINGM) || (pEndpoint->state == UDP_ENDPOINT_SENDINGM) ) { MblTransfer *pTransfer = (MblTransfer*)&(pEndpoint->transfer); TRACE_DEBUG_WP("EoMT "); /* Endpoint returns in Idle state */ pEndpoint->state = UDP_ENDPOINT_IDLE; /* Reset transfer descriptor */ if (pTransfer->transType) { MblTransfer *pMblt = (MblTransfer*)&(pEndpoint->transfer); pMblt->listState = 0; pMblt->outCurr = pMblt->inCurr = pMblt->outLast = 0; } /* Invoke callback */ if (pTransfer->fCallback != 0) { ((MblTransferCallback) pTransfer->fCallback) (pTransfer->pArgument, bStatus); } else { TRACE_DEBUG_WP("NoCB "); } } } /** * Clears the correct reception flag (bank 0 or bank 1) of an endpoint * \param bEndpoint Index of endpoint */ static void UDP_ClearRxFlag(uint8_t bEndpoint) { Endpoint *pEndpoint = &(endpoints[bEndpoint]); // Clear flag and change banks if (pEndpoint->bank == 0) { CLEAR_CSR(bEndpoint, UDP_CSR_RX_DATA_BK0); // Swap bank if in dual-fifo mode if (CHIP_USB_ENDPOINTS_BANKS(bEndpoint) > 1) { pEndpoint->bank = 1; } } else { CLEAR_CSR(bEndpoint, UDP_CSR_RX_DATA_BK1); pEndpoint->bank = 0; } } /** * Update multi-buffer-transfer descriptors. * \param pTransfer Pointer to instance MblTransfer. * \param size Size of bytes that processed. * \param forceEnd Force the buffer END. * \return 1 if current buffer ended. */ static uint8_t UDP_MblUpdate(MblTransfer *pTransfer, USBDTransferBuffer * pBi, uint16_t size, uint8_t forceEnd) { /* Update transfer descriptor */ pBi->remaining -= size; /* Check if list NULL */ if (pTransfer->listState == MBL_NULL) { return 1; } /* Check if current buffer ended */ if (pBi->remaining == 0 || forceEnd || size == 0) { /* Process to next buffer */ if ((++ pTransfer->outCurr) == pTransfer->listSize) pTransfer->outCurr = 0; /* Check buffer NULL case */ if (pTransfer->outCurr == pTransfer->inCurr) pTransfer->listState = MBL_NULL; else { pTransfer->listState = 0; /* Continue transfer, prepare for next operation */ pBi = &pTransfer->pMbl[pTransfer->outCurr]; pBi->buffered = 0; pBi->transferred = 0; pBi->remaining = pBi->size; } return 1; } return 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 uint8_t UDP_MblWriteFifo(uint8_t bEndpoint) { Endpoint *pEndpoint = &(endpoints[bEndpoint]); MblTransfer *pTransfer = (MblTransfer*)&(pEndpoint->transfer); USBDTransferBuffer *pBi = &(pTransfer->pMbl[pTransfer->outCurr]); int32_t size; volatile uint8_t * pBytes; volatile uint8_t bufferEnd = 1; /* Get the number of bytes to send */ size = pEndpoint->size; if (size > pBi->remaining) size = pBi->remaining; TRACE_DEBUG_WP("w%d.%" PRId32 " ", pTransfer->outCurr, size); /* Record last accessed buffer */ pTransfer->outLast = pTransfer->outCurr; pBytes = &(pBi->pBuffer[pBi->transferred + pBi->buffered]); pBi->buffered += size; bufferEnd = UDP_MblUpdate(pTransfer, pBi, size, 0); /* Write packet in the FIFO buffer */ if (size) { int32_t c8 = size >> 3; int32_t c1 = size & 0x7; for (; c8; c8 --) { UDP->UDP_FDR[bEndpoint] = *(pBytes ++); UDP->UDP_FDR[bEndpoint] = *(pBytes ++); UDP->UDP_FDR[bEndpoint] = *(pBytes ++); UDP->UDP_FDR[bEndpoint] = *(pBytes ++); UDP->UDP_FDR[bEndpoint] = *(pBytes ++); UDP->UDP_FDR[bEndpoint] = *(pBytes ++); UDP->UDP_FDR[bEndpoint] = *(pBytes ++); UDP->UDP_FDR[bEndpoint] = *(pBytes ++); } for (; c1; c1 --) { UDP->UDP_FDR[bEndpoint] = *(pBytes ++); } } return bufferEnd; } /** * 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(uint8_t bEndpoint) { Endpoint *pEndpoint = &(endpoints[bEndpoint]); Transfer *pTransfer = (Transfer*)&(pEndpoint->transfer); int32_t 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) { 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(uint8_t bEndpoint, int32_t wPacketSize) { Endpoint *pEndpoint = &(endpoints[bEndpoint]); Transfer *pTransfer = (Transfer*)&(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) = (uint8_t) 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) { uint8_t *pData = (uint8_t *)pRequest; uint32_t i; // Copy packet for (i = 0; i < 8; i++) { *pData = (uint8_t) UDP->UDP_FDR[0]; pData++; } } /** * 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 uint8_t UDP_IsTransferFinished(uint8_t bEndpoint) { Endpoint *pEndpoint = &(endpoints[bEndpoint]); Transfer *pTransfer = (Transfer*)&(pEndpoint->transfer); // Check if it is a Control endpoint // -> Control endpoint must always finish their transfer with a zero-length // packet if ((UDP->UDP_CSR[bEndpoint] & UDP_CSR_EPTYPE_Msk) == UDP_CSR_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(uint8_t bEndpoint) { Endpoint *pEndpoint = &(endpoints[bEndpoint]); Transfer *pTransfer = (Transfer*)&(pEndpoint->transfer); MblTransfer *pMblt = (MblTransfer*)&(pEndpoint->transfer); uint32_t status = UDP->UDP_CSR[bEndpoint]; uint16_t wPacketSize; USBGenericRequest request; TRACE_DEBUG_WP("E%d ", bEndpoint); TRACE_DEBUG_WP("st:0x%" PRIX32 " ", status); // Handle interrupts // IN packet sent if ((status & UDP_CSR_TXCOMP) != 0) { TRACE_DEBUG_WP("Wr "); // Check that endpoint was in MBL Sending state if (pEndpoint->state == UDP_ENDPOINT_SENDINGM) { USBDTransferBuffer * pMbli = &(pMblt->pMbl[pMblt->outLast]); uint8_t bufferEnd = 0; TRACE_DEBUG_WP("TxM%d.%d ", pMblt->listState, pMbli->buffered); // End of transfer ? if (pMblt->listState == MBL_NULL && pMbli->buffered == 0) { pMbli->transferred += pMbli->buffered; pMbli->buffered = 0; // Disable interrupt UDP->UDP_IDR = 1 << bEndpoint; UDP_EndOfTransfer(bEndpoint, USBD_STATUS_SUCCESS); CLEAR_CSR(bEndpoint, UDP_CSR_TXCOMP); } else { // Transfer remaining data TRACE_DEBUG_WP("%d ", pEndpoint->size); if (pMbli->buffered > pEndpoint->size) { pMbli->transferred += pEndpoint->size; pMbli->buffered -= pEndpoint->size; } else { pMbli->transferred += pMbli->buffered; pMbli->buffered = 0; } // Send next packet if (CHIP_USB_ENDPOINTS_BANKS(bEndpoint) == 1) { // No double buffering bufferEnd = UDP_MblWriteFifo(bEndpoint); SET_CSR(bEndpoint, UDP_CSR_TXPKTRDY); CLEAR_CSR(bEndpoint, UDP_CSR_TXCOMP); } else { // Double buffering SET_CSR(bEndpoint, UDP_CSR_TXPKTRDY); CLEAR_CSR(bEndpoint, UDP_CSR_TXCOMP); bufferEnd = UDP_MblWriteFifo(bEndpoint); } if (bufferEnd && pMblt->fCallback) { ((MblTransferCallback) pTransfer->fCallback) (pTransfer->pArgument, USBD_STATUS_PARTIAL_DONE); } } } // Check that endpoint was in Sending state else 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 & UDP_CSR_EPTYPE_Msk) != UDP_CSR_EPTYPE_CTRL) { UDP->UDP_IDR = 1 << bEndpoint; } UDP_EndOfTransfer(bEndpoint, USBD_STATUS_SUCCESS); CLEAR_CSR(bEndpoint, UDP_CSR_TXCOMP); } else { // Transfer remaining data TRACE_DEBUG_WP(" %d ", pEndpoint->size); pTransfer->transferred += pEndpoint->size; pTransfer->buffered -= pEndpoint->size; // Send next packet if (CHIP_USB_ENDPOINTS_BANKS(bEndpoint) == 1) { // No double buffering UDP_WritePayload(bEndpoint); SET_CSR(bEndpoint, UDP_CSR_TXPKTRDY); CLEAR_CSR(bEndpoint, UDP_CSR_TXCOMP); } else { // Double buffering SET_CSR(bEndpoint, UDP_CSR_TXPKTRDY); CLEAR_CSR(bEndpoint, UDP_CSR_TXCOMP); UDP_WritePayload(bEndpoint); } } } else { // Acknowledge interrupt TRACE_ERROR("Error Wr%d, %x\n\r", bEndpoint, pEndpoint->state); CLEAR_CSR(bEndpoint, UDP_CSR_TXCOMP); } } // OUT packet received if ((status & UDP_CSR_RXDATA_BK) != 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 & UDP_CSR_EPTYPE_Msk) == UDP_CSR_EPTYPE_CTRL) && ((status & UDP_CSR_RXBYTECNT_Msk) == 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 & UDP_CSR_FORCESTALL) != 0) { // Discard STALLed data TRACE_DEBUG_WP("Discard "); UDP_ClearRxFlag(bEndpoint); } // NAK the data else { TRACE_DEBUG_WP("Nak "); UDP->UDP_IDR = 1 << bEndpoint; } } // Endpoint is in Read state else { // Retrieve data and store it into the current transfer buffer wPacketSize = (uint16_t) (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 & UDP_CSR_EPTYPE_Msk) != UDP_CSR_EPTYPE_CTRL) { UDP->UDP_IDR = 1 << bEndpoint; } UDP_EndOfTransfer(bEndpoint, USBD_STATUS_SUCCESS); } } } // STALL sent if ((status & UDP_CSR_STALLSENTISOERROR) != 0) { CLEAR_CSR(bEndpoint, UDP_CSR_STALLSENTISOERROR); if ( (status & UDP_CSR_EPTYPE_Msk) == UDP_CSR_EPTYPE_ISO_IN || (status & UDP_CSR_EPTYPE_Msk) == UDP_CSR_EPTYPE_ISO_OUT ) { TRACE_WARNING("Isoe [%d] ", bEndpoint); UDP_EndOfTransfer(bEndpoint, USBD_STATUS_ABORTED); } else { TRACE_WARNING("Sta 0x%X [%d] ", (int)status, bEndpoint); if (pEndpoint->state != UDP_ENDPOINT_HALTED) { TRACE_WARNING( "_ " ); // If the endpoint is not halted, clear the STALL condition CLEAR_CSR(bEndpoint, UDP_CSR_FORCESTALL); } } } // SETUP packet received if ((status & UDP_CSR_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, UDP_CSR_DIR); } // Acknowledge setup packet CLEAR_CSR(bEndpoint, UDP_CSR_RXSETUP); // Forward the request to the upper layer USBD_RequestHandler(0, &request); } } /** * 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 pEndpoint Pointer to Endpoint struct. * \param pData Pointer to a buffer with the data to send. * \param dLength Size of the data buffer. * \return USBD_STATUS_SUCCESS if the transfer has been started; * otherwise, the corresponding error status code. */ static inline uint8_t UDP_Write(uint8_t bEndpoint, const void *pData, uint32_t dLength) { Endpoint *pEndpoint = &(endpoints[bEndpoint]); Transfer *pTransfer = (Transfer*)&(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(%" PRIu32 ") ", bEndpoint, dLength); /* int i; for (i = 0; i < dLength; i++) { if (!(i%16)) { printf("\n\r"); } printf("0x%x ", ((uint8_t*)pData)[i]); } printf("\n\r"); */ /* Setup the transfer descriptor */ pTransfer->pData = (void *) pData; pTransfer->remaining = dLength; pTransfer->buffered = 0; pTransfer->transferred = 0; /* Send the first packet */ pEndpoint->state = UDP_ENDPOINT_SENDING; while((UDP->UDP_CSR[bEndpoint]&UDP_CSR_TXPKTRDY)==UDP_CSR_TXPKTRDY); UDP_WritePayload(bEndpoint); SET_CSR(bEndpoint, UDP_CSR_TXPKTRDY); /* If double buffering is enabled and there is data remaining, prepare another packet */ if ((CHIP_USB_ENDPOINTS_BANKS(bEndpoint) > 1) && (pTransfer->remaining > 0)) { UDP_WritePayload(bEndpoint); } /* Enable interrupt on endpoint */ UDP->UDP_IER = 1 << bEndpoint; return USBD_STATUS_SUCCESS; } /** * Sends data through a USB endpoint. Sets up the transfer descriptor list, * 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 transfer buffer in the list 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 pEndpoint Pointer to Endpoint struct. * \param pData Pointer to a buffer with the data to send. * \param dLength Size of the data buffer. * \return USBD_STATUS_SUCCESS if the transfer has been started; * otherwise, the corresponding error status code. */ static inline uint8_t UDP_AddWr(uint8_t bEndpoint, const void *pData, uint32_t dLength) { Endpoint *pEndpoint = &(endpoints[bEndpoint]); MblTransfer *pMbl = (MblTransfer*)&(pEndpoint->transfer); USBDTransferBuffer *pTx; /* Check parameter */ if (dLength >= 0x10000) return USBD_STATUS_INVALID_PARAMETER; /* Data in progressing */ if (pEndpoint->state > UDP_ENDPOINT_IDLE) { /* If list full */ if (pMbl->listState == MBL_FULL) { return USBD_STATUS_LOCKED; } } TRACE_DEBUG_WP("AddW%d(%" PRIu32 ") ", bEndpoint, dLength); /* Add buffer to buffer list and update index */ pTx = &(pMbl->pMbl[pMbl->inCurr]); pTx->pBuffer = (uint8_t*)pData; pTx->size = pTx->remaining = dLength; pTx->transferred = pTx->buffered = 0; /* Update input index */ if (pMbl->inCurr >= (pMbl->listSize-1)) pMbl->inCurr = 0; else pMbl->inCurr ++; if (pMbl->inCurr == pMbl->outCurr) pMbl->listState = MBL_FULL; else pMbl->listState = 0; /* Start sending when offset achieved */ if (MBL_NbBuffer(pMbl->inCurr, pMbl->outCurr, pMbl->listSize) >= pMbl->offsetSize && pEndpoint->state == UDP_ENDPOINT_IDLE) { TRACE_DEBUG_WP("StartT "); /* Change state */ pEndpoint->state = UDP_ENDPOINT_SENDINGM; while((UDP->UDP_CSR[bEndpoint]&UDP_CSR_TXPKTRDY)==UDP_CSR_TXPKTRDY); /* Send first packet */ UDP_MblWriteFifo(bEndpoint); SET_CSR(bEndpoint, UDP_CSR_TXPKTRDY); /* If double buffering is enabled and there is remaining, continue */ if ((CHIP_USB_ENDPOINTS_BANKS(bEndpoint) > 1) && pMbl->pMbl[pMbl->outCurr].remaining) { UDP_MblWriteFifo(bEndpoint); } /* Enable interrupt on endpoint */ 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. * \return USBD_STATUS_SUCCESS if the read operation has been started; * otherwise, the corresponding error code. */ static inline uint8_t UDP_Read(uint8_t bEndpoint, void *pData, uint32_t dLength) { Endpoint *pEndpoint = &(endpoints[bEndpoint]); Transfer *pTransfer = (Transfer*)&(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(%" PRIu32 ") ", bEndpoint, dLength); /* int i; for (i = 0; i < dLength; i++) { if (!(i%16)) { printf("\n\r"); } printf("0x%x ", ((uint8_t*)pData)[i]); } printf("\n\r"); */ /* Set the transfer descriptor */ pTransfer->pData = pData; pTransfer->remaining = dLength; pTransfer->buffered = 0; pTransfer->transferred = 0; /* Enable interrupt on endpoint */ UDP->UDP_IER = 1 << bEndpoint; return USBD_STATUS_SUCCESS; } /*--------------------------------------------------------------------------- * Exported functions *---------------------------------------------------------------------------*/ uint16_t USBD_GetEndpointSize(uint8_t bEndpoint) { Endpoint *pEndpoint = &(endpoints[bEndpoint]); return pEndpoint->size; } /** * USBD (UDP) interrupt handler * Manages device resume, suspend, end of bus reset. * Forwards endpoint events to the appropriate handler. */ void USBD_IrqHandler(void) { uint32_t status; int32_t eptnum = 0; /* Enable peripheral ? */ //UDP_EnablePeripheralClock(); /* Get interrupt status Some interrupts may get masked depending on the device state */ status = UDP->UDP_ISR; status &= UDP->UDP_IMR; TRACE_DEBUG("status; 0x%" PRIx32 , status); if (USBD_GetState() < USBD_STATE_POWERED) { status &= UDP_ICR_WAKEUP | UDP_ICR_RXRSM; UDP->UDP_ICR = ~status; } /* Return immediately if there is no interrupt to service */ if (status == 0) { TRACE_DEBUG_WP(".\n\r"); return; } /* Toggle USB LED if the device is active */ if (USBD_GetState() >= USBD_STATE_POWERED) { //LED_Set(USBD_LEDUSB); } /* Service interrupts */ /** / Start Of Frame (SOF) */ //if (ISSET(dStatus, UDP_ISR_SOFINT)) { // // TRACE_DEBUG("SOF"); // // // Invoke the SOF callback // USB_StartOfFrameCallback(pUsb); // // // Acknowledge interrupt // UDP->UDP_ICR = UDP_ICR_SOFINT; // dStatus &= ~UDP_ISR_SOFINT; //} /* Resume (Wakeup) */ if ((status & (UDP_ISR_WAKEUP | UDP_ISR_RXRSM)) != 0) { TRACE_DEBUG_WP("Res "); /* Clear and disable resume interrupts */ UDP->UDP_ICR = UDP_ICR_WAKEUP | UDP_ICR_RXRSM | UDP_ICR_RXSUSP; UDP->UDP_IDR = UDP_IDR_WAKEUP | UDP_IDR_RXRSM; /* Do resome operations */ USBD_ResumeHandler(); } /* Suspend This interrupt is always treated last (hence the '==') */ if (status == UDP_ISR_RXSUSP) { TRACE_DEBUG_WP("Susp "); /* Enable wakeup */ UDP->UDP_IER = UDP_IER_WAKEUP | UDP_IER_RXRSM; /* Acknowledge interrupt */ UDP->UDP_ICR = UDP_ICR_RXSUSP; /* Do suspend operations */ USBD_SuspendHandler(); } /* End of bus reset */ else if ((status & UDP_ISR_ENDBUSRES) != 0) { TRACE_DEBUG_WP("EoBRes "); #if defined(BOARD_USB_DFU) #if defined(APPLICATION_dfu) /* if we are currently in the DFU bootloader, and we are beyond * the MANIFEST stage, we shall switch to the normal * application */ if (g_dfu->past_manifest) { #if defined(ENVIRONMENT_flash) USBDFU_SwitchToApp(); #elif defined(ENVIRONMENT_dfu) USBDFU_SwitchToDFU(); #endif } #else /* if we are currently in the main application, and we are in * appDETACH state or past downloading, switch into the DFU bootloader. */ if (g_dfu->state == DFU_STATE_appDETACH || g_dfu->state == DFU_STATE_dfuMANIFEST) DFURT_SwitchToDFU(); #endif /* APPLICATION_dfu */ #endif /* BOARD_USB_DFU */ /* Flush and enable the Suspend interrupt */ UDP->UDP_ICR = UDP_ICR_WAKEUP | UDP_ICR_RXRSM | UDP_ICR_RXSUSP; UDP->UDP_IER = UDP_IER_RXSUSP; /* Do RESET operations */ USBD_ResetHandler(); /* Acknowledge end of bus reset interrupt */ UDP->UDP_ICR = UDP_ICR_ENDBUSRES; } /* Endpoint interrupts */ else { status &= ((1 << CHIP_USB_NUMENDPOINTS) - 1); 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_DEBUG_WP("\n\r - "); } } eptnum++; } } /* Toggle LED back to its previous state */ TRACE_DEBUG_WP("!"); TRACE_DEBUG_WP("\n\r"); if (USBD_GetState() >= USBD_STATE_POWERED) { //LED_Clear(USBD_LEDUSB); } } /** * \brief Reset endpoints and disable them. * -# Terminate transfer if there is any, with given status; * -# Reset the endpoint & disable it. * \param bmEPs Bitmap for endpoints to reset. * \param bStatus Status passed to terminate transfer on endpoint. * \param bKeepCfg 1 to keep old endpoint configuration. * \note Use USBD_HAL_ConfigureEP() to configure and enable endpoint if not keeping old configuration. * \sa USBD_HAL_ConfigureEP(). */ void USBD_HAL_ResetEPs(uint32_t bmEPs, uint8_t bStatus, uint8_t bKeepCfg) { Endpoint *pEndpoint; uint32_t tmp = bmEPs & ((1<UDP_IDR = epBit; /* Kill pending TXPKTREADY */ CLEAR_CSR(ep, UDP_CSR_TXPKTRDY); /* Reset transfer information */ pEndpoint = &(endpoints[ep]); /* Reset endpoint state */ pEndpoint->bank = 0; /* Endpoint configure */ epCfg = UDP->UDP_CSR[ep]; /* Reset endpoint */ UDP->UDP_RST_EP |= epBit; UDP->UDP_RST_EP &= ~epBit; /* Restore configure */ if (bKeepCfg) { //SET_CSR(ep, pEndpoint->CSR); SET_CSR(ep, epCfg); } else { //pEndpoint->CSR = 0; pEndpoint->state = UDP_ENDPOINT_DISABLED; } /* Terminate transfer on this EP */ UDP_EndOfTransfer(ep, bStatus); } epBit <<= 1; } /* Reset EPs */ // UDP->UDP_RST_EP |= bmEPs; // UDP->UDP_RST_EP &= ~bmEPs; } /** * Cancel pending READ/WRITE * \param bmEPs Bitmap for endpoints to reset. * \note EP callback is invoked with USBD_STATUS_CANCELED. */ void USBD_HAL_CancelIo(uint32_t bmEPs) { uint32_t tmp = bmEPs & ((1<UDP_IDR = epBit; /* Kill pending TXPKTREADY */ CLEAR_CSR(ep, UDP_CSR_TXPKTRDY); /* Terminate transfer on this EP */ UDP_EndOfTransfer(ep, USBD_STATUS_CANCELED); } epBit <<= 1; } } /** * Configures an endpoint according to its endpoint Descriptor. * \param pDescriptor Pointer to an endpoint descriptor. */ uint8_t USBD_HAL_ConfigureEP(const USBEndpointDescriptor *pDescriptor) { Endpoint *pEndpoint; uint8_t bEndpoint; uint8_t bType; uint8_t bEndpointDir; /* NULL descriptor -> Control endpoint 0 in default */ if (pDescriptor == 0) { bEndpoint = 0; pEndpoint = &(endpoints[bEndpoint]); bType= USBEndpointDescriptor_CONTROL; bEndpointDir = 0; pEndpoint->size = CHIP_USB_ENDPOINTS_MAXPACKETSIZE(0); } /* Device descriptor -> Specific Control EP */ else if (pDescriptor->bDescriptorType == USBGenericDescriptor_DEVICE) { bEndpoint = 0; pEndpoint = &(endpoints[bEndpoint]); bType = USBEndpointDescriptor_CONTROL; bEndpointDir = 0; pEndpoint->size = ((USBDeviceDescriptor *)pDescriptor)->bMaxPacketSize0; } /* Not endpoint descriptor, ERROR! */ else if (pDescriptor->bDescriptorType != USBGenericDescriptor_ENDPOINT) { return 0xFF; } 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) || (pEndpoint->state == UDP_ENDPOINT_RECEIVINGM) || (pEndpoint->state == UDP_ENDPOINT_SENDINGM)) { UDP_EndOfTransfer(bEndpoint, USBD_STATUS_RESET); } pEndpoint->state = UDP_ENDPOINT_IDLE; /* Reset Endpoint Fifos */ UDP->UDP_RST_EP |= (1 << bEndpoint); UDP->UDP_RST_EP &= ~(1 << bEndpoint); /* Configure endpoint */ SET_CSR(bEndpoint, (uint32_t)UDP_CSR_EPEDS | (bType << 8) | (bEndpointDir << 10)); if (bType != USBEndpointDescriptor_CONTROL) { } else { UDP->UDP_IER = (1 << bEndpoint); } TRACE_DEBUG_WP("CfgEp%d ", bEndpoint); return bEndpoint; } /** * Set callback for a USB endpoint for transfer (read/write). * * \param bEP Endpoint number. * \param fCallback Optional callback function to invoke when the transfer is * complete. * \param pCbData Optional pointer to data to the callback function. * \return USBD_STATUS_SUCCESS or USBD_STATUS_LOCKED if endpoint is busy. */ uint8_t USBD_HAL_SetTransferCallback(uint8_t bEP, TransferCallback fCallback, void *pCbData) { Endpoint *pEndpoint = &(endpoints[bEP]); TransferHeader *pTransfer = (TransferHeader*)&(pEndpoint->transfer); /* Check that the endpoint is not transferring */ if (pEndpoint->state > UDP_ENDPOINT_IDLE) { return USBD_STATUS_LOCKED; } TRACE_DEBUG_WP("sXfrCb "); /* Setup the transfer callback and extension data */ pTransfer->fCallback = (void*)fCallback; pTransfer->pArgument = pCbData; return USBD_STATUS_SUCCESS; } /** * Configure an endpoint to use multi-buffer-list transfer mode. * The buffers can be added by _Read/_Write function. * \param pMbList Pointer to a multi-buffer list used, NULL to disable MBL. * \param mblSize Multi-buffer list size (number of buffers can be queued) * \param startOffset When number of buffer achieve this offset transfer start */ uint8_t USBD_HAL_SetupMblTransfer( uint8_t bEndpoint, USBDTransferBuffer* pMbList, uint16_t mblSize, uint16_t startOffset) { Endpoint *pEndpoint = &(endpoints[bEndpoint]); MblTransfer *pXfr = (MblTransfer*)&(pEndpoint->transfer); uint16_t i; /* Check that the endpoint is not transferring */ if (pEndpoint->state > UDP_ENDPOINT_IDLE) { return USBD_STATUS_LOCKED; } TRACE_DEBUG_WP("sMblXfr "); /* Enable Multi-Buffer Transfer List */ if (pMbList) { /* Reset list items */ for (i = 0; i < mblSize; i --) { pMbList[i].pBuffer = NULL; pMbList[i].size = 0; pMbList[i].transferred = 0; pMbList[i].buffered = 0; pMbList[i].remaining = 0; } /* Setup transfer */ pXfr->transType = 1; pXfr->listState = 0; /* OK */ pXfr->listSize = mblSize; pXfr->pMbl = pMbList; pXfr->outCurr = pXfr->outLast = 0; pXfr->inCurr = 0; pXfr->offsetSize = startOffset; } /* Disable Multi-Buffer Transfer */ else { pXfr->transType = 0; pXfr->pMbl = NULL; pXfr->listSize = 0; pXfr->offsetSize = 1; } return USBD_STATUS_SUCCESS; } /** * 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. * \return USBD_STATUS_SUCCESS if the transfer has been started; * otherwise, the corresponding error status code. */ uint8_t USBD_HAL_Write( uint8_t bEndpoint, const void *pData, uint32_t dLength) { if (endpoints[bEndpoint].transfer.transHdr.transType) return UDP_AddWr(bEndpoint, pData, dLength); else return UDP_Write(bEndpoint, pData, dLength); } /** * 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. * \return USBD_STATUS_SUCCESS if the read operation has been started; * otherwise, the corresponding error code. */ uint8_t USBD_HAL_Read(uint8_t bEndpoint, void *pData, uint32_t dLength) { if (endpoints[bEndpoint].transfer.transHdr.transType) return USBD_STATUS_SW_NOT_SUPPORTED; else return UDP_Read(bEndpoint, pData, dLength); } /** * \brief Enable Pull-up, connect. * * -# Enable HW access if needed * -# Enable Pull-Up * -# Disable HW access if needed */ void USBD_HAL_Connect(void) { uint8_t dis = UDP_EnablePeripheralClock(); UDP->UDP_TXVC |= UDP_TXVC_PUON; if (dis) UDP_DisablePeripheralClock(); } /** * \brief Disable Pull-up, disconnect. * * -# Enable HW access if needed * -# Disable PULL-Up * -# Disable HW access if needed */ void USBD_HAL_Disconnect(void) { uint8_t dis = UDP_EnablePeripheralClock(); UDP->UDP_TXVC &= ~(uint32_t)UDP_TXVC_PUON; if (dis) UDP_DisablePeripheralClock(); } /** * Starts a remote wake-up procedure. */ void USBD_HAL_RemoteWakeUp(void) { UDP_EnablePeripheralClock(); UDP_EnableUsbClock(); UDP_EnableTransceiver(); TRACE_DEBUG_WP("RWUp "); // Activates a remote wakeup (edge on ESR), then clear ESR UDP->UDP_GLB_STAT |= UDP_GLB_STAT_ESR; UDP->UDP_GLB_STAT &= ~(uint32_t)UDP_GLB_STAT_ESR; } /** * Sets the device address to the given value. * \param address New device address. */ void USBD_HAL_SetAddress(uint8_t address) { /* Set address */ UDP->UDP_FADDR = UDP_FADDR_FEN | (address & UDP_FADDR_FADD_Msk); /* If the address is 0, the device returns to the Default state */ if (address == 0) UDP->UDP_GLB_STAT = 0; /* If the address is non-zero, the device enters the Address state */ else UDP->UDP_GLB_STAT = UDP_GLB_STAT_FADDEN; } /** * Sets the current device configuration. * \param cfgnum - Configuration number to set. */ void USBD_HAL_SetConfiguration(uint8_t cfgnum) { /* If the configuration number if non-zero, the device enters the Configured state */ if (cfgnum != 0) UDP->UDP_GLB_STAT |= UDP_GLB_STAT_CONFG; /* If the configuration number is zero, the device goes back to the Address state */ else { UDP->UDP_GLB_STAT = UDP_FADDR_FEN; } } /** * Initializes the USB HW Access driver. */ void USBD_HAL_Init(void) { TRACE_DEBUG("%s\n\r", "USBD_HAL_Init"); /* Must before USB & TXVC access! */ UDP_EnablePeripheralClock(); /* Reset & disable endpoints */ USBD_HAL_ResetEPs(0xFFFFFFFF, USBD_STATUS_RESET, 0); /* Configure the pull-up on D+ and disconnect it */ UDP->UDP_TXVC &= ~(uint32_t)UDP_TXVC_PUON; UDP_EnableUsbClock(); UDP->UDP_IDR = 0xFE; UDP->UDP_IER = UDP_IER_WAKEUP; } /** * Causes the given endpoint to acknowledge the next packet it receives * with a STALL handshake except setup request. * \param bEP Endpoint number. * \return USBD_STATUS_SUCCESS or USBD_STATUS_LOCKED. */ uint8_t USBD_HAL_Stall(uint8_t bEP) { Endpoint *pEndpoint = &(endpoints[bEP]); /* Check that endpoint is in Idle state */ if (pEndpoint->state != UDP_ENDPOINT_IDLE) { TRACE_WARNING("UDP_Stall: EP%d locked\n\r", bEP); return USBD_STATUS_LOCKED; } /* STALL endpoint */ SET_CSR(bEP, UDP_CSR_FORCESTALL); TRACE_DEBUG_WP("Stall%d ", bEP); return USBD_STATUS_SUCCESS; } /** * Sets/Clear/Get the HALT state on the endpoint. * In HALT state, the endpoint should keep stalling any packet. * \param bEndpoint Endpoint number. * \param ctl Control code CLR/HALT/READ. * 0: Clear HALT state; * 1: Set HALT state; * .: Return HALT status. * \return USBD_STATUS_INVALID_PARAMETER if endpoint not exist, * otherwise endpoint halt status. */ uint8_t USBD_HAL_Halt(uint8_t bEndpoint, uint8_t ctl) { Endpoint *pEndpoint = &(endpoints[bEndpoint]); uint8_t status = 0; /* SET Halt */ if (ctl == 1) { /* 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, UDP_CSR_FORCESTALL); pEndpoint->state = UDP_ENDPOINT_HALTED; /* Enable the endpoint interrupt */ UDP->UDP_IER = 1 << bEndpoint; } } /* CLEAR Halt */ else if (ctl == 0) { /* Check if the endpoint is halted */ //if (pEndpoint->state != UDP_ENDPOINT_DISABLED) { if (pEndpoint->state == UDP_ENDPOINT_HALTED) { TRACE_DEBUG_WP("Unhalt%d ", bEndpoint); /* Return endpoint to Idle state */ pEndpoint->state = UDP_ENDPOINT_IDLE; /* Clear FORCESTALL flag */ CLEAR_CSR(bEndpoint, UDP_CSR_FORCESTALL); /* Reset Endpoint Fifos, beware this is a 2 steps operation */ UDP->UDP_RST_EP |= 1 << bEndpoint; UDP->UDP_RST_EP &= ~(1 << bEndpoint); } } /* Return Halt status */ 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. */ uint8_t USBD_HAL_IsHighSpeed(void) { return 0; } /** * Suspend USB Device HW Interface * * -# Disable transceiver * -# Disable USB Clock * -# Disable USB Peripheral */ void USBD_HAL_Suspend(void) { /* The device enters the Suspended state */ UDP_DisableTransceiver(); UDP_DisableUsbClock(); /* Don't disable peripheral clock; this somehow breaks completion of any IN transfers * that have already been written to the peripheral, and which we expect to complete * after resume */ //UDP_DisablePeripheralClock(); } /** * Activate USB Device HW Interface * -# Enable USB Peripheral * -# Enable USB Clock * -# Enable transceiver */ void USBD_HAL_Activate(void) { UDP_EnablePeripheralClock(); UDP_EnableUsbClock(); UDP_EnableTransceiver(); } /**@}*/