/* -------------------------------------------------------------------------- * Copyright (c) 2013-2016 ARM Limited. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the License); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at * * www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an AS IS BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * $Date: 02. March 2016 * $Revision: V2.9 * * Driver: Driver_USBD0 * Configured: via RTE_Device.h configuration file * Project: USB Device Driver for NXP LPC18xx * -------------------------------------------------------------------------- * Use the following configuration settings in the middleware component * to connect to this driver. * * Configuration Setting Value * --------------------- ----- * Connect to hardware via Driver_USBD# = 0 * -------------------------------------------------------------------------- * Defines used for driver configuration (at compile time): * * USBD_MAX_ENDPOINT_NUM: defines maximum number of IN/OUT Endpoint pairs * that driver will support with Control Endpoint 0 * not included, this value impacts driver memory * requirements * - default value: 5 * - maximum value: 5 * -------------------------------------------------------------------------- */ /* History: * Version 2.9 * Removed unnecessary __packed specifier for structures dQH_t and dTD_t * Version 2.8 * Corrected PowerControl function for conditional Power full (driver must be initialized) * Version 2.7 * PowerControl for Power OFF and Uninitialize functions made unconditional * Version 2.6 * Corrected isochronous endpoint configuration * Corrected transfer procedure * Version 2.5 * Corrected isochronous transfer functionality * Version 2.4 * Corrected CLK_M3_USB1_CFG into CLK_M3_USB0_CFG in USBD_PowerControl * function * Version 2.3 * Corrected PORTSC1_D_PFSC into USB_PORTSC1_D_PFSC * Version 2.2 * Corrected return value in USBD_PowerControl function. * Version 2.1 * Added USB_LPC18xx_USB0.h with register bit definitions * Pin configuration moved to USB_LPC18xx.c * Version 2.0 * Updated to 2.00 API * Version 1.3 * Re-implementation of the driver * Version 1.2 * Updated USB1 pin configurations * Version 1.1 * Based on API V1.10 (namespace prefix ARM_ added) * Version 1.0 * Initial release */ #include #include #include "Driver_USBD.h" #include "LPC18xx.h" #include "USB_LPC18xx.h" #include "RTE_Device.h" #include "RTE_Components.h" #if (RTE_USB_USB0 == 0) #error "USB0 is not enabled in the RTE_Device.h!" #endif #ifndef USBD_MAX_ENDPOINT_NUM #define USBD_MAX_ENDPOINT_NUM 5U #endif #if (USBD_MAX_ENDPOINT_NUM > 5) #error Too many Endpoints, maximum IN/OUT Endpoint pairs that this driver supports is 5 !!! #endif extern uint8_t USB0_role; extern uint8_t USB0_state; extern void USB0_PinsConfigure (void); extern void USB0_PinsUnconfigure (void); // USBD Driver ***************************************************************** #define ARM_USBD_DRV_VERSION ARM_DRIVER_VERSION_MAJOR_MINOR(2,9) // Driver Version static const ARM_DRIVER_VERSION usbd_driver_version = { ARM_USBD_API_VERSION, ARM_USBD_DRV_VERSION }; // Driver Capabilities static const ARM_USBD_CAPABILITIES usbd_driver_capabilities = { 0U, // VBUS Detection 0U, // Event VBUS On 0U // Event VBUS Off }; #define LPC_USBx LPC_USB0 #define ENDPTCTRL(ep_num) (*(volatile uint32_t *)((uint32_t)(&LPC_USBx->ENDPTCTRL0) + 4U * ep_num)) #define EP_NUM(ep_addr) (ep_addr & ARM_USB_ENDPOINT_NUMBER_MASK) #define EP_DIR(ep_addr) ((ep_addr >> 7) & 1U) #define EP_SLL(ep_addr) (EP_DIR(ep_addr) * 16U) #define EP_IDX(ep_addr) ((EP_NUM(ep_addr) * 2U) + ((ep_addr >> 7) & 1U)) #define EP_MSK(ep_addr) (1UL << (EP_NUM(ep_addr) + EP_SLL(ep_addr))) typedef struct { // USB Device Endpoint Queue Head uint32_t cap; uint32_t curr_dTD; uint32_t next_dTD; uint32_t dTD_token; uint32_t buf[5]; uint32_t reserved; uint32_t setup[2]; // Use reminder of 64 bytes for transfer information uint8_t *data; uint32_t num; uint32_t num_transferred_total; uint16_t num_transferring; uint8_t ep_type; uint8_t ep_active; } dQH_t; typedef struct { // USB Device Endpoint Transfer Descriptor uint32_t next_dTD; uint32_t dTD_token; uint32_t buf[5]; uint32_t reserved; } dTD_t; static ARM_USBD_SignalDeviceEvent_t SignalDeviceEvent; static ARM_USBD_SignalEndpointEvent_t SignalEndpointEvent; static ARM_USBD_STATE usbd_state; static uint32_t setup_packet[2]; // Setup packet data static volatile uint8_t setup_received; // Setup packet received static dQH_t __align(2048) dQH[(USBD_MAX_ENDPOINT_NUM + 1U) * 2U]; // Queue Heads static dTD_t __align( 32) dTD[(USBD_MAX_ENDPOINT_NUM + 1U) * 2U]; // Transfer Descriptors // Function prototypes static int32_t USBD_EndpointConfigure (uint8_t ep_addr, uint8_t ep_type, uint16_t ep_max_packet_size); // Auxiliary functions /** \fn void USBD_HW_EndpointFlush (uint8_t ep_addr) \brief Flush Endpoint. */ static void USBD_HW_EndpointFlush (uint8_t ep_addr) { uint32_t ep_msk; ep_msk = EP_MSK(ep_addr); LPC_USBx->ENDPTFLUSH = ep_msk; while (LPC_USBx->ENDPTFLUSH & ep_msk); } /** \fn void USBD_Reset (void) \brief Reset USB Endpoint settings and variables. */ static void USBD_Reset (void) { uint8_t i; // Reset global variables setup_packet[0] = 0U; setup_packet[1] = 0U; setup_received = 0U; memset((void *)&usbd_state, 0, sizeof(usbd_state)); memset((void *)dQH, 0, sizeof(dQH)); memset((void *)dTD, 0, sizeof(dTD)); for (i = 1U; i <= USBD_MAX_ENDPOINT_NUM; i++) { ENDPTCTRL(i) &= ~(USB_ENDPTCTRL_RXE | USB_ENDPTCTRL_TXE); } // Clear interrupts LPC_USBx->ENDPTNAK = 0xFFFFFFFFUL; LPC_USBx->ENDPTNAKEN = 0U; LPC_USBx->USBSTS_D = 0xFFFFFFFFUL; LPC_USBx->ENDPTSETUPSTAT = LPC_USBx->ENDPTSETUPSTAT; LPC_USBx->ENDPTCOMPLETE = LPC_USBx->ENDPTCOMPLETE; while (LPC_USBx->ENDPTPRIME); // Clear all Primed buffers LPC_USBx->ENDPTFLUSH = 0xFFFFFFFFUL; while (LPC_USBx->ENDPTFLUSH); // Interrupt threshold control: no threshold LPC_USBx->USBCMD_D &= ~(USB_USBCMD_D_ITC(0xFFUL)); // Default Initialize Control Endpoint 0 if (usbd_state.speed == ARM_USB_SPEED_HIGH) { // For High-speed USBD_EndpointConfigure (0x00U, ARM_USB_ENDPOINT_CONTROL, 64U); USBD_EndpointConfigure (0x80U, ARM_USB_ENDPOINT_CONTROL, 64U); } else { // For Full/Low-speed USBD_EndpointConfigure (0x00U, ARM_USB_ENDPOINT_CONTROL, 8U); USBD_EndpointConfigure (0x80U, ARM_USB_ENDPOINT_CONTROL, 8U); } // Set start of endpoint list address LPC_USBx->ENDPOINTLISTADDR = (uint32_t)dQH; // Setup lockouts off LPC_USBx->USBMODE_D |= USB_USBMODE_D_SLOM; } /** \fn void USBD_HW_ReadSetupPacket (void) \brief Read Setup Packet to buffer. */ static void USBD_HW_ReadSetupPacket (void) { do { LPC_USBx->USBCMD_D |= USB_USBCMD_D_SUTW; // Setup trip wire // Copy Setup Packet Data to buffer setup_packet[0] = dQH[0].setup[0]; setup_packet[1] = dQH[0].setup[1]; } while (!(LPC_USBx->USBCMD_D & LPC_USBx->USBCMD_D)); LPC_USBx->USBCMD_D &= ~USB_USBCMD_D_SUTW; // Clear Setup trip wire LPC_USBx->ENDPTSETUPSTAT = 1U; // Clear Setup bit } /** \fn void USBD_HW_EndpointTransfer (uint8_t ep_addr) \brief Start transfer on Endpoint. \param[in] ep_addr Endpoint Address - ep_addr.0..3: Address - ep_addr.7: Direction */ static void USBD_HW_EndpointTransfer (uint8_t ep_addr) { dQH_t *ptr_dqh; dTD_t *ptr_dtd; uint8_t *data; uint32_t ep_msk, num; uint8_t ep_idx; ep_idx = EP_IDX(ep_addr); ep_msk = EP_MSK(ep_addr); ptr_dqh = &dQH[ep_idx]; ptr_dtd = &dTD[ep_idx]; data = ptr_dqh->data + ptr_dqh->num_transferred_total; num = ptr_dqh->num - ptr_dqh->num_transferred_total; if (num > 0x4000U) { num = 0x4000U; } // Maximum transfer length is 16k while (LPC_USBx->ENDPTSTAT & ep_msk); memset (ptr_dtd, 0, sizeof(dTD_t)); // Driver does not support linked endpoint descriptors // Next link pointer is not valid ptr_dtd->next_dTD = 1U; // Configure Transfer Descriptor ptr_dtd->dTD_token |= USB_bTD_TOKEN_TOTAL_BYTES(num) | // Bytes to transfer USB_bTD_TOKEN_IOC | // Interrupt on complete USB_bTD_TOKEN_STATUS_ACTIVE ; // Transfer Active // Set Buffer Addresses ptr_dtd->buf[0] = (uint32_t)(data ); ptr_dtd->buf[1] = (uint32_t)(data + 0x1000U); ptr_dtd->buf[2] = (uint32_t)(data + 0x2000U); ptr_dtd->buf[3] = (uint32_t)(data + 0x3000U); ptr_dtd->buf[4] = (uint32_t)(data + 0x4000U); ptr_dqh->dTD_token &= ~USB_bTD_TOKEN_STATUS_MSK; // Clear status ptr_dqh->next_dTD = (uint32_t)(ptr_dtd); // Save Transfer Descriptor address to Queue Head overlay ptr_dqh->num_transferring = num; LPC_USBx->ENDPTPRIME |= ep_msk; // Prime Endpoint -> Start Transfer } // USBD Driver functions /** \fn ARM_DRIVER_VERSION USBD_GetVersion (void) \brief Get driver version. \return \ref ARM_DRIVER_VERSION */ static ARM_DRIVER_VERSION USBD_GetVersion (void) { return usbd_driver_version; } /** \fn ARM_USBD_CAPABILITIES USBD_GetCapabilities (void) \brief Get driver capabilities. \return \ref ARM_USBD_CAPABILITIES */ static ARM_USBD_CAPABILITIES USBD_GetCapabilities (void) { return usbd_driver_capabilities; } /** \fn int32_t USBD_Initialize (ARM_USBD_SignalDeviceEvent_t cb_device_event, ARM_USBD_SignalEndpointEvent_t cb_endpoint_event) \brief Initialize USB Device Interface. \param[in] cb_device_event Pointer to \ref ARM_USBD_SignalDeviceEvent \param[in] cb_endpoint_event Pointer to \ref ARM_USBD_SignalEndpointEvent \return \ref execution_status */ static int32_t USBD_Initialize (ARM_USBD_SignalDeviceEvent_t cb_device_event, ARM_USBD_SignalEndpointEvent_t cb_endpoint_event) { if ((USB0_state & USBD_DRIVER_INITIALIZED) != 0U) { return ARM_DRIVER_OK; } SignalDeviceEvent = cb_device_event; SignalEndpointEvent = cb_endpoint_event; USB0_role = ARM_USB_ROLE_DEVICE; USB0_PinsConfigure (); USB0_state = USBD_DRIVER_INITIALIZED; return ARM_DRIVER_OK; } /** \fn int32_t USBD_Uninitialize (void) \brief De-initialize USB Device Interface. \return \ref execution_status */ static int32_t USBD_Uninitialize (void) { USB0_PinsUnconfigure (); USB0_role = ARM_USB_ROLE_NONE; USB0_state &= ~USBD_DRIVER_INITIALIZED; return ARM_DRIVER_OK; } /** \fn int32_t USBD_PowerControl (ARM_POWER_STATE state) \brief Control USB Device Interface Power. \param[in] state Power state \return \ref execution_status */ static int32_t USBD_PowerControl (ARM_POWER_STATE state) { switch (state) { case ARM_POWER_OFF: NVIC_DisableIRQ (USB0_IRQn); // Disable interrupt NVIC_ClearPendingIRQ (USB0_IRQn); // Clear pending interrupt USB0_state &= ~USBD_DRIVER_POWERED; // Clear powered flag // Reset variables setup_received = 0U; memset((void *)&usbd_state, 0, sizeof(usbd_state)); memset((void *)dQH, 0, sizeof(dQH)); memset((void *)dTD, 0, sizeof(dTD)); if ((LPC_CGU->BASE_USB0_CLK & 1U) == 0U) { LPC_CREG->CREG0 |= (1U << 5); // Disable USB0 PHY LPC_CCU1->CLK_USB0_CFG &= ~1U; // Disable USB0 Base Clock while (LPC_CCU1->CLK_USB0_STAT & 1U); LPC_CCU1->CLK_M3_USB0_CFG &= ~1U; // Disable USB0 Register Interface Clock while (LPC_CCU1->CLK_M3_USB0_STAT & 1U); LPC_CGU->BASE_USB0_CLK = 1U; // Disable Base Clock } break; case ARM_POWER_FULL: if ((USB0_state & USBD_DRIVER_INITIALIZED) == 0U) { return ARM_DRIVER_ERROR; } if ((USB0_state & USBD_DRIVER_POWERED) != 0U) { return ARM_DRIVER_OK; } LPC_CGU->BASE_USB0_CLK = (0x01U << 11) | // Auto-block Enable (0x07U << 24) ; // Clock source: PLL0USB LPC_CCU1->CLK_M3_USB0_CFG |= 1U; // Enable USB0 Register Interface Clock while (!(LPC_CCU1->CLK_M3_USB0_STAT & 1U)); LPC_CCU1->CLK_USB0_CFG |= 1U; // Enable USB0 Base Clock while (!(LPC_CCU1->CLK_USB0_STAT & 1U)); // Reset USB Controller LPC_USBx->USBCMD_D = USB_USBCMD_D_RST; while ((LPC_USBx->USBCMD_D & (USB_USBCMD_D_RS | USB_USBCMD_D_RST)) != 0U); // Force device mode and set Setup lockouts off LPC_USBx->USBMODE_D = USB_USBMODE_D_CM1_0(2U) | USB_USBMODE_D_SLOM; LPC_CREG->CREG0 &= ~(1U << 5); // Enable USB0 PHY USBD_Reset (); // Reset variables and endpoint settings #if (RTE_USB_USB0_HS_EN) LPC_USBx->PORTSC1_D &= ~USB_PORTSC1_D_PFSC; #else LPC_USBx->PORTSC1_D |= USB_PORTSC1_D_PFSC; #endif // Configure OTG Register LPC_USBx->OTGSC = USB_OTGSC_VD | // VBUS discharge USB_OTGSC_OT ; // OTG termination // Enable interrupts LPC_USBx->USBINTR_D = (USB_USBINTR_D_UE | // USB interrupt enable USB_USBINTR_D_PCE | // Port change detect interrupt enable USB_USBINTR_D_SLE | // Suspend interrupt enable USB_USBINTR_D_URE); // Reset interrupt enable USB0_state |= USBD_DRIVER_POWERED; // Set powered flag NVIC_EnableIRQ (USB0_IRQn); // Enable interrupt break; default: return ARM_DRIVER_ERROR_UNSUPPORTED; } return ARM_DRIVER_OK; } /** \fn int32_t USBD_DeviceConnect (void) \brief Connect USB Device. \return \ref execution_status */ static int32_t USBD_DeviceConnect (void) { if ((USB0_state & USBD_DRIVER_POWERED) == 0U) { return ARM_DRIVER_ERROR; } LPC_USBx->USBCMD_D |= USB_USBCMD_D_RS; // Attach Device return ARM_DRIVER_OK; } /** \fn int32_t USBD_DeviceDisconnect (void) \brief Disconnect USB Device. \return \ref execution_status */ static int32_t USBD_DeviceDisconnect (void) { if ((USB0_state & USBD_DRIVER_POWERED) == 0U) { return ARM_DRIVER_ERROR; } LPC_USBx->USBCMD_D &= ~USB_USBCMD_D_RS; // Detach Device #if (RTE_USB0_IND0_PIN_EN) LPC_USBx->PORTSC1_D &= ~USB_PORTSC1_D_PIC1_0(1); // Clear indicator LED0 #endif return ARM_DRIVER_OK; } /** \fn ARM_USBD_STATE USBD_DeviceGetState (void) \brief Get current USB Device State. \return Device State \ref ARM_USBD_STATE */ static ARM_USBD_STATE USBD_DeviceGetState (void) { ARM_USBD_STATE dev_state = { 0U, 0U, 0U }; uint32_t portsc1_d; if ((USB0_state & USBD_DRIVER_POWERED) == 0U) { return dev_state; } portsc1_d = LPC_USBx->PORTSC1_D; dev_state = usbd_state; dev_state.active = ((portsc1_d & USB_PORTSC1_D_CCS) != 0U) && ((portsc1_d & USB_USBDSTS_D_SLI) == 0U) ; return dev_state; } /** \fn int32_t USBD_DeviceRemoteWakeup (void) \brief Trigger USB Remote Wakeup. \return \ref execution_status */ static int32_t USBD_DeviceRemoteWakeup (void) { if ((USB0_state & USBD_DRIVER_POWERED) == 0U) { return ARM_DRIVER_ERROR; } LPC_USBx->PORTSC1_D &= ~USB_PORTSC1_D_PHCD; // Enable PHY Clock LPC_USBx->PORTSC1_D |= USB_PORTSC1_D_FPR; // Force Port Resume return ARM_DRIVER_OK; } /** \fn int32_t USBD_DeviceSetAddress (uint8_t dev_addr) \brief Set USB Device Address. \param[in] dev_addr Device Address \return \ref execution_status */ static int32_t USBD_DeviceSetAddress (uint8_t dev_addr) { if ((USB0_state & USBD_DRIVER_POWERED) == 0U) { return ARM_DRIVER_ERROR; } LPC_USBx->DEVICEADDR = (dev_addr << USB_DEVICEADDR_USBADR_POS) & USB_DEVICEADDR_USBADR_MSK; LPC_USBx->DEVICEADDR |= USB_DEVICEADDR_USBADRA; return ARM_DRIVER_OK; } /** \fn int32_t USBD_ReadSetupPacket (uint8_t *setup) \brief Read setup packet received over Control Endpoint. \param[out] setup Pointer to buffer for setup packet \return \ref execution_status */ static int32_t USBD_ReadSetupPacket (uint8_t *setup) { if ((USB0_state & USBD_DRIVER_POWERED) == 0U) { return ARM_DRIVER_ERROR; } if (setup_received == 0U) { return ARM_DRIVER_ERROR; } setup_received = 0U; memcpy(setup, setup_packet, 8); if (setup_received != 0U) { // If new setup packet was received while this was being read return ARM_DRIVER_ERROR; } return ARM_DRIVER_OK; } /** \fn int32_t USBD_EndpointConfigure (uint8_t ep_addr, uint8_t ep_type, uint16_t ep_max_packet_size) \brief Configure USB Endpoint. \param[in] ep_addr Endpoint Address - ep_addr.0..3: Address - ep_addr.7: Direction \param[in] ep_type Endpoint Type (ARM_USB_ENDPOINT_xxx) \param[in] ep_max_packet_size Endpoint Maximum Packet Size \return \ref execution_status */ static int32_t USBD_EndpointConfigure (uint8_t ep_addr, uint8_t ep_type, uint16_t ep_max_packet_size) { dQH_t *ptr_dqh; uint32_t ep_mult; uint32_t ep_mps; uint8_t ep_num,ep_sll; ep_num = EP_NUM(ep_addr); if (ep_num > USBD_MAX_ENDPOINT_NUM) { return ARM_DRIVER_ERROR; } if ((USB0_state & USBD_DRIVER_POWERED) == 0U) { return ARM_DRIVER_ERROR; } ptr_dqh = &dQH[EP_IDX(ep_addr)]; if (ptr_dqh->ep_active != 0U) { return ARM_DRIVER_ERROR_BUSY; } ep_num = EP_NUM(ep_addr); ep_sll = EP_SLL(ep_addr); ep_mult = (ep_max_packet_size & ARM_USB_ENDPOINT_MICROFRAME_TRANSACTIONS_MASK) >> 11; ep_mps = ep_max_packet_size & ARM_USB_ENDPOINT_MAX_PACKET_SIZE_MASK; // Clear Endpoint Queue Head memset((void *)ptr_dqh, 0, sizeof(dQH_t)); ptr_dqh->ep_type = ep_type; if (ep_type == ARM_USB_ENDPOINT_ISOCHRONOUS) { // For isochronous endpoints number of transactions per microframe in high-speed (or frame in full-speed) // has to be 1 more than additional transactions per microframe for high-speed (or 1 for full-speed) ep_mult++; } if ((ep_mult > 1U) && (usbd_state.speed == ARM_USB_SPEED_FULL)) { ep_mult = 1U; } ptr_dqh->cap = ((ep_mult << USB_EPQH_CAP_MULT_POS) & USB_EPQH_CAP_MULT_MSK) | (USB_EPQH_CAP_MAX_PACKET_LEN(ep_mps)) | (USB_EPQH_CAP_ZLT) | ((ep_addr == 0U) * USB_EPQH_CAP_IOS); ptr_dqh->next_dTD = 1U; ptr_dqh->dTD_token = 0U; USBD_HW_EndpointFlush(ep_addr); // Clear Endpoint Control Settings ENDPTCTRL(ep_num) &= ~((USB_ENDPTCTRL_RXS | USB_ENDPTCTRL_RXT_MSK | USB_ENDPTCTRL_RXI | USB_ENDPTCTRL_RXR | USB_ENDPTCTRL_RXE ) << ep_sll); // Set Endpoint Control Settings ENDPTCTRL(ep_num) |= (USB_ENDPTCTRL_RXT(ep_type) | // Endpoint Type USB_ENDPTCTRL_RXR | // Data Tggle Rset USB_ENDPTCTRL_RXE ) // Endpoint Enable << ep_sll; return ARM_DRIVER_OK; } /** \fn int32_t USBD_EndpointUnconfigure (uint8_t ep_addr) \brief Unconfigure USB Endpoint. \param[in] ep_addr Endpoint Address - ep_addr.0..3: Address - ep_addr.7: Direction \return \ref execution_status */ static int32_t USBD_EndpointUnconfigure (uint8_t ep_addr) { dQH_t *ptr_dqh; dTD_t *ptr_dtd; uint8_t ep_idx, ep_num, ep_sll; ep_num = EP_NUM(ep_addr); if (ep_num > USBD_MAX_ENDPOINT_NUM) { return ARM_DRIVER_ERROR; } if ((USB0_state & USBD_DRIVER_POWERED) == 0U) { return ARM_DRIVER_ERROR; } ep_idx = EP_IDX(ep_addr); ptr_dqh = &dQH[ep_idx]; if (ptr_dqh->ep_active != 0U) { return ARM_DRIVER_ERROR_BUSY; } ptr_dtd = &dTD[ep_idx]; ep_sll = EP_SLL(ep_addr); // Clear Endpoint Control Settings ENDPTCTRL(ep_num) &= ~((USB_ENDPTCTRL_RXS | USB_ENDPTCTRL_RXT_MSK | USB_ENDPTCTRL_RXI | USB_ENDPTCTRL_RXR | USB_ENDPTCTRL_RXE ) << ep_sll); ENDPTCTRL(ep_num) |= (USB_ENDPTCTRL_RXR << ep_sll); // Data toggle reset // Clear Endpoint Queue Head and Endpoint Transfer Descriptor memset((void *)ptr_dqh, 0, sizeof(dQH_t)); memset((void *)ptr_dtd, 0, sizeof(dTD_t)); return ARM_DRIVER_OK; } /** \fn int32_t USBD_EndpointStall (uint8_t ep_addr, bool stall) \brief Set/Clear Stall for USB Endpoint. \param[in] ep_addr Endpoint Address - ep_addr.0..3: Address - ep_addr.7: Direction \param[in] stall Operation - \b false Clear - \b true Set \return \ref execution_status */ static int32_t USBD_EndpointStall (uint8_t ep_addr, bool stall) { dQH_t *ptr_dqh; uint8_t ep_num, ep_sll; ep_num = EP_NUM(ep_addr); if (ep_num > USBD_MAX_ENDPOINT_NUM) { return ARM_DRIVER_ERROR; } if ((USB0_state & USBD_DRIVER_POWERED) == 0U) { return ARM_DRIVER_ERROR; } ptr_dqh = &dQH[EP_IDX(ep_addr)]; if (ptr_dqh->ep_active != 0U) { return ARM_DRIVER_ERROR_BUSY; } ep_sll = EP_SLL(ep_addr); if (stall != 0U) { // Set endpoint stall ENDPTCTRL(ep_num) |= (USB_ENDPTCTRL_RXS << ep_sll); } else { // Clear endpoint stall ENDPTCTRL(ep_num) &= ~(USB_ENDPTCTRL_RXS << ep_sll); ptr_dqh->dTD_token = 0U; USBD_HW_EndpointFlush(ep_addr); ENDPTCTRL(ep_num) |= (USB_ENDPTCTRL_RXR << ep_sll); // Data toggle reset } return ARM_DRIVER_OK; } /** \fn int32_t USBD_EndpointTransfer (uint8_t ep_addr, uint8_t *data, uint32_t num) \brief Read data from or Write data to USB Endpoint. \param[in] ep_addr Endpoint Address - ep_addr.0..3: Address - ep_addr.7: Direction \param[out] data Pointer to buffer for data to read or with data to write \param[in] num Number of data bytes to transfer \return \ref execution_status */ static int32_t USBD_EndpointTransfer (uint8_t ep_addr, uint8_t *data, uint32_t num) { dQH_t *ptr_dqh; if (EP_NUM(ep_addr) > USBD_MAX_ENDPOINT_NUM) { return ARM_DRIVER_ERROR; } if ((USB0_state & USBD_DRIVER_POWERED) == 0U) { return ARM_DRIVER_ERROR; } ptr_dqh = &dQH[EP_IDX(ep_addr)]; if (ptr_dqh->ep_active != 0U) { return ARM_DRIVER_ERROR_BUSY; } ptr_dqh->ep_active = 1U; ptr_dqh->data = data; ptr_dqh->num = num; ptr_dqh->num_transferred_total = 0U; ptr_dqh->num_transferring = 0U; USBD_HW_EndpointTransfer(ep_addr); // Start transfer return ARM_DRIVER_OK; } /** \fn uint32_t USBD_EndpointTransferGetResult (uint8_t ep_addr) \brief Get result of USB Endpoint transfer. \param[in] ep_addr Endpoint Address - ep_addr.0..3: Address - ep_addr.7: Direction \return number of successfully transferred data bytes */ static uint32_t USBD_EndpointTransferGetResult (uint8_t ep_addr) { if (EP_NUM(ep_addr) > USBD_MAX_ENDPOINT_NUM) { return 0U; } return (dQH[EP_IDX(ep_addr)].num_transferred_total); } /** \fn int32_t USBD_EndpointTransferAbort (uint8_t ep_addr) \brief Abort current USB Endpoint transfer. \param[in] ep_addr Endpoint Address - ep_addr.0..3: Address - ep_addr.7: Direction \return \ref execution_status */ static int32_t USBD_EndpointTransferAbort (uint8_t ep_addr) { dQH_t *ptr_dqh; uint32_t ep_msk; uint8_t ep_num, ep_sll; ep_num = EP_NUM(ep_addr); if (ep_num > USBD_MAX_ENDPOINT_NUM) { return ARM_DRIVER_ERROR; } if ((USB0_state & USBD_DRIVER_POWERED) == 0U) { return ARM_DRIVER_ERROR; } ptr_dqh = &dQH[EP_IDX(ep_addr)]; ep_msk = EP_MSK(ep_addr); ep_sll = EP_SLL(ep_addr); USBD_HW_EndpointFlush(ep_addr); LPC_USBx->ENDPTCOMPLETE = ep_msk; // Clear Completed Flag ENDPTCTRL(ep_num) |= (USB_ENDPTCTRL_RXR << ep_sll); // Data toggle reset ptr_dqh->dTD_token &= ~0xFFU; ptr_dqh->ep_active = 0U; return ARM_DRIVER_OK; } /** \fn uint16_t USBD_GetFrameNumber (void) \brief Get current USB Frame Number. \return Frame Number */ static uint16_t USBD_GetFrameNumber (void) { if ((USB0_state & USBD_DRIVER_POWERED) == 0U) { return 0U; } return ((LPC_USBx->FRINDEX_D & USB_FRINDEX_D_FRINDEX13_3_MSK) >> USB_FRINDEX_D_FRINDEX13_3_POS); } /** \fn void USBD0_IRQ (void) \brief USB0 Device Interrupt Routine (IRQ). */ void USBD0_IRQ (void) { dQH_t *ptr_dqh; uint32_t sts, cmpl; uint16_t ep_mps, num_transferred; uint8_t ep_num, ep_addr; sts = LPC_USBx->USBSTS_D & LPC_USBx->USBINTR_D; // Read active interrupts cmpl = LPC_USBx->ENDPTCOMPLETE; // Read Endpoint completed status LPC_USBx->USBSTS_D = sts; // Clear interrupts LPC_USBx->ENDPTCOMPLETE = cmpl; // Clear Endpoint completed status if ((sts & USB_USBDSTS_D_URI) != 0U) { // Reset interrupt USBD_Reset(); SignalDeviceEvent(ARM_USBD_EVENT_RESET); } if ((sts & USB_USBDSTS_D_SLI) != 0U) { // Suspend interrupt SignalDeviceEvent(ARM_USBD_EVENT_SUSPEND); #if (RTE_USB0_IND0_PIN_EN) LPC_USBx->PORTSC1_D &= ~USB_PORTSC1_D_PIC1_0(1); // Clear indicator LED0 #endif } if ((sts & USB_USBDSTS_D_PCI) != 0U) { // Port change detect interrupt if (((LPC_USBx->PORTSC1_D & USB_PORTSC1_D_PSPD_MSK) >> USB_PORTSC1_D_PSPD_POS) == 2U) { usbd_state.speed = ARM_USB_SPEED_HIGH; SignalDeviceEvent(ARM_USBD_EVENT_HIGH_SPEED); } else { usbd_state.speed = ARM_USB_SPEED_FULL; } #if (RTE_USB0_IND0_PIN_EN) LPC_USBx->PORTSC1_D |= USB_PORTSC1_D_PIC1_0(1); // Set indicator LED0 #endif SignalDeviceEvent(ARM_USBD_EVENT_RESUME); } if ((sts & USB_USBDSTS_D_UI) != 0U) { // USB interrupt - completed transfer if ((LPC_USBx->ENDPTSETUPSTAT) != 0U) { // Setup Packet Received USBD_HW_ReadSetupPacket(); setup_received = 1U; SignalEndpointEvent(0, ARM_USBD_EVENT_SETUP); } if ((cmpl & USB_ENDPTCOMPLETE_ETCE_MSK) != 0U) { // IN Data Sent for (ep_num = 0U; ep_num <= USBD_MAX_ENDPOINT_NUM; ep_num++) { if ((cmpl & USB_ENDPTCOMPLETE_ETCE_MSK) & (1U << (ep_num + USB_ENDPTCOMPLETE_ETCE_POS))) { ep_addr = ep_num | ARM_USB_ENDPOINT_DIRECTION_MASK; ptr_dqh = &dQH[EP_IDX(ep_addr)]; ptr_dqh->num_transferred_total += ptr_dqh->num_transferring; // Check if all required IN data was sent if (ptr_dqh->num == ptr_dqh->num_transferred_total) { ptr_dqh->ep_active = 0U; // Clear Endpoint busy flag SignalEndpointEvent(ep_addr, ARM_USBD_EVENT_IN); // Send IN event } else if (ptr_dqh->ep_active != 0U) { USBD_HW_EndpointTransfer (ep_addr); // If this was not last transfer, start next } } } } if ((cmpl & USB_ENDPTCOMPLETE_ERCE_MSK) != 0U) { // OUT Data Received for (ep_num = 0U; ep_num <= USBD_MAX_ENDPOINT_NUM; ep_num++) { if ((cmpl & USB_ENDPTCOMPLETE_ERCE_MSK) & (1 << ep_num)) { ep_addr = ep_num; ptr_dqh = &dQH[EP_IDX(ep_addr)]; ep_mps = (ptr_dqh->cap & USB_EPQH_CAP_MAX_PACKET_LEN_MSK) >> USB_EPQH_CAP_MAX_PACKET_LEN_POS; num_transferred = ptr_dqh->num_transferring - ((ptr_dqh->dTD_token & USB_bTD_TOKEN_TOTAL_BYTES_MSK) >> USB_bTD_TOKEN_TOTAL_BYTES_POS); ptr_dqh->num_transferred_total += num_transferred; // Check if all OUT data was received: // - data terminated with ZLP or short packet or // - all required data received if (((num_transferred % ep_mps) != 0U) || (ptr_dqh->num == ptr_dqh->num_transferred_total)) { ptr_dqh->ep_active = 0U; // Clear Endpoint busy flag SignalEndpointEvent(ep_addr, ARM_USBD_EVENT_OUT); // Send OUT event } else if (ptr_dqh->ep_active != 0U) { USBD_HW_EndpointTransfer (ep_addr); // If this was not last transfer, start next } } } } } } ARM_DRIVER_USBD Driver_USBD0 = { USBD_GetVersion, USBD_GetCapabilities, USBD_Initialize, USBD_Uninitialize, USBD_PowerControl, USBD_DeviceConnect, USBD_DeviceDisconnect, USBD_DeviceGetState, USBD_DeviceRemoteWakeup, USBD_DeviceSetAddress, USBD_ReadSetupPacket, USBD_EndpointConfigure, USBD_EndpointUnconfigure, USBD_EndpointStall, USBD_EndpointTransfer, USBD_EndpointTransferGetResult, USBD_EndpointTransferAbort, USBD_GetFrameNumber };