/**************************************************************************//** * * @copyright (C) 2020 Nuvoton Technology Corp. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2022-3-15 Wayne First version * ******************************************************************************/ #include #if defined(BSP_USING_USBD) #include #include #include "NuMicro.h" #include #define LOG_TAG "drv.usbd" #define DBG_ENABLE #define DBG_SECTION_NAME "drv.usbd" #define DBG_LEVEL DBG_ERROR #define DBG_COLOR #include /* Private define ---------------------------------------------------------------*/ /* Define EP maximum packet size */ #define EP0_MAX_PKT_SIZE 64 #define EP1_MAX_PKT_SIZE EP0_MAX_PKT_SIZE /* EP0 and EP1 are assigned the same size for control endpoint */ #define EP2_MAX_PKT_SIZE 64 #define EP3_MAX_PKT_SIZE 64 #define EP4_MAX_PKT_SIZE 32 #define EP5_MAX_PKT_SIZE 32 #define EP6_MAX_PKT_SIZE 64 #define EP7_MAX_PKT_SIZE 64 #define EP8_MAX_PKT_SIZE 32 #define EP9_MAX_PKT_SIZE 32 #define EP10_MAX_PKT_SIZE 64 #define EP11_MAX_PKT_SIZE 64 #define EP12_MAX_PKT_SIZE 32 #define EP13_MAX_PKT_SIZE 32 #define EP14_MAX_PKT_SIZE 64 #define EP15_MAX_PKT_SIZE 64 #define EP16_MAX_PKT_SIZE 32 #define EP17_MAX_PKT_SIZE 32 #define EP18_MAX_PKT_SIZE 64 #define EP19_MAX_PKT_SIZE 64 #define EP20_MAX_PKT_SIZE 32 #define EP21_MAX_PKT_SIZE 32 #define EP22_MAX_PKT_SIZE 64 #define EP23_MAX_PKT_SIZE 64 #define EP24_MAX_PKT_SIZE 32 #define SETUP_BUF_BASE 0 #define SETUP_BUF_LEN 8 #define EPADR_SW2HW(address) ((((address & USB_EPNO_MASK) * 2) + (!(address & USB_DIR_IN)))) #define EPADR_HW2SW(address) ((address & USB_EPNO_MASK) / 2) /* Private typedef --------------------------------------------------------------*/ struct nu_usbd { USBD_T *Instance; /* REG base */ uint8_t address_tmp; /* Keep assigned address for flow control */ }; typedef struct nu_usbd *nu_usbd_t; typedef struct { uint32_t u32BufferBase; uint32_t u32BufferLength; uint32_t u32; } S_EP_CXT; /* Private variables ------------------------------------------------------------*/ static struct nu_usbd nu_usbd_obj = { .Instance = USBD, .address_tmp = 0, }; static struct udcd _rt_obj_udc; static const uint32_t s_au32MaxPktSize[USBD_MAX_EP] = { EP0_MAX_PKT_SIZE, //EP0 EP1_MAX_PKT_SIZE, //EP1 EP2_MAX_PKT_SIZE, //EP2 EP3_MAX_PKT_SIZE, //EP3 EP4_MAX_PKT_SIZE, //EP4 EP5_MAX_PKT_SIZE, //EP5 EP6_MAX_PKT_SIZE, //EP6 EP7_MAX_PKT_SIZE, //EP7 EP8_MAX_PKT_SIZE, //EP8 EP9_MAX_PKT_SIZE, //EP9 EP10_MAX_PKT_SIZE, //EP10 EP11_MAX_PKT_SIZE, //EP11 EP12_MAX_PKT_SIZE, //EP12 EP13_MAX_PKT_SIZE, //EP13 EP14_MAX_PKT_SIZE, //EP14 EP15_MAX_PKT_SIZE, //EP15 EP16_MAX_PKT_SIZE, //EP16 EP17_MAX_PKT_SIZE, //EP17 EP18_MAX_PKT_SIZE, //EP18 EP19_MAX_PKT_SIZE, //EP19 EP20_MAX_PKT_SIZE, //EP20 EP21_MAX_PKT_SIZE, //EP21 EP22_MAX_PKT_SIZE, //EP22 EP23_MAX_PKT_SIZE, //EP23 EP24_MAX_PKT_SIZE //EP24 }; static struct ep_id _ep_pool[] = { {EPADR_HW2SW(EP0), USB_EP_ATTR_CONTROL, USB_DIR_INOUT, EP0_MAX_PKT_SIZE, ID_ASSIGNED }, {EPADR_HW2SW(EP2), USB_EP_ATTR_BULK, USB_DIR_IN, EP2_MAX_PKT_SIZE, ID_UNASSIGNED}, {EPADR_HW2SW(EP3), USB_EP_ATTR_BULK, USB_DIR_OUT, EP3_MAX_PKT_SIZE, ID_UNASSIGNED}, {EPADR_HW2SW(EP4), USB_EP_ATTR_INT, USB_DIR_IN, EP4_MAX_PKT_SIZE, ID_UNASSIGNED}, {EPADR_HW2SW(EP5), USB_EP_ATTR_INT, USB_DIR_OUT, EP5_MAX_PKT_SIZE, ID_UNASSIGNED}, {EPADR_HW2SW(EP6), USB_EP_ATTR_BULK, USB_DIR_IN, EP6_MAX_PKT_SIZE, ID_UNASSIGNED}, {EPADR_HW2SW(EP7), USB_EP_ATTR_BULK, USB_DIR_OUT, EP7_MAX_PKT_SIZE, ID_UNASSIGNED}, {EPADR_HW2SW(EP8), USB_EP_ATTR_INT, USB_DIR_IN, EP8_MAX_PKT_SIZE, ID_UNASSIGNED}, {EPADR_HW2SW(EP9), USB_EP_ATTR_INT, USB_DIR_OUT, EP9_MAX_PKT_SIZE, ID_UNASSIGNED}, {EPADR_HW2SW(EP10), USB_EP_ATTR_BULK, USB_DIR_IN, EP10_MAX_PKT_SIZE, ID_UNASSIGNED}, {EPADR_HW2SW(EP11), USB_EP_ATTR_BULK, USB_DIR_OUT, EP11_MAX_PKT_SIZE, ID_UNASSIGNED}, {EPADR_HW2SW(EP12), USB_EP_ATTR_INT, USB_DIR_IN, EP12_MAX_PKT_SIZE, ID_UNASSIGNED}, {EPADR_HW2SW(EP13), USB_EP_ATTR_INT, USB_DIR_OUT, EP13_MAX_PKT_SIZE, ID_UNASSIGNED}, {EPADR_HW2SW(EP14), USB_EP_ATTR_BULK, USB_DIR_IN, EP14_MAX_PKT_SIZE, ID_UNASSIGNED}, {EPADR_HW2SW(EP15), USB_EP_ATTR_BULK, USB_DIR_OUT, EP15_MAX_PKT_SIZE, ID_UNASSIGNED}, {EPADR_HW2SW(EP16), USB_EP_ATTR_INT, USB_DIR_IN, EP16_MAX_PKT_SIZE, ID_UNASSIGNED}, {EPADR_HW2SW(EP17), USB_EP_ATTR_INT, USB_DIR_OUT, EP17_MAX_PKT_SIZE, ID_UNASSIGNED}, {EPADR_HW2SW(EP18), USB_EP_ATTR_BULK, USB_DIR_IN, EP18_MAX_PKT_SIZE, ID_UNASSIGNED}, {EPADR_HW2SW(EP19), USB_EP_ATTR_BULK, USB_DIR_OUT, EP19_MAX_PKT_SIZE, ID_UNASSIGNED}, {EPADR_HW2SW(EP20), USB_EP_ATTR_INT, USB_DIR_IN, EP20_MAX_PKT_SIZE, ID_UNASSIGNED}, {EPADR_HW2SW(EP21), USB_EP_ATTR_INT, USB_DIR_OUT, EP21_MAX_PKT_SIZE, ID_UNASSIGNED}, {EPADR_HW2SW(EP22), USB_EP_ATTR_BULK, USB_DIR_IN, EP22_MAX_PKT_SIZE, ID_UNASSIGNED}, {EPADR_HW2SW(EP23), USB_EP_ATTR_BULK, USB_DIR_OUT, EP23_MAX_PKT_SIZE, ID_UNASSIGNED}, {EPADR_HW2SW(EP24), USB_EP_ATTR_INT, USB_DIR_IN, EP24_MAX_PKT_SIZE, ID_UNASSIGNED}, {0xFF, USB_EP_ATTR_TYPE_MASK, USB_DIR_MASK, 0, ID_ASSIGNED }, }; static struct ep_id *get_ep_entry(int ep_addr) { int i; for (i = 0; i < sizeof(_ep_pool) / sizeof(_ep_pool[0]); i++) { if (_ep_pool[i].addr == EPADR_HW2SW(ep_addr)) return &_ep_pool[i]; } return RT_NULL; } #define PRINT_EP_BASE(ep, base) rt_kprintf("%d: %08x\n", ep, USBD_GET_EP_BUF_ADDR(ep)); static void _nu_ep_partition(void) { int i; uint32_t u32EPBufBase, u32EPBufLen; /* Init setup packet buffer */ /* Buffer range for setup packet -> [0 ~ 0x7] */ USBD->STBUFSEG = SETUP_BUF_BASE; /*****************************************************/ u32EPBufBase = SETUP_BUF_BASE + SETUP_BUF_LEN; //For EP0 /* EP0 ==> control IN endpoint, address 0 */ USBD_CONFIG_EP(EP0, USBD_CFG_CSTALL | USBD_CFG_EPMODE_IN | EPADR_HW2SW(EP0)); /* Buffer range for EP0 */ USBD_SET_EP_BUF_ADDR(EP0, u32EPBufBase); u32EPBufLen = s_au32MaxPktSize[0]; //EP0 max pkt size u32EPBufBase += u32EPBufLen; /* EP1 ==> control OUT endpoint, address 0 */ USBD_CONFIG_EP(EP1, USBD_CFG_CSTALL | USBD_CFG_EPMODE_OUT | EPADR_HW2SW(EP1)); /* Buffer range for EP1 */ USBD_SET_EP_BUF_ADDR(EP1, u32EPBufBase); u32EPBufLen = s_au32MaxPktSize[1]; //EP1 max pkt size u32EPBufBase += u32EPBufLen; /*****************************************************/ for (i = EP2; i < USBD_MAX_EP; i++) { uint32_t u32Config = EPADR_HW2SW(i); struct ep_id *psEpId; u32EPBufLen = s_au32MaxPktSize[i]; RT_ASSERT(u32EPBufBase <= 1536); psEpId = get_ep_entry(i); if (psEpId == RT_NULL) continue; switch (psEpId->dir) { case USB_DIR_IN: u32Config |= USBD_CFG_EPMODE_IN; break; case USB_DIR_OUT: u32Config |= USBD_CFG_EPMODE_OUT; break; default: continue; } /* Endpoint configuration */ USBD_CONFIG_EP(i, u32Config); /* Buffer range for EP */ USBD_SET_EP_BUF_ADDR(i, u32EPBufBase); //PRINT_EP_BASE(i, u32EPBufBase); u32EPBufBase += u32EPBufLen; } } static rt_err_t _ep_set_stall(rt_uint8_t address) { USBD_SET_EP_STALL(EPADR_SW2HW(address)); return RT_EOK; } static rt_err_t _ep_clear_stall(rt_uint8_t address) { USBD_ClearStall(EPADR_SW2HW(address)); return RT_EOK; } static rt_err_t _set_address(rt_uint8_t address) { if (0 != address) { nu_usbd_obj.address_tmp = address; } return RT_EOK; } static rt_err_t _set_config(rt_uint8_t address) { return RT_EOK; } static rt_err_t _ep_enable(uep_t ep) { RT_ASSERT(ep != RT_NULL); RT_ASSERT(ep->ep_desc != RT_NULL); USBD_CONFIG_EP(EPADR_SW2HW(EP_ADDRESS(ep)), USBD_CFG_CSTALL | ((EP_ADDRESS(ep) & USB_DIR_IN) ? USBD_CFG_EPMODE_IN : USBD_CFG_EPMODE_OUT) | (EP_ADDRESS(ep) & USB_EPNO_MASK)); return RT_EOK; } static rt_err_t _ep_disable(uep_t ep) { RT_ASSERT(ep != RT_NULL); RT_ASSERT(ep->ep_desc != RT_NULL); USBD_CONFIG_EP(EPADR_SW2HW(EP_ADDRESS(ep)), USBD_CFG_EPMODE_DISABLE); return RT_EOK; } static rt_size_t _ep_read(rt_uint8_t address, void *buffer) { rt_size_t size = 0; rt_uint8_t *buf; rt_uint32_t hw_ep_num = EPADR_SW2HW(address); RT_ASSERT(!(address & USB_DIR_IN)); RT_ASSERT(buffer != RT_NULL); size = USBD_GET_PAYLOAD_LEN(hw_ep_num); buf = (uint8_t *)(USBD_BUF_BASE + USBD_GET_EP_BUF_ADDR(hw_ep_num)); USBD_MemCopy(buffer, (uint8_t *)buf, size); return size; } static rt_size_t _ep_read_prepare(rt_uint8_t address, void *buffer, rt_size_t size) { RT_ASSERT(!(address & USB_DIR_IN)); USBD_SET_PAYLOAD_LEN(EPADR_SW2HW(address), size); return size; } static rt_size_t _ep_write(rt_uint8_t address, void *buffer, rt_size_t size) { RT_ASSERT((address & USB_DIR_IN)); /* even number is for IN endpoint */ rt_uint32_t hw_ep_num = EPADR_SW2HW(address); uint8_t *buf; buf = (uint8_t *)(USBD_BUF_BASE + USBD_GET_EP_BUF_ADDR(hw_ep_num)); USBD_MemCopy(buf, (uint8_t *)buffer, size); USBD_SET_PAYLOAD_LEN(hw_ep_num, size); return size; } static rt_err_t _ep0_send_status(void) { /* Status stage */ USBD_SET_DATA1(EP0); USBD_SET_PAYLOAD_LEN(EP0, 0); return RT_EOK; } static rt_err_t _suspend(void) { return RT_EOK; } static rt_err_t _wakeup(void) { return RT_EOK; } __STATIC_INLINE void _USBD_IRQHandler(void) { rt_uint32_t u32IntSts = USBD_GET_INT_FLAG(); rt_uint32_t u32State = USBD_GET_BUS_STATE(); //------------------------------------------------------------------ if (u32IntSts & USBD_INTSTS_VBDETIF_Msk) { // Floating detect USBD_CLR_INT_FLAG(USBD_INTSTS_VBDETIF_Msk); if (USBD_IS_ATTACHED()) { /* USB Plug In */ USBD_ENABLE_USB(); rt_usbd_connect_handler(&_rt_obj_udc); } else { /* USB Unplug */ USBD_DISABLE_USB(); rt_usbd_disconnect_handler(&_rt_obj_udc); } } if (u32IntSts & USBD_INTSTS_SOFIF_Msk) { USBD_CLR_INT_FLAG(USBD_INTSTS_SOFIF_Msk); rt_usbd_sof_handler(&_rt_obj_udc); } //------------------------------------------------------------------ if (u32IntSts & USBD_INTSTS_BUSIF_Msk) { /* Clear event flag */ USBD_CLR_INT_FLAG(USBD_INTSTS_BUSIF_Msk); if (u32State & USBD_ATTR_USBRST_Msk) { USBD_ENABLE_USB(); /* Reset PID DATA0 */ for (rt_uint32_t i = 0ul; i < USBD_MAX_EP; i++) { nu_usbd_obj.Instance->EP[i].CFG &= ~USBD_CFG_DSQSYNC_Msk; } /* Reset USB device address */ USBD_SET_ADDR(0ul); /* Bus reset */ rt_usbd_reset_handler(&_rt_obj_udc); } if (u32State & USBD_ATTR_SUSPEND_Msk) { /* Enable USB but disable PHY */ USBD_DISABLE_PHY(); } if (u32State & USBD_ATTR_RESUME_Msk) { /* Enable USB and enable PHY */ USBD_ENABLE_USB(); } } //------------------------------------------------------------------ if (u32IntSts & USBD_INTSTS_WAKEUP) { /* Clear event flag */ USBD_CLR_INT_FLAG(USBD_INTSTS_WAKEUP); USBD_ENABLE_USB(); } if (u32IntSts & USBD_INTSTS_USBIF_Msk) { // USB event if (u32IntSts & USBD_INTSTS_SETUP_Msk) { // Setup packet /* Clear event flag */ USBD_CLR_INT_FLAG(USBD_INTSTS_SETUP_Msk); /* Clear the data IN/OUT ready flag of control end-points */ USBD_STOP_TRANSACTION(EP0); USBD_STOP_TRANSACTION(EP1); USBD_SET_DATA1(EP0); rt_usbd_ep0_setup_handler(&_rt_obj_udc, (struct urequest *)USBD_BUF_BASE); } /* Service EP events */ rt_uint32_t u32EpIntSts = USBD_GET_EP_INT_FLAG(); // EP events if (u32EpIntSts & USBD_EPINTSTS_EPEVT0_Msk) { /* Clear event flag */ USBD_CLR_EP_INT_FLAG(USBD_EPINTSTS_EPEVT0_Msk); if ((USBD_GET_ADDR() == 0) && (nu_usbd_obj.address_tmp) ) { USBD_SET_ADDR(nu_usbd_obj.address_tmp); LOG_I("SET ADDR: 0x%02x", nu_usbd.address_tmp); nu_usbd_obj.address_tmp = 0; } rt_usbd_ep0_in_handler(&_rt_obj_udc); } if (u32EpIntSts & USBD_EPINTSTS_EPEVT1_Msk) { /* Clear event flag */ USBD_CLR_EP_INT_FLAG(USBD_EPINTSTS_EPEVT1_Msk); rt_usbd_ep0_out_handler(&_rt_obj_udc, 0); } /* For EP2 ~ EP24 */ { rt_int32_t u32EpIrqIdx; rt_int32_t u32EpIrqStatus = u32EpIntSts & (~((1 << EP2) - 1)); // Skip EP0/EP1 traveling. // Find index of pin is attached in pool. while ((u32EpIrqIdx = nu_ctz(u32EpIrqStatus)) < USBD_MAX_EP) // Count Trailing Zeros ==> Find First One { /* Clear event flag */ USBD_CLR_EP_INT_FLAG(1 << u32EpIrqIdx); /* Report upper layer. */ rt_usbd_ep_in_handler(&_rt_obj_udc, _ep_pool[u32EpIrqIdx - 1].dir | EPADR_HW2SW(u32EpIrqIdx), 0); u32EpIrqStatus &= ~(1 << u32EpIrqIdx); } } } } void USBD_IRQHandler(void) { rt_interrupt_enter(); _USBD_IRQHandler(); rt_interrupt_leave(); } static rt_err_t _init(rt_device_t device) { nu_usbd_t nu_usbd = (nu_usbd_t)device->user_data; uint32_t u32RegLockBackup = SYS_IsRegLocked(); /* Initialize USB PHY */ SYS_UnlockReg(); /* Select USBD */ SYS->USBPHY = (SYS->USBPHY & ~SYS_USBPHY_USBROLE_Msk) | SYS_USBPHY_USBEN_Msk | SYS_USBPHY_SBO_Msk; SYS_ResetModule(USBD_RST); if (u32RegLockBackup) SYS_LockReg(); _nu_ep_partition(); /* Initial USB engine */ /* BYTEM=1: Byte mode: The size of the transfer from CPU to USB SRAM can be Byte only. PWRDN=1: Turn-on related circuit of PHY transceiver. DPPUEN=1: Pull-up resistor in USB_D+ bus Active. */ nu_usbd->Instance->ATTR = 0x7D0ul; /* Force SE0 */ USBD_SET_SE0(); NVIC_EnableIRQ(USBD_IRQn); USBD_Start(); return RT_EOK; } const static struct udcd_ops _udc_ops = { _set_address, _set_config, _ep_set_stall, _ep_clear_stall, _ep_enable, _ep_disable, _ep_read_prepare, _ep_read, _ep_write, _ep0_send_status, _suspend, _wakeup, }; #ifdef RT_USING_DEVICE_OPS const static struct rt_device_ops _ops = { _init, RT_NULL, RT_NULL, RT_NULL, RT_NULL, RT_NULL, }; #endif int nu_usbd_register(void) { if (RT_NULL != rt_device_find("usbd")) { LOG_E("\nUSBD Register failed. Another USBD device registered\n"); return -RT_ERROR; } rt_memset((void *)&_rt_obj_udc, 0, sizeof(struct udcd)); _rt_obj_udc.parent.type = RT_Device_Class_USBDevice; #ifdef RT_USING_DEVICE_OPS _rt_obj_udc.parent.ops = &_ops; #else _rt_obj_udc.parent.init = _init; #endif _rt_obj_udc.parent.user_data = &nu_usbd_obj; _rt_obj_udc.ops = &_udc_ops; /* Register endpoint information */ _rt_obj_udc.ep_pool = _ep_pool; _rt_obj_udc.ep0.id = &_ep_pool[0]; _rt_obj_udc.device_is_hs = RT_FALSE; /* Support Full-Speed only */ rt_device_register((rt_device_t)&_rt_obj_udc, "usbd", 0); return rt_usb_device_init(); } INIT_DEVICE_EXPORT(nu_usbd_register); #endif