/* * Kneron USBD API * * Copyright (C) s/2019/2020/ Kneron, Inc. All rights reserved. * */ // #define USBD2_DBG #include #include #include "cmsis_os2.h" #include "base.h" #include "kdrv_scu_ext.h" #include "kdrv_usbd2v.h" #ifdef USBD2_DBG #include "kmdw_console.h" #endif #ifdef USBD2_DBG #define usbd2_dbg(__format__, ...) kmdw_printf(__format__, ##__VA_ARGS__) #else #define usbd2_dbg(__format__, ...) #endif // OTG Control Status Register (0x80) #define REG_OTG_CSR 0x80 #define SPD_TYPE (BIT22 | BIT23) #define VBUS_VLD_RO BIT19 #define B_SESS_END_RO BIT16 // OTG Interrupt Stauts Register (0x84) // OTG Interrupt Enable Register (0x88) #define REG_OTG_ISR 0x84 #define REG_OTG_IER 0x88 #define OTG_APLGRMV_RW1C BIT12 #define OTG_A_WAIT_CON_RW1C BIT11 #define OTG_OVC_RW1C BIT10 #define OTG_IDCHG_RW1C BIT9 #define OTG_RLCHG_RW1C BIT8 #define OTG_B_SESS_END_RW1C BIT6 #define OTG_A_VBUS_ERR_RW1C BIT5 #define OTG_A_SRP_DET_RW1C BIT4 #define OTG_B_SRP_DN_RW1C BIT0 // Global HC/OTG/DEV Interrupt Status Register (0xC0) // Global Mask of HC/OTG/DEV Interrupt Register (0xC4) #define REG_GLB_ISR 0xC0 #define REG_GLB_INT 0xC4 #define INT_POLARITY BIT3 #define OTG_INT BIT1 #define DEV_INT BIT0 // Device Main Control Register (0x100) #define REG_DEV_CTL 0x100 // Device Address register (0x104) #define REG_DEV_ADR 0x104 #define AFT_CONF BIT7 // Device Test Register (0x108) #define REG_DEV_TST 0x108 #define TST_CLRFF BIT0 // Device SOF Mask Timer Register (0x110) #define REG_DEV_SMT 0x110 // PHY Test Mode Selector Register (0x114) #define REG_PHY_TST 0x114 #define TST_JSTA BIT0 // Device CX configuration adn FIFO empty status (0x120) #define REG_CXCFE 0x120 #define F_EMP_0 BIT8 #define CX_CLR BIT3 #define CX_STL BIT2 #define CX_DONE BIT0 // Device Idle Counter Register (0x124) #define REG_DEV_ICR 0x124 // Group total interrupt mask (0x130) // Group total interrupt status (0x140) #define REG_DEV_MIGR 0x130 #define REG_DEV_IGR 0x140 #define GX_INT_G3_RO BIT3 #define GX_INT_G2_RO BIT2 #define GX_INT_G1_RO BIT1 #define GX_INT_G0_RO BIT0 // Group 0 interrupt mask (0x134) // Group 0 interrupt status (0x144) // control transfer #define REG_DEV_MISG0 0x134 #define REG_DEV_ISG0 0x144 #define G0_CX_COMABT_INT_RW1C BIT5 #define G0_CX_COMFAIL_INT_RO BIT4 #define G0_CX_COMEND_INT_RO BIT3 #define G0_CX_OUT_INT_RO BIT2 #define G0_CX_IN_INT_RO BIT1 #define G0_CX_SETUP_INT_RO BIT0 // Group 1 interrupt mask (0x138) // Group 1 interrupt status (0x148) // FIFO interrupts #define REG_DEV_MISG1 0x138 #define REG_DEV_ISG1 0x148 #define MF0_IN_INT BIT16 #define MF0_SPK_INT BIT1 #define MF0_OUT_INT BIT0 // Group 1 interrupts (0x148) #define G1_F3_IN_INT_RO BIT19 #define G1_F2_IN_INT_RO BIT18 #define G1_F1_IN_INT_RO BIT17 #define G1_F0_IN_INT_RO BIT16 #define G1_F3_SPK_INT_RO BIT7 #define G1_F3_OUT_INT_RO BIT6 #define G1_F2_SPK_INT_RO BIT5 #define G1_F2_OUT_INT_RO BIT4 #define G1_F1_SPK_INT_RO BIT3 #define G1_F1_OUT_INT_RO BIT2 #define G1_F0_SPK_INT_RO BIT1 #define G1_F0_OUT_INT_RO BIT0 // Group 2 interrupt mask (0x13C) // Group 2 interrupt source (0x14C) #define REG_DEV_MISG2 0x13C #define REG_DEV_ISG2 0x14C #define G2_Dev_Wakeup_byVbus_RO BIT10 #define G2_Dev_Idle_RO BIT9 #define G2_DMA_ERROR_RW1C BIT8 #define G2_DMA_CMPLT_RW1C BIT7 #define G2_RX0BYTE_INT_RW1C BIT6 #define G2_TX0BYTE_INT_RW1C BIT5 #define G2_ISO_SEQ_ABORT_INT_RW1C BIT4 #define G2_ISO_SEQ_ERR_INT_RW1C BIT3 #define G2_RESM_INT_RW1C BIT2 #define G2_SUSP_INT_RW1C BIT1 #define G2_USBRST_INT_RW1C BIT0 // Devcie Receive Zero-Length Data Packet Register (0x150) #define REG_DEV_RXZ 0x150 #define RX0BYTE_EP1 BIT0 // Device IN endpoint & MaxPacketSize (0x160 + 4(n-1)) #define REG_DEV_INMPS_1 0x160 #define TX0BYTE_IEPn BIT15 #define RSTG_IEPn BIT12 #define STL_IEPn BIT11 // Device IN endpoint & MaxPacketSize (0x180 + 4(n-1)) #define REG_DEV_OUTMPS_1 0x180 #define RSTG_OEPn BIT12 #define STL_IEPn BIT11 // Device Endpoint 1~4 Map Register (0x1A0) #define REG_DEV_EPMAP0 0x1A0 // Device Endpoint 5~8 Map Register (0x1A4) #define REG_DEV_EPMAP1 0x1A4 // Device FIFO Map Register (0x1A8) #define REG_DEV_FMAP 0x1A8 // Device FIFO Configuration Register (0x1AC) #define REG_DEV_FCFG 0x1AC // Device FIFO Byte Count Register (0x1B0 + 4(fifo_no-1)) #define FFRST BIT12 #define BC_Fn 0x7ff // DMA Target FIFO register (0x1C0) #define REG_DMA_TFN 0x1C0 #define DMA_TARGET_ACC_CXF BIT4 #define DMA_TARGET_ACC_F3 BIT3 #define DMA_TARGET_ACC_F2 BIT2 #define DMA_TARGET_ACC_F1 BIT1 #define DMA_TARGET_ACC_F0 BIT0 #define DMA_TARGET_ACC_NONE 0x0 // DMA Controller Param 1 (0x1C8) #define REG_DMA_CPS1 0x1C8 #define DMA_TYPE BIT1 #define DMA_START BIT0 // DMA Controller Param 2 (0x1CC) #define REG_DMA_CPS2 0x1CC // DMA Controller Param 3 (0x1D0) // setup packet 8 bytes direct DMA read #define REG_DMA_CPS3 0x1D0 #define MAX_ENP_NUM 4 #define FIFO_NUM 4 // we have 4 FIFOs, each has 1-KB #define UsbRegRead(reg_offset) inw(USB_FOTG210_PA_BASE + reg_offset) #define UsbRegWrite(reg_offset, val) outw(USB_FOTG210_PA_BASE + reg_offset, val) #define UsbRegMaskedSet(reg_offset, val) masked_outw((USB_FOTG210_PA_BASE + reg_offset), (val), (val)) #define UsbRegMaskedClr(reg_offset, val) masked_outw((USB_FOTG210_PA_BASE + reg_offset), 0, (val)) #define VDMA_ONE_TXFER_SIZE (100 * 1024) // 100 KB enum { CONFIG_DEFAULT_STATE = 0, CONFIG_ADDRESS_STATE, CONFIG_CONFIGURED_STATE, }; // for FIFO_Ctrl_t:transferType enum { TXFER_CONTROL = 0, TXFER_ISO, TXFER_BULK, TXFER_INT, }; // for SETUP packet request enum { RESP_LATER = 1, /* busy now */ RESP_ACK, /* reqeust is done */ RESP_STALL, /* request is not supported */ }; typedef struct { uint8_t isTransferring; uint8_t dir; // 0:OUT, 1:IN uint32_t user_buf_len; uint32_t received_length; bool send_ZLP; kdrv_status_t status; } FIFO_context_t; static FIFO_context_t _fifo_ctx[FIFO_NUM]; static int _enp_to_fifo[MAX_ENP_NUM + 1] = {-1}; static int _fifo_to_enp[FIFO_NUM] = {0}; static osEventFlagsId_t _evt_id; static osSemaphoreId_t _enp_semaphore[MAX_ENP_NUM + 1] = {NULL}; static kdrv_usbd2v_device_descriptor_t *_dev_desc = NULL; static kdrv_usbd2v_string_descriptor_t *_dev_str_desc; static kdrv_usbd2v_link_status_callback_t _evt_cb; static kdrv_usbd2v_user_control_callback_t _usr_cx_cb; static uint32_t _config_state = CONFIG_DEFAULT_STATE; static kdrv_usbd2v_link_status_t _link_status = USBD2_STATUS_DISCONNECTED; #if 0 static bool _isInterrupt() { return (SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) != 0; } #endif // for data SRAM, the address must be remapped static uint32_t _dma_remap_addr(uint32_t addr) { uint32_t tmp; if ((addr & (SdRAM_MEM_BASE)) == SdRAM_MEM_BASE) { tmp = ((addr) & (~0x10000000)) | 0x20000000; return tmp; } return addr; } static void _set_link_status_detection(bool enable) { // use B_SEE_END for link status detection if (enable) { UsbRegWrite(REG_OTG_ISR, OTG_B_SESS_END_RW1C); UsbRegWrite(REG_OTG_IER, OTG_B_SESS_END_RW1C); } else { UsbRegWrite(REG_OTG_IER, 0x0); } } static void _vdma_cx_write(uint32_t *addr, uint32_t len) { UsbRegWrite(0x304, _dma_remap_addr((uint32_t)addr)); UsbRegWrite(0x300, (len << 8) | BIT1 | BIT0); } static kdrv_status_t _reset_endpoint(int enp_no) { usbd2_dbg("%s() enp_no %d\n", __FUNCTION__, enp_no); uint8_t fifo_no = _enp_to_fifo[enp_no]; UsbRegWrite(0x1B0 + 4 * fifo_no, FFRST); // for IN endpoints UsbRegMaskedSet(REG_DEV_INMPS_1 + 4 * (enp_no - 1), RSTG_IEPn); UsbRegMaskedClr(REG_DEV_INMPS_1 + 4 * (enp_no - 1), RSTG_IEPn); // for OUT endpoints UsbRegMaskedSet(REG_DEV_OUTMPS_1 + 4 * (enp_no - 1), RSTG_OEPn); UsbRegMaskedClr(REG_DEV_OUTMPS_1 + 4 * (enp_no - 1), RSTG_OEPn); return KDRV_STATUS_OK; } static void _reset_all_endpoints() { usbd2_dbg("%s()\n", __FUNCTION__); for (int enp_no = 1; enp_no <= MAX_ENP_NUM; enp_no++) if (_enp_to_fifo[enp_no] >= 0) _reset_endpoint(enp_no); } static int8_t _send_host_device_status() { uint8_t sData[2] = {0}; // self-power : 0, remote wakeup : 0 usbd2_dbg("[int] send device status\n"); _vdma_cx_write((uint32_t *)sData, 2); return RESP_LATER; } static int8_t _send_host_device_descriptor(kdrv_usbd2v_setup_packet_t *setup) { usbd2_dbg("[int] send device descriptor\n"); uint16_t txLen = MIN(_dev_desc->bLength, setup->wLength); _vdma_cx_write((uint32_t *)_dev_desc, txLen); return RESP_LATER; } static int8_t _send_host_string_descriptor(kdrv_usbd2v_setup_packet_t *setup, uint8_t type) { usbd2_dbg("[int] send string descriptor, type %d\n", type); uint16_t txLen = 0; uint32_t *buf = NULL; if (type == 0) //language id { txLen = MIN(_dev_str_desc->bLength, setup->wLength); buf = (uint32_t *)_dev_str_desc; } else if (type == 1 || type == 2 || type == 3) //iManufacturer,iProduct,iSerialNumber { txLen = MIN(_dev_str_desc->desc[type - 1]->bLength, setup->wLength); buf = (uint32_t *)_dev_str_desc->desc[type - 1]; } if (buf) { _vdma_cx_write((uint32_t *)buf, txLen); return RESP_LATER; } else return RESP_STALL; } static int8_t _send_host_configuration_descriptors(kdrv_usbd2v_setup_packet_t *setup) { uint32_t confIdx = setup->wValue & 0xFF; usbd2_dbg("[int] send configuration descriptor\n"); // some error checking if (confIdx > 0) return RESP_STALL; kdrv_usbd2v_config_descriptor_t *conf_desc = _dev_desc->config; uint16_t txLen = MIN(conf_desc->wTotalLength, setup->wLength); // create a buffer for combining all sub-descriptors static uint8_t *buf_desc = NULL; if (!buf_desc) { buf_desc = malloc(conf_desc->wTotalLength); // collect all sub-descriptos int one memory uint8_t offset = 0; memcpy(buf_desc, conf_desc, conf_desc->bLength); offset += conf_desc->bLength; kdrv_usbd2v_interface_descriptor_t *intf_desc = conf_desc->interface; memcpy(buf_desc + offset, intf_desc, intf_desc->bLength); offset += intf_desc->bLength; for (int j = 0; j < intf_desc->bNumEndpoints; j++) { kdrv_usbd2v_endpoint_descriptor_t *endp_desc = intf_desc->endpoint[j]; memcpy(buf_desc + offset, endp_desc, endp_desc->bLength); offset += endp_desc->bLength; } } _vdma_cx_write((uint32_t *)buf_desc, txLen); return RESP_LATER; } static void _init_fifo_configurations(kdrv_usbd2v_interface_descriptor_t *intf) { uint32_t fifo_map_val = 0x0; // for 0x1A8 uint32_t endp_map0_val = 0x0; // for 0x1A0 uint32_t fifo_config_val = 0x0; // for 0x1AC int fifo_no = 0; int fifo_add = 1; for (int i = 0; i < MAX_ENP_NUM; i++) { _enp_to_fifo[i + 1] = -1; _fifo_to_enp[i] = 0; } // here assume endpoint number order is ascending for (int i = 0; i < intf->bNumEndpoints; i++) { uint8_t bEndpointAddress = intf->endpoint[i]->bEndpointAddress; uint8_t bmAttributes = intf->endpoint[i]->bmAttributes; uint16_t wMaxPacketSize = intf->endpoint[i]->wMaxPacketSize; uint8_t isIn = !!(bEndpointAddress & 0x80); uint8_t enp_no = bEndpointAddress & 0xF; // retrieve endpoint no without direction uint32_t bitfield; // set FIFO's direction and corresponding endpoint no. bitfield = (isIn << 4) | enp_no; fifo_map_val |= (bitfield << (fifo_no * 8)); // set endpoint's FIFO no. // assume enp_no <= MAX_ENP_NUM bitfield = isIn ? fifo_no : (fifo_no << 4); endp_map0_val |= (bitfield << ((enp_no - 1) * 8)); // enable the corresponding FIFO and set transfer type bitfield = (BIT5 | (bmAttributes & 0x3)); if (intf->bNumEndpoints <= 2 || (intf->bNumEndpoints == 3 && i == 0)) { bitfield |= BIT2; fifo_add = 2; } else fifo_add = 1; fifo_config_val |= bitfield << (fifo_no * 8); // set max packet size & reset toggle bit if (isIn) { // for IN endpoints UsbRegWrite(REG_DEV_INMPS_1 + 4 * (enp_no - 1), wMaxPacketSize & 0x7ff); UsbRegMaskedSet(REG_DEV_INMPS_1 + 4 * (enp_no - 1), RSTG_IEPn); UsbRegMaskedClr(REG_DEV_INMPS_1 + 4 * (enp_no - 1), RSTG_IEPn); } else { // for OUT endpoints UsbRegWrite(REG_DEV_OUTMPS_1 + 4 * (enp_no - 1), wMaxPacketSize & 0x7ff); UsbRegMaskedSet(REG_DEV_OUTMPS_1 + 4 * (enp_no - 1), RSTG_OEPn); UsbRegMaskedClr(REG_DEV_OUTMPS_1 + 4 * (enp_no - 1), RSTG_OEPn); } _enp_to_fifo[enp_no] = fifo_no; _fifo_to_enp[fifo_no] = enp_no; fifo_no += fifo_add; } // clear all FIFO UsbRegMaskedSet(REG_DEV_TST, TST_CLRFF); // set FIFO interrupt mask // UsbRegWrite(REG_DEV_MISG1, fifo_int_mask); // endpoint map 0 UsbRegWrite(REG_DEV_EPMAP0, endp_map0_val); #ifdef USBD2_DBG { uint32_t reg_value = UsbRegRead(REG_DEV_EPMAP0); usbd2_dbg("\nsetting endpoint -> FIFO map register [0x%03X]:\n", REG_DEV_EPMAP0); for (int i = 0; i < 4; i++) { uint32_t temp = reg_value >> (i * 8); usbd2_dbg("IN enp_%d maps to FIFO_%d\n", i + 1, temp & 0x3); } for (int i = 0; i < 4; i++) { uint32_t temp = reg_value >> (i * 8) + 4; usbd2_dbg("OUT enp_%d maps to FIFO_%d\n", i + 1, temp & 0x3); } } #endif // fifo map UsbRegWrite(REG_DEV_FMAP, fifo_map_val); #ifdef USBD2_DBG { uint32_t reg_value = UsbRegRead(REG_DEV_FMAP); usbd2_dbg("\nsetting FIFO -> endpoint map register [0x%03X]:\n", REG_DEV_FMAP); for (int i = 0; i < 4; i++) { uint32_t temp = reg_value >> (i * 8); char temp_str[4]; switch ((temp >> 4) & 0x3) { case 0: strcpy(temp_str, "Out"); break; case 1: strcpy(temp_str, "In"); break; case 2: strcpy(temp_str, "Bi"); break; } usbd2_dbg("FIFO_%d map to %s enp_%d\n", i, temp_str, temp & 0xF); } } #endif // fifo config / enable UsbRegWrite(REG_DEV_FCFG, fifo_config_val); #ifdef USBD2_DBG { uint32_t reg_value = UsbRegRead(REG_DEV_FCFG); usbd2_dbg("\nsetting FIFO config register [0x%03X]:\n", REG_DEV_FCFG); for (int i = 0; i < 4; i++) { uint32_t temp = reg_value >> (i * 8); usbd2_dbg("- FIFO %d configs:\n", i); usbd2_dbg(" >> block enabled : %s\n", (temp & BIT5) ? "O" : "X"); if (!(temp & BIT5)) continue; usbd2_dbg(" >> block size : %s\n", (temp & BIT4) ? "1024" : "512"); switch ((temp >> 2) & 0x3) { case 0: usbd2_dbg(" >> block number: Single\n"); break; case 1: usbd2_dbg(" >> block number: Double\n"); break; case 2: usbd2_dbg(" >> block number: Triple\n"); break; case 3: usbd2_dbg(" >> block number: Reserved\n"); break; } switch (temp & 0x3) { case 0: usbd2_dbg(" >> transfer type: Reserved\n"); break; case 1: usbd2_dbg(" >> transfer type: ISOCH\n"); break; case 2: usbd2_dbg(" >> transfer type: Bulk\n"); break; case 3: usbd2_dbg(" >> transfer type: Interrupt\n"); break; } } usbd2_dbg("\n"); } #endif // set Device SOF Mask Timer value as data sheet recommended for HS UsbRegWrite(REG_DEV_SMT, 0x44C); // set configuration set bit, now allow HW to handle endpoint transfer UsbRegMaskedSet(REG_DEV_ADR, AFT_CONF); } static int8_t _set_configuration(kdrv_usbd2v_setup_packet_t *setup) { int8_t resp = RESP_STALL; uint8_t config_val = setup->wValue & 0xFF; usbd2_dbg("[int] set configuration, config = %d\n", config_val); if (config_val == 0) { _config_state = CONFIG_ADDRESS_STATE; // clear configuration set bit UsbRegMaskedClr(REG_DEV_ADR, AFT_CONF); resp = RESP_ACK; // reset all endpoints and terminate all in-progress transfer _reset_all_endpoints(); } else if (config_val == 1) { kdrv_usbd2v_config_descriptor_t *config = _dev_desc->config; if (_config_state != CONFIG_CONFIGURED_STATE) { _config_state = CONFIG_CONFIGURED_STATE; kdrv_usbd2v_terminate_all_endpoint(); // init fifo and endpoint stuff _init_fifo_configurations(config->interface); _link_status = USBD2_STATUS_CONFIGURED; _evt_cb(USBD2_STATUS_CONFIGURED); // now that it is configured, we enable link status detection for disconnection status _set_link_status_detection(true); // unlock transfer API for (int enp_no = 1; enp_no <= MAX_ENP_NUM; enp_no++) osSemaphoreRelease(_enp_semaphore[enp_no]); } resp = RESP_ACK; } return resp; } static int8_t handle_standard_request(kdrv_usbd2v_setup_packet_t *setup) { int8_t resp = RESP_STALL; // handle requests which are not affected by ep0 halt switch (setup->bRequest) { case 0x0: // GET_STATUS if (setup->wValue == 0x0) { resp = _send_host_device_status(); } break; case 0x1: // CLEAR_FEATURE { if (setup->wValue == 0x0) { // endpoint halt _reset_endpoint(setup->wIndex); resp = RESP_ACK; } break; } case 0x3: // SET_FEATURE break; } switch (setup->bRequest) { case 0x5: // SET_ADDRESS { // USB2.0 spec says should not be greaten than 127 if (setup->wValue <= 127) { // set DEVADR and also clear AFT_CONF UsbRegWrite(REG_DEV_ADR, setup->wValue); resp = RESP_ACK; } } break; case 0x6: // GET_DESCRIPTOR { // low byte: index of specified descriptor type uint8_t descp_idx = (setup->wValue & 0xFF); // high byte: descriptor type switch (setup->wValue >> 8) { case 1: // DEVICE descriptor resp = _send_host_device_descriptor(setup); break; case 2: // CONFIGURATION descriptor resp = _send_host_configuration_descriptors(setup); break; case 3: // STRING descriptor resp = _send_host_string_descriptor(setup, descp_idx); break; case 4: // INTERFACE descriptor break; case 5: // ENDPOINT descriptor break; case 6: // DEVICE_QUALIFIER descriptor break; case 7: // OTHER_SPEED_CONFIGURATION descriptor break; case 8: // INTERFACE_POWER descriptor break; } } break; case 0x7: // SET_DESCRIPTOR break; case 0x8: // GET_CONFIGURATION break; case 0x9: // SET_CONFIGURATION resp = _set_configuration(setup); break; } return resp; } static void _handle_device_interrupts() { uint32_t grp_x_int_status = UsbRegRead(REG_DEV_IGR); #ifdef USBD2_DBG usbd2_dbg("\n[int] !!!!!!!!!! from device group: "); for (int i = 0; i < 4; i++) if (grp_x_int_status & (0x1 << i)) usbd2_dbg("%d ", i); usbd2_dbg("\n"); #endif if (grp_x_int_status & GX_INT_G2_RO) { uint32_t grp_2_interrupts = (UsbRegRead(REG_DEV_ISG2) & ~(UsbRegRead(REG_DEV_MISG2))); #ifdef USBD2_DBG if (grp_2_interrupts & BIT0) usbd2_dbg("[int] grp_2_interrupt: %s\n", "USB reset"); if (grp_2_interrupts & BIT1) usbd2_dbg("[int] grp_2_interrupt: %s\n", "USB suspend"); if (grp_2_interrupts & BIT2) usbd2_dbg("[int] grp_2_interrupt: %s\n", "USB resume"); if (grp_2_interrupts & BIT3) usbd2_dbg("[int] grp_2_interrupt: %s\n", "ISOC seq error"); if (grp_2_interrupts & BIT4) usbd2_dbg("[int] grp_2_interrupt: %s\n", "ISOC seq abort"); if (grp_2_interrupts & BIT5) usbd2_dbg("[int] grp_2_interrupt: %s\n", "sent ZLP done"); if (grp_2_interrupts & BIT6) usbd2_dbg("[int] grp_2_interrupt: %s\n", "received ZLP"); if (grp_2_interrupts & BIT7) usbd2_dbg("[int] grp_2_interrupt: %s\n", "DMA complete"); if (grp_2_interrupts & BIT8) usbd2_dbg("[int] grp_2_interrupt: %s\n", "DMA error"); if (grp_2_interrupts & BIT9) usbd2_dbg("[int] grp_2_interrupt: %s\n", "device idle"); if (grp_2_interrupts & BIT10) usbd2_dbg("[int] grp_2_interrupt: %s\n", "ake up by vbus"); usbd2_dbg("\n"); #endif if (grp_2_interrupts & G2_USBRST_INT_RW1C) { // clear SET_CONFIG state and usb device address UsbRegWrite(REG_DEV_ADR, 0x0); // clear EP0 STALL bit UsbRegMaskedClr(REG_CXCFE, CX_STL); // disable (mask) all FIFOs interrupts UsbRegWrite(REG_DEV_MISG1, 0xFFFFFFFF); // clear all FIFO UsbRegMaskedSet(REG_DEV_TST, TST_CLRFF); // clear this interrupt bit UsbRegWrite(REG_DEV_ISG2, G2_USBRST_INT_RW1C); _config_state = CONFIG_DEFAULT_STATE; } if (grp_2_interrupts & G2_TX0BYTE_INT_RW1C) { uint32_t zlp_sent_interrupts = UsbRegRead(0x154); UsbRegWrite(0x154, zlp_sent_interrupts); for (int i = 0; i < MAX_ENP_NUM; i++) { if (zlp_sent_interrupts & (0x1 << i)) { int fifo_no = _enp_to_fifo[i + 1]; usbd2_dbg("[int] ZLP done, fifo %d IN notify event\n", fifo_no); _fifo_ctx[fifo_no].status = KDRV_STATUS_OK; osEventFlagsSet(_evt_id, (0x1 << fifo_no)); } } // clear this interrupt bit UsbRegWrite(REG_DEV_ISG2, G2_TX0BYTE_INT_RW1C); } } if (grp_x_int_status & GX_INT_G0_RO) { uint32_t grp_0_interrupts = (UsbRegRead(REG_DEV_ISG0) & ~(UsbRegRead(REG_DEV_MISG0))); #ifdef USBD2_DBG if (grp_0_interrupts & BIT0) usbd2_dbg("[int] grp_0_interrupt: %s\n", "CX SETUP packet"); if (grp_0_interrupts & BIT1) usbd2_dbg("[int] grp_0_interrupt: %s\n", "CX IN"); if (grp_0_interrupts & BIT2) usbd2_dbg("[int] grp_0_interrupt: %s\n", "CX OUT"); if (grp_0_interrupts & BIT3) usbd2_dbg("[int] grp_0_interrupt: %s\n", "CX Status Packet"); if (grp_0_interrupts & BIT4) usbd2_dbg("[int] grp_0_interrupt: %s\n", "CX Failed"); if (grp_0_interrupts & BIT5) usbd2_dbg("[int] grp_0_interrupt: %s\n", "CX abort"); #endif if (grp_0_interrupts & G0_CX_SETUP_INT_RO) { static kdrv_usbd2v_setup_packet_t setup_packet = {0}; memset(&setup_packet, 0, sizeof(setup_packet)); UsbRegWrite(0x304, _dma_remap_addr((uint32_t)&setup_packet)); UsbRegWrite(0x300, (8 << 8) | BIT0); // polling VDMA complete while ((UsbRegRead(0x328) & BIT0) == 0) ; UsbRegWrite(0x328, BIT0); usbd2_dbg("[int] VDMA recv CX SETUP : bmRequestType 0x%x bRequest 0x%x wValue 0x%x wIndex 0x%x wLength %d\n", setup_packet.bmRequestType, setup_packet.bRequest, setup_packet.wValue, setup_packet.wIndex, setup_packet.wLength); int8_t resp; uint8_t bmRequestType_type = ((setup_packet.bmRequestType & 0x60) >> 5); switch (bmRequestType_type) { case 0: // Standard request usbd2_dbg("[int] ....\n"); resp = handle_standard_request(&setup_packet); break; case 1: // Class request case 2: // Vendor request usbd2_dbg("[int] ....\n"); if (_usr_cx_cb(&setup_packet) == true) resp = RESP_ACK; else resp = RESP_STALL; break; default: usbd2_dbg("[int] ....\n"); resp = RESP_STALL; break; } if (resp == RESP_ACK) { usbd2_dbg("[int] setting CX_DONE ....\n"); // indicate an OK request to host UsbRegMaskedSet(REG_CXCFE, CX_DONE); } else if (resp == RESP_STALL) { usbd2_dbg("[int] setting CX_STL ....\n"); // indicate a request error to host UsbRegMaskedSet(REG_CXCFE, CX_STL | CX_DONE); } // else RESP_LATER due to VDMA in transfer } } if (grp_x_int_status & GX_INT_G3_RO) { usbd2_dbg("[int] VDMA interrupts = 0x%x\n", UsbRegRead(0x328)); uint32_t vdma_intrp = UsbRegRead(0x328); UsbRegWrite(0x328, vdma_intrp); if (vdma_intrp & BIT0) { // VDMA CX complete usbd2_dbg("[int] VDMA CX complete\n"); usbd2_dbg("[int] setting CX_DONE ....\n"); UsbRegMaskedSet(REG_CXCFE, CX_DONE); } if (vdma_intrp & (BIT1 | BIT2 | BIT3 | BIT4)) { // VDMA FIFO complete for (uint32_t fifo_no = 0; fifo_no < FIFO_NUM; fifo_no++) { // find which FIFO if (vdma_intrp & (0x2 << fifo_no)) { // IN or OUT if (_fifo_ctx[fifo_no].dir == 0) { usbd2_dbg("[int] VDMA fifo %d OUT complete\n", fifo_no); uint32_t left_size = UsbRegRead(0x308 + 8 * fifo_no) >> 8; if (left_size > 0 || _fifo_ctx[fifo_no].user_buf_len == 0) { usbd2_dbg("[int] VDMA fifo %d OUT transfer done, last recv size %d\n", fifo_no, VDMA_ONE_TXFER_SIZE - left_size); _fifo_ctx[fifo_no].received_length -= left_size; _fifo_ctx[fifo_no].status = KDRV_STATUS_OK; osEventFlagsSet(_evt_id, (0x1 << fifo_no)); continue; } uint32_t vdma_txfer_size = MIN(_fifo_ctx[fifo_no].user_buf_len, VDMA_ONE_TXFER_SIZE); usbd2_dbg("[int] VDMA fifo %d OUT next transfer addr 0x%x size %d\n", fifo_no, UsbRegRead(0x30C + 8 * fifo_no), vdma_txfer_size); _fifo_ctx[fifo_no].user_buf_len -= vdma_txfer_size; _fifo_ctx[fifo_no].received_length += vdma_txfer_size; UsbRegWrite(0x308 + 8 * fifo_no, (vdma_txfer_size << 8) | 0x1); } else { if (_fifo_ctx[fifo_no].user_buf_len == 0) { usbd2_dbg("[int] VDMA fifo %d IN transfer all done\n", fifo_no); if (_fifo_ctx[fifo_no].send_ZLP) { usbd2_dbg("[int] fifo %d IN send ZLP\n", fifo_no); int enp_no = _fifo_to_enp[fifo_no]; UsbRegMaskedSet(REG_DEV_INMPS_1 + 4 * (enp_no - 1), TX0BYTE_IEPn); } else { usbd2_dbg("[int] short pkt, fifo %d IN notify event\n", fifo_no); _fifo_ctx[fifo_no].status = KDRV_STATUS_OK; osEventFlagsSet(_evt_id, (0x1 << fifo_no)); } continue; } usbd2_dbg("[int] VDMA fifo %d IN complete\n", fifo_no); uint32_t left_size = UsbRegRead(0x308 + 8 * fifo_no) >> 8; if (left_size != 0) usbd2_dbg("[int] VDMA fifo %d IN error !!!, left_size = %d\n", fifo_no, left_size); uint32_t vdma_txfer_size = MIN(_fifo_ctx[fifo_no].user_buf_len, VDMA_ONE_TXFER_SIZE); usbd2_dbg("[int] VDMA fifo %d IN next transfer addr 0x%x size %d\n", fifo_no, UsbRegRead(0x30C + 8 * fifo_no), vdma_txfer_size); _fifo_ctx[fifo_no].user_buf_len -= vdma_txfer_size; UsbRegWrite(0x308 + 8 * fifo_no, (vdma_txfer_size << 8) | BIT1 | BIT0); } } } } if (vdma_intrp & (BIT16 | BIT17 | BIT18 | BIT19 | BIT20)) { usbd2_dbg("[int] VDMA error !!!!!!!!\n"); } } } // USB ISR static void _usbd2_isr(void) { uint32_t global_int = UsbRegRead(REG_GLB_ISR); if (global_int & DEV_INT) _handle_device_interrupts(); if (global_int & OTG_INT) { // turn off status detection because it can be triggered forever if disconnected _set_link_status_detection(false); _link_status = USBD2_STATUS_DISCONNECTED; _evt_cb(USBD2_STATUS_DISCONNECTED); // reset endpoint to make transfer get terminated _reset_all_endpoints(); } } static void _usbd2_init_register_isr(void) { SCU_EXTREG_USB_OTG_CTRL_SET_EXTCTRL_SUSPENDM(1); SCU_EXTREG_USB_OTG_CTRL_SET_u_iddig(1); SCU_EXTREG_USB_OTG_CTRL_SET_wakeup(0); SCU_EXTREG_USB_OTG_CTRL_SET_l1_wakeup(0); SCU_EXTREG_USB_OTG_CTRL_SET_OSCOUTEN(0); SCU_EXTREG_USB_OTG_CTRL_SET_PLLALIV(0); SCU_EXTREG_USB_OTG_CTRL_SET_XTLSEL(0); SCU_EXTREG_USB_OTG_CTRL_SET_OUTCLKSEL(0); _set_link_status_detection(false); // enable Device and OTG interrupt UsbRegWrite(REG_GLB_INT, ~(INT_POLARITY | DEV_INT | OTG_INT) & 0xF); #ifdef USBD2_DBG { uint32_t reg_value = UsbRegRead(REG_GLB_INT); usbd2_dbg("\nsetting global interrupt register [0x%03X]:\n", REG_GLB_INT); usbd2_dbg("- interrupt active %s\n", (reg_value & 0x8) ? "high" : "low"); usbd2_dbg("- host interrupt : %s\n", (reg_value & 0x4) ? "X" : "O"); usbd2_dbg("- OTG interrupt : %s\n", (reg_value & 0x2) ? "X" : "O"); usbd2_dbg("- device interrupt : %s\n", (reg_value & 0x1) ? "X" : "O"); usbd2_dbg("\n"); } #endif // listen all 4 groups for 0x140 //UsbRegWrite(REG_DEV_MIGR, GX_INT_G1_RO | GX_INT_G2_RO); //UsbRegWrite(REG_DEV_MIGR, GX_INT_G1_RO); UsbRegWrite(REG_DEV_MIGR, 0); #ifdef USBD2_DBG { uint32_t reg_value = UsbRegRead(REG_DEV_MIGR); usbd2_dbg("\nsetting groups interrupt enable register [0x%03X]:\n", REG_DEV_MIGR); for (int i = 0; i < 4; i++) usbd2_dbg("- group %d interrupt : %s\n", i, (reg_value & (0x1 << i)) ? "X" : "O"); usbd2_dbg("\n"); } #endif UsbRegWrite(REG_DEV_MISG0, 0); #ifdef USBD2_DBG { uint32_t reg_value = UsbRegRead(REG_DEV_MISG0); usbd2_dbg("\nsetting group 0 interrupt register [0x%03X]:\n", REG_DEV_MISG0); usbd2_dbg("- CX abort interrupt : %s\n", (reg_value & 0x20) ? "X" : "O"); usbd2_dbg("- CX Failed interrupt : %s\n", (reg_value & 0x10) ? "X" : "O"); usbd2_dbg("- CX Status Packet interrupt : %s\n", (reg_value & 0x8) ? "X" : "O"); usbd2_dbg("- CX OUT interrupt : %s\n", (reg_value & 0x4) ? "X" : "O"); usbd2_dbg("- CX IN interrupt : %s\n", (reg_value & 0x2) ? "X" : "O"); usbd2_dbg("- CX SETUP interrupt : %s\n", (reg_value & 0x1) ? "X" : "O"); usbd2_dbg("\n"); } #endif // grop 1 interrupt mask // at initialization turn off all fifo interrupts // for FIFO IN, it triggers interrupt while FIFO is empty ! UsbRegWrite(REG_DEV_MISG1, 0xFFFFFFFF); #ifdef USBD2_DBG { uint32_t reg_value = UsbRegRead(REG_DEV_MISG1); usbd2_dbg("\nsetting group 1 interrupt register [0x%03X]:\n", REG_DEV_MISG1); for (int i = 0; i < 4; i++) { uint32_t temp1 = reg_value >> (i * 2); uint32_t temp2 = reg_value >> (16 + i); usbd2_dbg("- FIFO %d interrupt >> OUT: %s, ShortPkt: %s, IN: %s\n", i, (temp1 & 0x1) ? "off" : "on", (temp1 & 0x2) ? "off" : "on", (temp2 & 0x1) ? "off" : "on"); } usbd2_dbg("\n"); } #endif // grop 2 interrupt mask UsbRegWrite(REG_DEV_MISG2, ~(G2_TX0BYTE_INT_RW1C | G2_USBRST_INT_RW1C)); #ifdef USBD2_DBG { uint32_t reg_value = UsbRegRead(REG_DEV_MISG2); usbd2_dbg("\nsetting group 2 interrupt register [0x%03X]:\n", REG_DEV_MISG2); usbd2_dbg("- wake up by vbus interrupt : %s\n", (reg_value & 0x40) ? "X" : "O"); usbd2_dbg("- device idle interrupt : %s\n", (reg_value & 0x200) ? "X" : "O"); usbd2_dbg("- DMA error interrupt : %s\n", (reg_value & 0x100) ? "X" : "O"); usbd2_dbg("- DMA complete interrupt : %s\n", (reg_value & 0x80) ? "X" : "O"); usbd2_dbg("- received ZLP interrupt : %s\n", (reg_value & 0x40) ? "X" : "O"); usbd2_dbg("- sent ZLP interrupt : %s\n", (reg_value & 0x20) ? "X" : "O"); usbd2_dbg("- ISOC seq abort interrupt : %s\n", (reg_value & 0x10) ? "X" : "O"); usbd2_dbg("- ISOC seq error interrupt : %s\n", (reg_value & 0x8) ? "X" : "O"); usbd2_dbg("- resume interrupt : %s\n", (reg_value & 0x4) ? "X" : "O"); usbd2_dbg("- suspend interrupt : %s\n", (reg_value & 0x2) ? "X" : "O"); usbd2_dbg("- bus reset interrupt : %s\n", (reg_value & 0x1) ? "X" : "O"); usbd2_dbg("\n"); } #endif // set device idle counter = 7ms UsbRegWrite(REG_DEV_ICR, 0x7); // device soft reset UsbRegMaskedSet(REG_DEV_CTL, BIT4); // clear all FIFO counter UsbRegMaskedSet(REG_DEV_TST, BIT0); // enable chip UsbRegMaskedSet(REG_DEV_CTL, BIT5); // clear all interrupts status for RW1C bits UsbRegWrite(REG_OTG_ISR, 0xFFFFFFFF); UsbRegWrite(REG_DEV_ISG0, 0xFFFFFFFF); UsbRegWrite(REG_DEV_ISG2, 0xFFFFFFFF); // global interrupt enable UsbRegMaskedSet(REG_DEV_CTL, BIT2); UsbRegWrite(0x330, 1); // init VDMA NVIC_SetVector(OTG_SBS_3_IRQ, (uint32_t)_usbd2_isr); // Clear and Enable SAI IRQ //NVIC_ClearPendingIRQ(OTG_SBS_3_IRQ); NVIC_EnableIRQ(OTG_SBS_3_IRQ); } static void _default_status_isr_callback(kdrv_usbd2v_link_status_t event) { } static bool _default_usr_cx_isr_callback(kdrv_usbd2v_setup_packet_t *setup) { return false; // RESP_STALL } ////////////////// below are API //////////////////// kdrv_status_t kdrv_usbd2v_initialize( kdrv_usbd2v_device_descriptor_t *dev_desc, kdrv_usbd2v_string_descriptor_t *dev_str_desc, kdrv_usbd2v_link_status_callback_t status_isr_cb, kdrv_usbd2v_user_control_callback_t usr_cx_isr_cb) { _dev_desc = dev_desc; _dev_str_desc = dev_str_desc; _evt_cb = (status_isr_cb == NULL) ? _default_status_isr_callback : status_isr_cb; _usr_cx_cb = (usr_cx_isr_cb == NULL) ? _default_usr_cx_isr_callback : usr_cx_isr_cb; _evt_id = osEventFlagsNew(NULL); _usbd2_init_register_isr(); for (int enp_no = 1; enp_no <= MAX_ENP_NUM; enp_no++) _enp_semaphore[enp_no] = osSemaphoreNew(1, 0, NULL); // at first it is locked return KDRV_STATUS_OK; } kdrv_status_t kdrv_usbd2v_uninitialize(void) { osEventFlagsDelete(_evt_id); for (int enp_no = 1; enp_no <= MAX_ENP_NUM; enp_no++) { osSemaphoreDelete(_enp_semaphore[enp_no]); _enp_semaphore[enp_no] = NULL; } return KDRV_STATUS_OK; } kdrv_status_t kdrv_usbd2v_set_enable(bool enable) { if ((UsbRegRead(REG_PHY_TST) & TST_JSTA) == 0) { UsbRegMaskedSet(REG_PHY_TST, TST_JSTA); osDelay(5); } if (enable) // Make PHY work properly, FIXME ? UsbRegMaskedClr(REG_PHY_TST, TST_JSTA); else // Make PHY not work, FIXME ? UsbRegMaskedSet(REG_PHY_TST, TST_JSTA); return KDRV_STATUS_OK; } kdrv_usbd2v_link_status_t kdrv_usbd2v_get_link_status(void) { return _link_status; } kdrv_status_t kdrv_usbd2v_reset_device() { usbd2_dbg("%s()\n", __FUNCTION__); // disable bus kdrv_usbd2v_set_enable(false); // reset all endpoints and terminate all in-progress transfer _reset_all_endpoints(); // some delay for USB bus, FIXME ? osDelay(100); _config_state = CONFIG_DEFAULT_STATE; // re-init registers and isr _usbd2_init_register_isr(); // re-enable bus kdrv_usbd2v_set_enable(true); return KDRV_STATUS_OK; } kdrv_status_t kdrv_usbd2v_terminate_all_endpoint(void) { usbd2_dbg("%s()\n", __FUNCTION__); UsbRegWrite(0x330, 0); // stop VDMA for (int enp_no = 1; enp_no <= MAX_ENP_NUM; enp_no++) { int fifo_no = _enp_to_fifo[enp_no]; if (fifo_no >= 0) { UsbRegWrite(0x1B0 + 4 * fifo_no, FFRST); if (_fifo_ctx[fifo_no].isTransferring) { // notify transfer done to user _fifo_ctx[fifo_no].status = KDRV_STATUS_USBD_TRANSFER_TERMINATED; osEventFlagsSet(_evt_id, (0x1 << fifo_no)); } } } UsbRegWrite(0x330, 1); // start VDMA return KDRV_STATUS_OK; } kdrv_status_t kdrv_usbd2v_bulk_send(uint32_t endpoint, uint32_t *buf, uint32_t txLen, uint32_t timeout_ms) { kdrv_status_t status; uint8_t enp_no = endpoint & 0xF; uint8_t fifo_no = _enp_to_fifo[enp_no]; osSemaphoreAcquire(_enp_semaphore[enp_no], osWaitForever); usbd2_dbg("[bulk_send] enp 0x%x fifo_no %d buf 0x%x txLen %d\n", endpoint, fifo_no, buf, txLen); _fifo_ctx[fifo_no].send_ZLP = ((txLen & 0x1FF) == 0) ? true : false; uint32_t vdma_txfer_size = MIN(txLen, VDMA_ONE_TXFER_SIZE); txLen -= vdma_txfer_size; _fifo_ctx[fifo_no].isTransferring = true; _fifo_ctx[fifo_no].user_buf_len = txLen; _fifo_ctx[fifo_no].dir = 1; osEventFlagsClear(_evt_id, (0x1 << fifo_no)); UsbRegWrite(0x30C + 8 * fifo_no, _dma_remap_addr((uint32_t)buf)); UsbRegWrite(0x308 + 8 * fifo_no, (vdma_txfer_size << 8) | BIT1 | BIT0); uint32_t flags = osEventFlagsWait(_evt_id, (0x1 << fifo_no), osFlagsWaitAny, timeout_ms); if (flags == osFlagsErrorTimeout) status = KDRV_STATUS_USBD_TRANSFER_TIMEOUT; else status = _fifo_ctx[fifo_no].status; _fifo_ctx[fifo_no].isTransferring = false; usbd2_dbg("[bulk_send] enp 0x%x status %d\n", endpoint, status); if (status != KDRV_STATUS_USBD_TRANSFER_DISCONNECTED) osSemaphoreRelease(_enp_semaphore[enp_no]); return status; } #define FAKE_ZLP 0x11223344 kdrv_status_t kdrv_usbd2v_bulk_receive(uint32_t endpoint, uint32_t *buf, uint32_t *blen, uint32_t timeout_ms) { kdrv_status_t status; uint8_t enp_no = endpoint & 0xF; uint8_t fifo_no = _enp_to_fifo[enp_no]; osSemaphoreAcquire(_enp_semaphore[enp_no], osWaitForever); usbd2_dbg("[bulk_recv] enp 0x%x fifo_no %d buf 0x%x buf_len %d\n", endpoint, fifo_no, buf, *blen); uint32_t left_buf_size = *blen; uint32_t vdma_txfer_size = MIN(left_buf_size, VDMA_ONE_TXFER_SIZE); left_buf_size -= vdma_txfer_size; _fifo_ctx[fifo_no].isTransferring = true; _fifo_ctx[fifo_no].user_buf_len = left_buf_size; _fifo_ctx[fifo_no].received_length = vdma_txfer_size; // pre value _fifo_ctx[fifo_no].dir = 0; osEventFlagsClear(_evt_id, (0x1 << fifo_no)); UsbRegWrite(0x30C + 8 * fifo_no, _dma_remap_addr((uint32_t)buf)); UsbRegWrite(0x308 + 8 * fifo_no, (vdma_txfer_size << 8) | BIT0); uint32_t flags = osEventFlagsWait(_evt_id, (0x1 << fifo_no), osFlagsWaitAny, timeout_ms); if (flags == osFlagsErrorTimeout) status = KDRV_STATUS_USBD_TRANSFER_TIMEOUT; else { *blen = _fifo_ctx[fifo_no].received_length; status = _fifo_ctx[fifo_no].status; if (((*blen - 4) & 0x1FF) == 0 && *(uint32_t *)((uint32_t)buf + *blen - 4) == FAKE_ZLP) { usbd2_dbg("[bulk_recv] enp 0x%x recv fake ZLP\n", endpoint); *blen -= 4; } } _fifo_ctx[fifo_no].isTransferring = false; usbd2_dbg("[bulk_recv] enp 0x%x recv %d status %d\n", endpoint, *blen, status); if (status != KDRV_STATUS_USBD_TRANSFER_DISCONNECTED) osSemaphoreRelease(_enp_semaphore[enp_no]); return status; } bool kdrv_usbd2v_interrupt_send_check_buffer_empty(uint32_t endpoint) { uint8_t enp_no = endpoint & 0xF; uint8_t fifo_no = _enp_to_fifo[enp_no]; return !!(UsbRegRead(REG_CXCFE) & (F_EMP_0 << fifo_no)); } kdrv_status_t kdrv_usbd2v_interrupt_send(uint32_t endpoint, uint32_t *buf, uint32_t txLen, uint32_t timeout_ms) { kdrv_status_t status; uint8_t enp_no = endpoint & 0xF; uint8_t fifo_no = _enp_to_fifo[enp_no]; osSemaphoreAcquire(_enp_semaphore[enp_no], osWaitForever); // clear FIFO content before transmission UsbRegWrite(0x1B0 + 4 * fifo_no, FFRST); uint32_t vdma_txfer_size = MIN(txLen, 1024); _fifo_ctx[fifo_no].isTransferring = true; _fifo_ctx[fifo_no].user_buf_len = 0; _fifo_ctx[fifo_no].dir = 1; osEventFlagsClear(_evt_id, (0x1 << fifo_no)); UsbRegWrite(0x30C + 8 * fifo_no, _dma_remap_addr((uint32_t)buf)); UsbRegWrite(0x308 + 8 * fifo_no, (vdma_txfer_size << 8) | BIT1 | BIT0); uint32_t flags = osEventFlagsWait(_evt_id, (0x1 << fifo_no), osFlagsWaitAny, timeout_ms); if (flags == osFlagsErrorTimeout) status = KDRV_STATUS_USBD_TRANSFER_TIMEOUT; else status = _fifo_ctx[fifo_no].status; _fifo_ctx[fifo_no].isTransferring = false; if (status != KDRV_STATUS_USBD_TRANSFER_DISCONNECTED) osSemaphoreRelease(_enp_semaphore[enp_no]); return status; }