diff options
| author | HiFiPhile <[email protected]> | 2026-06-22 21:30:58 +0200 |
|---|---|---|
| committer | HiFiPhile <[email protected]> | 2026-06-22 21:30:58 +0200 |
| commit | 693cdce08e14833f26f4e8a1f26e4fd546be4c35 (patch) | |
| tree | 7667d2223dc32d9f21b5b60ab200e64ed46e3e20 /src/portable/st | |
| parent | 41e9eaa65a935136085d78ec4b99c81ff991b560 (diff) | |
| parent | cd3561bf158afd5a5718904b8139a338d1e3b67c (diff) | |
Merge remote-tracking branch 'tinyusb/master' into pr-osal-spin-deinit
Signed-off-by: HiFiPhile <[email protected]>
Diffstat (limited to 'src/portable/st')
| -rw-r--r-- | src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c | 660 | ||||
| -rw-r--r-- | src/portable/st/stm32_fsdev/fsdev_at32.h | 131 | ||||
| -rw-r--r-- | src/portable/st/stm32_fsdev/fsdev_ch32.h | 116 | ||||
| -rw-r--r-- | src/portable/st/stm32_fsdev/fsdev_common.c | 116 | ||||
| -rw-r--r-- | src/portable/st/stm32_fsdev/fsdev_common.h | 456 | ||||
| -rw-r--r-- | src/portable/st/stm32_fsdev/fsdev_stm32.h | 322 | ||||
| -rw-r--r-- | src/portable/st/stm32_fsdev/fsdev_type.h | 307 | ||||
| -rw-r--r-- | src/portable/st/stm32_fsdev/hcd_stm32_fsdev.c | 835 |
8 files changed, 1779 insertions, 1164 deletions
diff --git a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c index ed823a832..6f7f490a8 100644 --- a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c +++ b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c @@ -41,6 +41,7 @@ * F302xB/C, F303xB/C, F373 512 byte buffer; no internal D+ pull-up * F302x6/8, F302xD/E2, F303xD/E 1024 byte buffer; no internal D+ pull-up * C0 2048 byte buffer; 32-bit bus; host mode + * C5 2048 byte buffer; 32-bit bus; host mode * G0 2048 byte buffer; 32-bit bus; host mode * G4 1024 byte buffer * H5 2048 byte buffer; 32-bit bus; host mode @@ -59,7 +60,6 @@ * - Enable USB clock; Perhaps use __HAL_RCC_USB_CLK_ENABLE(); * - (Optionally configure GPIO HAL to tell it the USB driver is using the USB pins) * - call tusb_init(); - * - periodically call tusb_task(); * * Assumptions of the driver: * - You are not using CAN (it must share the packet buffer) @@ -87,19 +87,6 @@ * below functions could be adjusting the wrong interrupts (if they had been reconfigured) * - LPM is not used correctly, or at all? * - * USB documentation and Reference implementations - * - STM32 Reference manuals - * - STM32 USB Hardware Guidelines AN4879 - * - * - STM32 HAL (much of this driver is based on this) - * - libopencm3/lib/stm32/common/st_usbfs_core.c - * - Keil USB Device http://www.keil.com/pack/doc/mw/USB/html/group__usbd.html - * - * - YouTube OpenTechLab 011; https://www.youtube.com/watch?v=4FOkJLp_PUw - * - * Advantages over HAL driver: - * - Tiny (saves RAM, assumes a single USB peripheral) - * * Notes: * - The buffer table is allocated as endpoints are opened. The allocation is only * cleared when the device is reset. This may be bad if the USB device needs @@ -108,22 +95,10 @@ #include "tusb_option.h" -#if CFG_TUD_ENABLED && defined(TUP_USBIP_FSDEV) && \ - !(defined(TUP_USBIP_FSDEV_CH32) && CFG_TUD_WCH_USBIP_FSDEV == 0) - -#include "device/dcd.h" +#if CFG_TUD_ENABLED && defined(TUP_USBIP_FSDEV) && !(defined(TUP_USBIP_FSDEV_CH32) && CFG_TUD_WCH_USBIP_FSDEV == 0) -#if defined(TUP_USBIP_FSDEV_STM32) - #include "fsdev_stm32.h" -#elif defined(TUP_USBIP_FSDEV_CH32) - #include "fsdev_ch32.h" -#elif defined(TUP_USBIP_FSDEV_AT32) - #include "fsdev_at32.h" -#else - #error "Unknown USB IP" -#endif - -#include "fsdev_type.h" + #include "device/dcd.h" + #include "fsdev_common.h" //--------------------------------------------------------------------+ // MACRO CONSTANT TYPEDEF @@ -131,25 +106,25 @@ // One of these for every EP IN & OUT, uses a bit of RAM.... typedef struct { - uint8_t *buffer; + uint8_t *buffer; tu_fifo_t *ff; - uint16_t total_len; - uint16_t queued_len; - uint16_t max_packet_size; - uint8_t ep_idx; // index for USB_EPnR register - bool iso_in_sending; // Workaround for ISO IN EP doesn't have interrupt mask + uint16_t total_len; + uint16_t queued_len; + uint16_t max_packet_size; + uint8_t ep_idx; // index for USB_EPnR register + bool iso_in_sending; // Workaround for ISO IN EP doesn't have interrupt mask } xfer_ctl_t; // EP allocator typedef struct { uint8_t ep_num; uint8_t ep_type; - bool allocated[2]; + bool allocated[2]; } ep_alloc_t; static xfer_ctl_t xfer_status[CFG_TUD_ENDPPOINT_MAX][2]; static ep_alloc_t ep_alloc_status[FSDEV_EP_COUNT]; -static uint8_t remoteWakeCountdown; // When wake is requested +static uint8_t remoteWakeCountdown; // When wake is requested //--------------------------------------------------------------------+ // Prototypes @@ -163,17 +138,12 @@ static bool edpt_xfer(uint8_t rhport, uint8_t ep_num, tusb_dir_t dir); // PMA allocation/access static uint16_t ep_buf_ptr; ///< Points to first free memory location static uint32_t dcd_pma_alloc(uint16_t len, bool dbuf); -static uint8_t dcd_ep_alloc(uint8_t ep_addr, uint8_t ep_type); -static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, uint16_t nbytes); -static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, uint16_t nbytes); - -static bool dcd_write_packet_memory_ff(tu_fifo_t *ff, uint16_t dst, uint16_t wNBytes); -static bool dcd_read_packet_memory_ff(tu_fifo_t *ff, uint16_t src, uint16_t wNBytes); +static uint8_t dcd_ep_alloc(uint8_t ep_addr, uint8_t ep_type); static void edpt0_open(uint8_t rhport); TU_ATTR_ALWAYS_INLINE static inline void edpt0_prepare_setup(void) { - btable_set_rx_bufsize(0, BTABLE_BUF_RX, 8); + btable_set_rx_bufsize(0, BTABLE_BUF_RX, 8); } //--------------------------------------------------------------------+ @@ -187,42 +157,22 @@ TU_ATTR_ALWAYS_INLINE static inline xfer_ctl_t *xfer_ctl_ptr(uint8_t epnum, uint //--------------------------------------------------------------------+ // Controller API //--------------------------------------------------------------------+ -bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { - (void) rh_init; - // Follow the RM mentions to use a special ordering of PDWN and FRES - for (volatile uint32_t i = 0; i < 200; i++) { // should be a few us - asm("NOP"); - } - - // Perform USB peripheral reset - FSDEV_REG->CNTR = USB_CNTR_FRES | USB_CNTR_PDWN; - for (volatile uint32_t i = 0; i < 200; i++) { // should be a few us - asm("NOP"); - } +bool dcd_init(uint8_t rhport, const tusb_rhport_init_t *rh_init) { + (void)rh_init; - FSDEV_REG->CNTR &= ~USB_CNTR_PDWN; + fsdev_core_reset(); - // Wait startup time, for F042 and F070, this is <= 1 us. - for (volatile uint32_t i = 0; i < 200; i++) { // should be a few us - asm("NOP"); - } FSDEV_REG->CNTR = 0; // Enable USB -#if !defined(FSDEV_BUS_32BIT) + #if !defined( CFG_TUSB_FSDEV_32BIT) // BTABLE register does not exist any more on 32-bit bus devices FSDEV_REG->BTABLE = FSDEV_BTABLE_BASE; -#endif - - FSDEV_REG->ISTR = 0; // Clear pending interrupts + #endif - // Reset endpoints to disabled - for (uint32_t i = 0; i < FSDEV_EP_COUNT; i++) { - // This doesn't clear all bits since some bits are "toggle", but does set the type to DISABLED. - ep_write(i, 0u, false); - } + // Enable interrupts for device mode + FSDEV_REG->CNTR |= + U_CNTR_RESETM | U_CNTR_ESOFM | U_CNTR_CTRM | U_CNTR_SUSPM | U_CNTR_WKUPM | U_CNTR_PMAOVRM; - FSDEV_REG->CNTR |= USB_CNTR_RESETM | USB_CNTR_ESOFM | USB_CNTR_CTRM | - USB_CNTR_SUSPM | USB_CNTR_WKUPM | USB_CNTR_PMAOVRM; handle_bus_reset(rhport); // Enable pull-up if supported @@ -231,13 +181,21 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { return true; } +bool dcd_deinit(uint8_t rhport) { + (void)rhport; + + fsdev_deinit(); + + return true; +} + void dcd_sof_enable(uint8_t rhport, bool en) { (void)rhport; if (en) { - FSDEV_REG->CNTR |= USB_CNTR_SOFM; + FSDEV_REG->CNTR |= U_CNTR_SOFM; } else { - FSDEV_REG->CNTR &= ~USB_CNTR_SOFM; + FSDEV_REG->CNTR &= ~U_CNTR_SOFM; } } @@ -246,7 +204,7 @@ void dcd_set_address(uint8_t rhport, uint8_t dev_addr) { (void)dev_addr; // Respond with status - dcd_edpt_xfer(rhport, TUSB_DIR_IN_MASK | 0x00, NULL, 0); + dcd_edpt_xfer(rhport, TUSB_DIR_IN_MASK | 0x00, NULL, 0, false); // DCD can only set address after status for this request is complete. // do it at dcd_edpt0_status_complete() @@ -255,7 +213,7 @@ void dcd_set_address(uint8_t rhport, uint8_t dev_addr) { void dcd_remote_wakeup(uint8_t rhport) { (void)rhport; - FSDEV_REG->CNTR |= USB_CNTR_RESUME; + FSDEV_REG->CNTR |= U_CNTR_RESUME; remoteWakeCountdown = 4u; // required to be 1 to 15 ms, ESOF should trigger every 1ms. } @@ -264,8 +222,8 @@ static void handle_bus_reset(uint8_t rhport) { for (uint32_t i = 0; i < FSDEV_EP_COUNT; i++) { // Clear EP allocation status - ep_alloc_status[i].ep_num = 0xFF; - ep_alloc_status[i].ep_type = 0xFF; + ep_alloc_status[i].ep_num = 0xFF; + ep_alloc_status[i].ep_type = 0xFF; ep_alloc_status[i].allocated[0] = false; ep_alloc_status[i].allocated[1] = false; } @@ -273,17 +231,17 @@ static void handle_bus_reset(uint8_t rhport) { // Reset PMA allocation ep_buf_ptr = FSDEV_BTABLE_BASE + 8 * FSDEV_EP_COUNT; - edpt0_open(rhport); // open control endpoint (both IN & OUT) + edpt0_open(rhport); // open control endpoint (both IN & OUT) - FSDEV_REG->DADDR = USB_DADDR_EF; // Enable USB Function + FSDEV_REG->DADDR = U_DADDR_EF; // Enable USB Function } // Handle CTR interrupt for the TX/IN direction static void handle_ctr_tx(uint32_t ep_id) { - uint32_t ep_reg = ep_read(ep_id) | USB_EP_CTR_TX | USB_EP_CTR_RX; + uint32_t ep_reg = ep_read(ep_id) | U_EP_CTR_TX | U_EP_CTR_RX; - uint8_t const ep_num = ep_reg & USB_EPADDR_FIELD; - xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, TUSB_DIR_IN); + const uint8_t ep_num = ep_reg & U_EPADDR_FIELD; + xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, TUSB_DIR_IN); if (ep_is_iso(ep_reg)) { // Ignore spurious interrupts that we don't schedule @@ -293,12 +251,16 @@ static void handle_ctr_tx(uint32_t ep_id) { return; } xfer->iso_in_sending = false; - uint8_t buf_id = (ep_reg & USB_EP_DTOG_TX) ? 0 : 1; + #if FSDEV_USE_SBUF_ISO == 0 + uint8_t buf_id = (ep_reg & U_EP_DTOG_TX) ? 0 : 1; + #else + uint8_t buf_id = BTABLE_BUF_TX; + #endif btable_set_count(ep_id, buf_id, 0); } if (xfer->total_len != xfer->queued_len) { - dcd_transmit_packet(xfer, ep_id); + dcd_transmit_packet(xfer, (uint16_t)ep_id); } else { dcd_event_xfer_complete(0, ep_num | TUSB_DIR_IN_MASK, xfer->queued_len, XFER_RESULT_SUCCESS, true); } @@ -306,17 +268,17 @@ static void handle_ctr_tx(uint32_t ep_id) { static void handle_ctr_setup(uint32_t ep_id) { uint16_t rx_count = btable_get_count(ep_id, BTABLE_BUF_RX); - uint16_t rx_addr = btable_get_addr(ep_id, BTABLE_BUF_RX); - uint8_t setup_packet[8] TU_ATTR_ALIGNED(4); + uint16_t rx_addr = (uint16_t)btable_get_addr(ep_id, BTABLE_BUF_RX); + uint8_t setup_packet[8] TU_ATTR_ALIGNED(4); - dcd_read_packet_memory(setup_packet, rx_addr, rx_count); + tu_hwfifo_read(PMA_BUF_AT(rx_addr), setup_packet, rx_count, NULL); // Clear CTR RX if another setup packet arrived before this, it will be discarded ep_write_clear_ctr(ep_id, TUSB_DIR_OUT); // Setup packet should always be 8 bytes. If not, we probably missed the packet if (rx_count == 8) { - dcd_event_setup_received(0, (uint8_t*) setup_packet, true); + dcd_event_setup_received(0, (uint8_t *)setup_packet, true); // Hardware should reset EP0 RX/TX to NAK and both toggle to 1 } else { // Missed setup packet !!! @@ -327,29 +289,30 @@ static void handle_ctr_setup(uint32_t ep_id) { // Handle CTR interrupt for the RX/OUT direction static void handle_ctr_rx(uint32_t ep_id) { - uint32_t ep_reg = ep_read(ep_id) | USB_EP_CTR_TX | USB_EP_CTR_RX; - uint8_t const ep_num = ep_reg & USB_EPADDR_FIELD; - bool const is_iso = ep_is_iso(ep_reg); - xfer_ctl_t* xfer = xfer_ctl_ptr(ep_num, TUSB_DIR_OUT); + uint32_t ep_reg = ep_read(ep_id) | U_EP_CTR_TX | U_EP_CTR_RX; + const uint8_t ep_num = ep_reg & U_EPADDR_FIELD; + const bool is_iso = ep_is_iso(ep_reg); + xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, TUSB_DIR_OUT); uint8_t buf_id; -#if FSDEV_USE_SBUF_ISO == 0 + #if FSDEV_USE_SBUF_ISO == 0 bool const dbl_buf = is_iso; -#else + #else bool const dbl_buf = false; -#endif + #endif if (dbl_buf) { - buf_id = (ep_reg & USB_EP_DTOG_RX) ? 0 : 1; + buf_id = (ep_reg & U_EP_DTOG_RX) ? 0 : 1; } else { buf_id = BTABLE_BUF_RX; } - uint16_t const rx_count = btable_get_count(ep_id, buf_id); - uint16_t pma_addr = (uint16_t) btable_get_addr(ep_id, buf_id); + const uint16_t rx_count = btable_get_count(ep_id, buf_id); + uint16_t pma_addr = (uint16_t)btable_get_addr(ep_id, buf_id); + fsdev_pma_buf_t *pma_buf = PMA_BUF_AT(pma_addr); if (xfer->ff) { - dcd_read_packet_memory_ff(xfer->ff, pma_addr, rx_count); + tu_hwfifo_read_to_fifo(pma_buf, xfer->ff, rx_count, NULL); } else { - dcd_read_packet_memory(xfer->buffer + xfer->queued_len, pma_addr, rx_count); + tu_hwfifo_read(pma_buf, xfer->buffer + xfer->queued_len, rx_count, NULL); } xfer->queued_len += rx_count; @@ -367,10 +330,10 @@ static void handle_ctr_rx(uint32_t ep_id) { } else { // Set endpoint active again for receiving more data. Note that isochronous endpoints stay active always if (!is_iso) { - uint16_t const cnt = tu_min16(xfer->total_len - xfer->queued_len, xfer->max_packet_size); + const uint16_t cnt = tu_min16(xfer->total_len - xfer->queued_len, xfer->max_packet_size); btable_set_rx_bufsize(ep_id, BTABLE_BUF_RX, cnt); } - ep_reg &= USB_EPREG_MASK | EP_STAT_MASK(TUSB_DIR_OUT); // will change RX Status, reserved other toggle bits + ep_reg &= U_EPREG_MASK | EP_STAT_MASK(TUSB_DIR_OUT); // will change RX Status, reserved other toggle bits ep_change_status(&ep_reg, TUSB_DIR_OUT, EP_STAT_VALID); ep_write(ep_id, ep_reg, false); } @@ -380,80 +343,62 @@ void dcd_int_handler(uint8_t rhport) { uint32_t int_status = FSDEV_REG->ISTR; /* Put SOF flag at the beginning of ISR in case to get least amount of jitter if it is used for timing purposes */ - if (int_status & USB_ISTR_SOF) { - FSDEV_REG->ISTR = (fsdev_bus_t)~USB_ISTR_SOF; - dcd_event_sof(0, FSDEV_REG->FNR & USB_FNR_FN, true); + if ((int_status & U_ISTR_SOF) && (FSDEV_REG->CNTR & U_CNTR_SOFM)) { + FSDEV_REG->ISTR = (fsdev_bus_t)~U_ISTR_SOF; + dcd_event_sof(0, FSDEV_REG->FNR & U_FNR_FN, true); } - if (int_status & USB_ISTR_RESET) { + if (int_status & U_ISTR_RESET) { // USBRST is start of reset. - FSDEV_REG->ISTR = (fsdev_bus_t)~USB_ISTR_RESET; + FSDEV_REG->ISTR = (fsdev_bus_t)~U_ISTR_RESET; handle_bus_reset(rhport); dcd_event_bus_reset(0, TUSB_SPEED_FULL, true); return; // Don't do the rest of the things here; perhaps they've been cleared? } - if (int_status & USB_ISTR_WKUP) { - FSDEV_REG->CNTR &= ~USB_CNTR_LPMODE; - FSDEV_REG->CNTR &= ~USB_CNTR_FSUSP; + if (int_status & U_ISTR_WKUP) { + FSDEV_REG->CNTR &= ~U_CNTR_LPMODE; + FSDEV_REG->CNTR &= ~U_CNTR_FSUSP; - FSDEV_REG->ISTR = (fsdev_bus_t)~USB_ISTR_WKUP; + FSDEV_REG->ISTR = (fsdev_bus_t)~U_ISTR_WKUP; dcd_event_bus_signal(0, DCD_EVENT_RESUME, true); } - if (int_status & USB_ISTR_SUSP) { + if (int_status & U_ISTR_SUSP) { /* Suspend is asserted for both suspend and unplug events. without Vbus monitoring, * these events cannot be differentiated, so we only trigger suspend. */ /* Force low-power mode in the macrocell */ - FSDEV_REG->CNTR |= USB_CNTR_FSUSP; - FSDEV_REG->CNTR |= USB_CNTR_LPMODE; + FSDEV_REG->CNTR |= U_CNTR_FSUSP; + FSDEV_REG->CNTR |= U_CNTR_LPMODE; /* clear of the ISTR bit must be done after setting of CNTR_FSUSP */ - FSDEV_REG->ISTR = (fsdev_bus_t)~USB_ISTR_SUSP; + FSDEV_REG->ISTR = (fsdev_bus_t)~U_ISTR_SUSP; dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true); } - if (int_status & USB_ISTR_ESOF) { + if (int_status & U_ISTR_ESOF) { if (remoteWakeCountdown == 1u) { - FSDEV_REG->CNTR &= ~USB_CNTR_RESUME; + FSDEV_REG->CNTR &= ~U_CNTR_RESUME; } if (remoteWakeCountdown > 0u) { remoteWakeCountdown--; } - FSDEV_REG->ISTR = (fsdev_bus_t)~USB_ISTR_ESOF; + FSDEV_REG->ISTR = (fsdev_bus_t)~U_ISTR_ESOF; } // loop to handle all pending CTR interrupts - while (FSDEV_REG->ISTR & USB_ISTR_CTR) { + while (FSDEV_REG->ISTR & U_ISTR_CTR) { // skip DIR bit, and use CTR TX/RX instead, since there is chance we have both TX/RX completed in one interrupt - uint32_t const ep_id = FSDEV_REG->ISTR & USB_ISTR_EP_ID; - uint32_t const ep_reg = ep_read(ep_id); + const uint32_t ep_id = FSDEV_REG->ISTR & U_ISTR_EP_ID; + const uint32_t ep_reg = ep_read(ep_id); - if (ep_reg & USB_EP_CTR_RX) { - #ifdef FSDEV_BUS_32BIT - /* https://www.st.com/resource/en/errata_sheet/es0561-stm32h503cbebkbrb-device-errata-stmicroelectronics.pdf - * https://www.st.com/resource/en/errata_sheet/es0587-stm32u535xx-and-stm32u545xx-device-errata-stmicroelectronics.pdf - * From H503/U535 errata: Buffer description table update completes after CTR interrupt triggers - * Description: - * - During OUT transfers, the correct transfer interrupt (CTR) is triggered a little before the last USB SRAM accesses - * have completed. If the software responds quickly to the interrupt, the full buffer contents may not be correct. - * Workaround: - * - Software should ensure that a small delay is included before accessing the SRAM contents. This delay - * should be 800 ns in Full Speed mode and 6.4 μs in Low Speed mode - * - Since H5 can run up to 250Mhz -> 1 cycle = 4ns. Per errata, we need to wait 200 cycles. Though executing code - * also takes time, so we'll wait 60 cycles (count = 20). - * - Since Low Speed mode is not supported/popular, we will ignore it for now. - * - * Note: this errata may also apply to G0, U5, H5 etc. - */ - volatile uint32_t cycle_count = 20; // defined as PCD_RX_PMA_CNT in stm32 hal_driver - while (cycle_count > 0U) { - cycle_count--; // each count take 3 cycles (1 for sub, jump, and compare) - } - #endif + if (ep_reg & U_EP_CTR_RX) { + #if defined(TUP_USBIP_FSDEV_STM32) && defined(CFG_TUSB_FSDEV_32BIT) + fsdev_btable_workaround_delay(false); + #endif - if (ep_reg & USB_EP_SETUP) { + if (ep_reg & U_EP_SETUP) { handle_ctr_setup(ep_id); // CTR will be clear after copied setup packet } else { ep_write_clear_ctr(ep_id, TUSB_DIR_OUT); @@ -461,15 +406,15 @@ void dcd_int_handler(uint8_t rhport) { } } - if (ep_reg & USB_EP_CTR_TX) { + if (ep_reg & U_EP_CTR_TX) { ep_write_clear_ctr(ep_id, TUSB_DIR_IN); handle_ctr_tx(ep_id); } } - if (int_status & USB_ISTR_PMAOVR) { + if (int_status & U_ISTR_PMAOVR) { TU_BREAKPOINT(); - FSDEV_REG->ISTR = (fsdev_bus_t)~USB_ISTR_PMAOVR; + FSDEV_REG->ISTR = (fsdev_bus_t)~U_ISTR_PMAOVR; } } @@ -479,14 +424,13 @@ void dcd_int_handler(uint8_t rhport) { // Invoked when a control transfer's status stage is complete. // May help DCD to prepare for next control transfer, this API is optional. -void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const *request) { +void dcd_edpt0_status_complete(uint8_t rhport, const tusb_control_request_t *request) { (void)rhport; if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE && - request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD && - request->bRequest == TUSB_REQ_SET_ADDRESS) { - uint8_t const dev_addr = (uint8_t)request->wValue; - FSDEV_REG->DADDR = (USB_DADDR_EF | dev_addr); + request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD && request->bRequest == TUSB_REQ_SET_ADDRESS) { + const uint8_t dev_addr = (uint8_t)request->wValue; + FSDEV_REG->DADDR = (U_DADDR_EF | dev_addr); } edpt0_prepare_setup(); @@ -497,15 +441,14 @@ void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const *req * In case of double buffering, high 16bit is the address of 2nd buffer * During failure, TU_ASSERT is used. If this happens, rework/reallocate memory manually. */ -static uint32_t dcd_pma_alloc(uint16_t len, bool dbuf) -{ - uint8_t blsize, num_block; +static uint32_t dcd_pma_alloc(uint16_t len, bool dbuf) { + uint8_t blsize, num_block; uint16_t aligned_len = pma_align_buffer_size(len, &blsize, &num_block); - (void) blsize; - (void) num_block; + (void)blsize; + (void)num_block; uint32_t addr = ep_buf_ptr; - ep_buf_ptr = (uint16_t)(ep_buf_ptr + aligned_len); // increment buffer pointer + ep_buf_ptr = (uint16_t)(ep_buf_ptr + aligned_len); // increment buffer pointer if (dbuf) { addr |= ((uint32_t)ep_buf_ptr) << 16; @@ -513,7 +456,7 @@ static uint32_t dcd_pma_alloc(uint16_t len, bool dbuf) } // Verify packet buffer is not overflowed - TU_ASSERT(ep_buf_ptr <= FSDEV_PMA_SIZE, 0xFFFF); + TU_ASSERT(ep_buf_ptr <= CFG_TUSB_FSDEV_PMA_SIZE, 0xFFFF); return addr; } @@ -521,35 +464,32 @@ static uint32_t dcd_pma_alloc(uint16_t len, bool dbuf) /*** * Allocate hardware endpoint */ -static uint8_t dcd_ep_alloc(uint8_t ep_addr, uint8_t ep_type) -{ - uint8_t const epnum = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); +static uint8_t dcd_ep_alloc(uint8_t ep_addr, uint8_t ep_type) { + const uint8_t epnum = tu_edpt_number(ep_addr); + const uint8_t dir = tu_edpt_dir(ep_addr); for (uint8_t i = 0; i < FSDEV_EP_COUNT; i++) { // Check if already allocated - if (ep_alloc_status[i].allocated[dir] && - ep_alloc_status[i].ep_type == ep_type && + if (ep_alloc_status[i].allocated[dir] && ep_alloc_status[i].ep_type == ep_type && ep_alloc_status[i].ep_num == epnum) { return i; } -#if FSDEV_USE_SBUF_ISO == 0 + #if FSDEV_USE_SBUF_ISO == 0 bool const dbl_buf = ep_type == TUSB_XFER_ISOCHRONOUS; -#else + #else bool const dbl_buf = false; -#endif + #endif // If EP of current direction is not allocated // For double-buffered mode both directions needs to be free - if (!ep_alloc_status[i].allocated[dir] && - (!dbl_buf || !ep_alloc_status[i].allocated[dir ^ 1])) { + if (!ep_alloc_status[i].allocated[dir] && (!dbl_buf || !ep_alloc_status[i].allocated[dir ^ 1])) { // Check if EP number is the same if (ep_alloc_status[i].ep_num == 0xFF || ep_alloc_status[i].ep_num == epnum) { // One EP pair has to be the same type if (ep_alloc_status[i].ep_type == 0xFF || ep_alloc_status[i].ep_type == ep_type) { - ep_alloc_status[i].ep_num = epnum; - ep_alloc_status[i].ep_type = ep_type; + ep_alloc_status[i].ep_num = epnum; + ep_alloc_status[i].ep_type = ep_type; ep_alloc_status[i].allocated[dir] = true; return i; @@ -563,25 +503,25 @@ static uint8_t dcd_ep_alloc(uint8_t ep_addr, uint8_t ep_type) } void edpt0_open(uint8_t rhport) { - (void) rhport; + (void)rhport; dcd_ep_alloc(0x0, TUSB_XFER_CONTROL); dcd_ep_alloc(0x80, TUSB_XFER_CONTROL); xfer_status[0][0].max_packet_size = CFG_TUD_ENDPOINT0_SIZE; - xfer_status[0][0].ep_idx = 0; + xfer_status[0][0].ep_idx = 0; xfer_status[0][1].max_packet_size = CFG_TUD_ENDPOINT0_SIZE; - xfer_status[0][1].ep_idx = 0; + xfer_status[0][1].ep_idx = 0; - uint16_t pma_addr0 = dcd_pma_alloc(CFG_TUD_ENDPOINT0_SIZE, false); - uint16_t pma_addr1 = dcd_pma_alloc(CFG_TUD_ENDPOINT0_SIZE, false); + uint16_t pma_addr0 = (uint16_t)dcd_pma_alloc(CFG_TUD_ENDPOINT0_SIZE, false); + uint16_t pma_addr1 = (uint16_t)dcd_pma_alloc(CFG_TUD_ENDPOINT0_SIZE, false); btable_set_addr(0, BTABLE_BUF_RX, pma_addr0); btable_set_addr(0, BTABLE_BUF_TX, pma_addr1); - uint32_t ep_reg = ep_read(0) & ~USB_EPREG_MASK; // only get toggle bits - ep_reg |= USB_EP_CONTROL; + uint32_t ep_reg = ep_read(0) & ~U_EPREG_MASK; // only get toggle bits + ep_reg |= U_EP_CONTROL; ep_change_status(&ep_reg, TUSB_DIR_IN, EP_STAT_NAK); ep_change_status(&ep_reg, TUSB_DIR_OUT, EP_STAT_NAK); // no need to explicitly set DTOG bits since we aren't masked DTOG bit @@ -590,25 +530,25 @@ void edpt0_open(uint8_t rhport) { ep_write(0, ep_reg, false); } -bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *desc_ep) { +bool dcd_edpt_open(uint8_t rhport, const tusb_desc_endpoint_t *desc_ep) { (void)rhport; - uint8_t const ep_addr = desc_ep->bEndpointAddress; - uint8_t const ep_num = tu_edpt_number(ep_addr); - tusb_dir_t const dir = tu_edpt_dir(ep_addr); - const uint16_t packet_size = tu_edpt_packet_size(desc_ep); - uint8_t const ep_idx = dcd_ep_alloc(ep_addr, desc_ep->bmAttributes.xfer); + const uint8_t ep_addr = desc_ep->bEndpointAddress; + const uint8_t ep_num = tu_edpt_number(ep_addr); + const tusb_dir_t dir = tu_edpt_dir(ep_addr); + const uint16_t packet_size = tu_edpt_packet_size(desc_ep); + const uint8_t ep_idx = dcd_ep_alloc(ep_addr, desc_ep->bmAttributes.xfer); TU_ASSERT(ep_idx < FSDEV_EP_COUNT); - uint32_t ep_reg = ep_read(ep_idx) & ~USB_EPREG_MASK; - ep_reg |= tu_edpt_number(ep_addr) | USB_EP_CTR_TX | USB_EP_CTR_RX; + uint32_t ep_reg = ep_read(ep_idx) & ~U_EPREG_MASK; + ep_reg |= tu_edpt_number(ep_addr) | U_EP_CTR_TX | U_EP_CTR_RX; // Set type switch (desc_ep->bmAttributes.xfer) { case TUSB_XFER_BULK: - ep_reg |= USB_EP_BULK; + ep_reg |= U_EP_BULK; break; case TUSB_XFER_INTERRUPT: - ep_reg |= USB_EP_INTERRUPT; + ep_reg |= U_EP_INTERRUPT; break; default: @@ -617,21 +557,21 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *desc_ep) { } /* Create a packet memory buffer area. */ - uint16_t pma_addr = dcd_pma_alloc(packet_size, false); + uint16_t pma_addr = (uint16_t)dcd_pma_alloc(packet_size, false); btable_set_addr(ep_idx, dir == TUSB_DIR_IN ? BTABLE_BUF_TX : BTABLE_BUF_RX, pma_addr); - xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir); + xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir); xfer->max_packet_size = packet_size; - xfer->ep_idx = ep_idx; + xfer->ep_idx = ep_idx; ep_change_status(&ep_reg, dir, EP_STAT_NAK); ep_change_dtog(&ep_reg, dir, 0); // reserve other direction toggle bits if (dir == TUSB_DIR_IN) { - ep_reg &= ~(USB_EPRX_STAT | USB_EP_DTOG_RX); + ep_reg &= ~(U_EPRX_STAT | U_EP_DTOG_RX); } else { - ep_reg &= ~(USB_EPTX_STAT | USB_EP_DTOG_TX); + ep_reg &= ~(U_EPTX_STAT | U_EP_DTOG_TX); } ep_write(ep_idx, ep_reg, true); @@ -646,8 +586,8 @@ void dcd_edpt_close_all(uint8_t rhport) { // Reset endpoint ep_write(i, 0, false); // Clear EP allocation status - ep_alloc_status[i].ep_num = 0xFF; - ep_alloc_status[i].ep_type = 0xFF; + ep_alloc_status[i].ep_num = 0xFF; + ep_alloc_status[i].ep_type = 0xFF; ep_alloc_status[i].allocated[0] = false; ep_alloc_status[i].allocated[1] = false; } @@ -655,68 +595,68 @@ void dcd_edpt_close_all(uint8_t rhport) { dcd_int_enable(rhport); // Reset PMA allocation - ep_buf_ptr = FSDEV_BTABLE_BASE + 8 * CFG_TUD_ENDPPOINT_MAX + 2 * CFG_TUD_ENDPOINT0_SIZE; + ep_buf_ptr = FSDEV_BTABLE_BASE + 8 * FSDEV_EP_COUNT + 2 * CFG_TUD_ENDPOINT0_SIZE; } bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) { (void)rhport; - uint8_t const ep_num = tu_edpt_number(ep_addr); - uint8_t const dir = tu_edpt_dir(ep_addr); - uint8_t const ep_idx = dcd_ep_alloc(ep_addr, TUSB_XFER_ISOCHRONOUS); + const uint8_t ep_num = tu_edpt_number(ep_addr); + const uint8_t dir = tu_edpt_dir(ep_addr); + const uint8_t ep_idx = dcd_ep_alloc(ep_addr, TUSB_XFER_ISOCHRONOUS); -#if CFG_TUD_FSDEV_DOUBLE_BUFFERED_ISO_EP != 0 - uint32_t pma_addr = dcd_pma_alloc(largest_packet_size, true); - uint16_t pma_addr2 = pma_addr >> 16; -#else - uint32_t pma_addr = dcd_pma_alloc(largest_packet_size, false); - uint16_t pma_addr2 = pma_addr; -#endif + #if CFG_TUD_FSDEV_DOUBLE_BUFFERED_ISO_EP != 0 + uint32_t pma_addr = dcd_pma_alloc(largest_packet_size, true); + uint16_t pma_addr2 = (uint16_t)(pma_addr >> 16); + #else + uint32_t pma_addr = dcd_pma_alloc(largest_packet_size, false); + uint16_t pma_addr2 = (uint16_t)pma_addr; + #endif -#if FSDEV_USE_SBUF_ISO == 0 - btable_set_addr(ep_idx, 0, pma_addr); + #if FSDEV_USE_SBUF_ISO == 0 + btable_set_addr(ep_idx, 0, (uint16_t)pma_addr); btable_set_addr(ep_idx, 1, pma_addr2); -#else - btable_set_addr(ep_idx, dir == TUSB_DIR_IN ? BTABLE_BUF_TX : BTABLE_BUF_RX, pma_addr); - (void) pma_addr2; -#endif + #else + btable_set_addr(ep_idx, dir == TUSB_DIR_IN ? BTABLE_BUF_TX : BTABLE_BUF_RX, (uint16_t)pma_addr); + (void)pma_addr2; + #endif - xfer_ctl_t* xfer = xfer_ctl_ptr(ep_num, dir); - xfer->ep_idx = ep_idx; + xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir); + xfer->ep_idx = ep_idx; return true; } -bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const *desc_ep) { +bool dcd_edpt_iso_activate(uint8_t rhport, const tusb_desc_endpoint_t *desc_ep) { (void)rhport; - uint8_t const ep_addr = desc_ep->bEndpointAddress; - uint8_t const ep_num = tu_edpt_number(ep_addr); - tusb_dir_t const dir = tu_edpt_dir(ep_addr); - xfer_ctl_t* xfer = xfer_ctl_ptr(ep_num, dir); + const uint8_t ep_addr = desc_ep->bEndpointAddress; + const uint8_t ep_num = tu_edpt_number(ep_addr); + const tusb_dir_t dir = tu_edpt_dir(ep_addr); + xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir); - uint8_t const ep_idx = xfer->ep_idx; + const uint8_t ep_idx = xfer->ep_idx; xfer->max_packet_size = tu_edpt_packet_size(desc_ep); - uint32_t ep_reg = ep_read(ep_idx) & ~USB_EPREG_MASK; - ep_reg |= tu_edpt_number(ep_addr) | USB_EP_ISOCHRONOUS | USB_EP_CTR_TX | USB_EP_CTR_RX; -#if FSDEV_USE_SBUF_ISO != 0 - ep_reg |= USB_EP_KIND; + uint32_t ep_reg = ep_read(ep_idx) & ~U_EPREG_MASK; + ep_reg |= tu_edpt_number(ep_addr) | U_EP_ISOCHRONOUS | U_EP_CTR_TX | U_EP_CTR_RX; + #if FSDEV_USE_SBUF_ISO != 0 + ep_reg |= U_EP_KIND; ep_change_status(&ep_reg, dir, EP_STAT_DISABLED); ep_change_dtog(&ep_reg, dir, 0); if (dir == TUSB_DIR_IN) { - ep_reg &= ~(USB_EPRX_STAT | USB_EP_DTOG_RX); + ep_reg &= ~(U_EPRX_STAT | U_EP_DTOG_RX); } else { - ep_reg &= ~(USB_EPTX_STAT | USB_EP_DTOG_TX); + ep_reg &= ~(U_EPTX_STAT | U_EP_DTOG_TX); } -#else + #else ep_change_status(&ep_reg, TUSB_DIR_IN, EP_STAT_DISABLED); ep_change_status(&ep_reg, TUSB_DIR_OUT, EP_STAT_DISABLED); ep_change_dtog(&ep_reg, dir, 0); ep_change_dtog(&ep_reg, (tusb_dir_t)(1 - dir), 1); -#endif + #endif ep_write(ep_idx, ep_reg, true); @@ -725,28 +665,29 @@ bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const *desc_ep) // Currently, single-buffered, and only 64 bytes at a time (max) static void dcd_transmit_packet(xfer_ctl_t *xfer, uint16_t ep_ix) { - uint16_t len = tu_min16(xfer->total_len - xfer->queued_len, xfer->max_packet_size); - uint32_t ep_reg = ep_read(ep_ix) | USB_EP_CTR_TX | USB_EP_CTR_RX; // reserve CTR + uint16_t len = tu_min16(xfer->total_len - xfer->queued_len, xfer->max_packet_size); + uint32_t ep_reg = ep_read(ep_ix) | U_EP_CTR_TX | U_EP_CTR_RX; // reserve CTR - bool const is_iso = ep_is_iso(ep_reg); + const bool is_iso = ep_is_iso(ep_reg); uint8_t buf_id; -#if FSDEV_USE_SBUF_ISO == 0 + #if FSDEV_USE_SBUF_ISO == 0 bool const dbl_buf = is_iso; -#else + #else bool const dbl_buf = false; -#endif + #endif if (dbl_buf) { - buf_id = (ep_reg & USB_EP_DTOG_TX) ? 1 : 0; + buf_id = (ep_reg & U_EP_DTOG_TX) ? 1 : 0; } else { buf_id = BTABLE_BUF_TX; } - uint16_t addr_ptr = (uint16_t) btable_get_addr(ep_ix, buf_id); + uint16_t addr_ptr = (uint16_t)btable_get_addr(ep_ix, buf_id); + fsdev_pma_buf_t *pma_buf = PMA_BUF_AT(addr_ptr); if (xfer->ff) { - dcd_write_packet_memory_ff(xfer->ff, addr_ptr, len); + tu_hwfifo_write_from_fifo(pma_buf, xfer->ff, len, NULL); } else { - dcd_write_packet_memory(addr_ptr, &(xfer->buffer[xfer->queued_len]), len); + tu_hwfifo_write(pma_buf, &(xfer->buffer[xfer->queued_len]), len, NULL); } xfer->queued_len += len; @@ -756,25 +697,30 @@ static void dcd_transmit_packet(xfer_ctl_t *xfer, uint16_t ep_ix) { if (is_iso) { xfer->iso_in_sending = true; } - ep_reg &= USB_EPREG_MASK | EP_STAT_MASK(TUSB_DIR_IN); // only change TX Status, reserve other toggle bits + ep_reg &= U_EPREG_MASK | EP_STAT_MASK(TUSB_DIR_IN); // only change TX Status, reserve other toggle bits ep_write(ep_ix, ep_reg, true); } static bool edpt_xfer(uint8_t rhport, uint8_t ep_num, tusb_dir_t dir) { - (void) rhport; + (void)rhport; - xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir); - uint8_t const ep_idx = xfer->ep_idx; + xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir); + const uint8_t ep_idx = xfer->ep_idx; if (dir == TUSB_DIR_IN) { dcd_transmit_packet(xfer, ep_idx); } else { - uint32_t ep_reg = ep_read(ep_idx) | USB_EP_CTR_TX | USB_EP_CTR_RX; // reserve CTR - ep_reg &= USB_EPREG_MASK | EP_STAT_MASK(dir); + uint32_t ep_reg = ep_read(ep_idx) | U_EP_CTR_TX | U_EP_CTR_RX; // reserve CTR + ep_reg &= U_EPREG_MASK | EP_STAT_MASK(dir); uint16_t cnt = tu_min16(xfer->total_len, xfer->max_packet_size); - if (ep_is_iso(ep_reg)) { + #if FSDEV_USE_SBUF_ISO == 0 + bool const dbl_buf = ep_is_iso(ep_reg); + #else + bool const dbl_buf = false; + #endif + if (dbl_buf) { btable_set_rx_bufsize(ep_idx, 0, cnt); btable_set_rx_bufsize(ep_idx, 1, cnt); } else { @@ -788,27 +734,29 @@ static bool edpt_xfer(uint8_t rhport, uint8_t ep_num, tusb_dir_t dir) { return true; } -bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes) { - uint8_t const ep_num = tu_edpt_number(ep_addr); - tusb_dir_t const dir = tu_edpt_dir(ep_addr); - xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir); +bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes, bool is_isr) { + (void)is_isr; + const uint8_t ep_num = tu_edpt_number(ep_addr); + const tusb_dir_t dir = tu_edpt_dir(ep_addr); + xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir); - xfer->buffer = buffer; - xfer->ff = NULL; - xfer->total_len = total_bytes; + xfer->buffer = buffer; + xfer->ff = NULL; + xfer->total_len = total_bytes; xfer->queued_len = 0; return edpt_xfer(rhport, ep_num, dir); } -bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t *ff, uint16_t total_bytes) { - uint8_t const ep_num = tu_edpt_number(ep_addr); - tusb_dir_t const dir = tu_edpt_dir(ep_addr); - xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir); +bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t *ff, uint16_t total_bytes, bool is_isr) { + (void)is_isr; + const uint8_t ep_num = tu_edpt_number(ep_addr); + const tusb_dir_t dir = tu_edpt_dir(ep_addr); + xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir); - xfer->buffer = NULL; - xfer->ff = ff; - xfer->total_len = total_bytes; + xfer->buffer = NULL; + xfer->ff = ff; + xfer->total_len = total_bytes; xfer->queued_len = 0; return edpt_xfer(rhport, ep_num, dir); @@ -816,13 +764,13 @@ bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t *ff, uint16_t void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) { (void)rhport; - uint8_t const ep_num = tu_edpt_number(ep_addr); - tusb_dir_t const dir = tu_edpt_dir(ep_addr); - xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir); - uint8_t const ep_idx = xfer->ep_idx; + const uint8_t ep_num = tu_edpt_number(ep_addr); + const tusb_dir_t dir = tu_edpt_dir(ep_addr); + xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir); + const uint8_t ep_idx = xfer->ep_idx; - uint32_t ep_reg = ep_read(ep_idx) | USB_EP_CTR_TX | USB_EP_CTR_RX; // reserve CTR bits - ep_reg &= USB_EPREG_MASK | EP_STAT_MASK(dir); + uint32_t ep_reg = ep_read(ep_idx) | U_EP_CTR_TX | U_EP_CTR_RX; // reserve CTR bits + ep_reg &= U_EPREG_MASK | EP_STAT_MASK(dir); ep_change_status(&ep_reg, dir, EP_STAT_STALL); ep_write(ep_idx, ep_reg, true); @@ -831,13 +779,13 @@ void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) { void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) { (void)rhport; - uint8_t const ep_num = tu_edpt_number(ep_addr); - tusb_dir_t const dir = tu_edpt_dir(ep_addr); - xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir); - uint8_t const ep_idx = xfer->ep_idx; + const uint8_t ep_num = tu_edpt_number(ep_addr); + const tusb_dir_t dir = tu_edpt_dir(ep_addr); + xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir); + const uint8_t ep_idx = xfer->ep_idx; - uint32_t ep_reg = ep_read(ep_idx) | USB_EP_CTR_TX | USB_EP_CTR_RX; // reserve CTR bits - ep_reg &= USB_EPREG_MASK | EP_STAT_MASK(dir) | EP_DTOG_MASK(dir); + uint32_t ep_reg = ep_read(ep_idx) | U_EP_CTR_TX | U_EP_CTR_RX; // reserve CTR bits + ep_reg &= U_EPREG_MASK | EP_STAT_MASK(dir) | EP_DTOG_MASK(dir); if (!ep_is_iso(ep_reg)) { ep_change_status(&ep_reg, dir, EP_STAT_NAK); @@ -846,168 +794,22 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) { ep_write(ep_idx, ep_reg, true); } -//--------------------------------------------------------------------+ -// PMA read/write -//--------------------------------------------------------------------+ - -// Write to packet memory area (PMA) from user memory -// - Packet memory must be either strictly 16-bit or 32-bit depending on FSDEV_BUS_32BIT -// - Uses unaligned for RAM (since M0 cannot access unaligned address) -static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, uint16_t nbytes) { - if (nbytes == 0) return true; - uint32_t n_write = nbytes / FSDEV_BUS_SIZE; - - fsdev_pma_buf_t* pma_buf = PMA_BUF_AT(dst); - const uint8_t *src8 = src; - - while (n_write--) { - pma_buf->value = fsdevbus_unaligned_read(src8); - src8 += FSDEV_BUS_SIZE; - pma_buf++; - } - - // odd bytes e.g 1 for 16-bit or 1-3 for 32-bit - uint16_t odd = nbytes & (FSDEV_BUS_SIZE - 1); - if (odd) { - fsdev_bus_t temp = 0; - for(uint16_t i = 0; i < odd; i++) { - temp |= *src8++ << (i * 8); - } - pma_buf->value = temp; - } - - return true; +void dcd_int_enable(uint8_t rhport) { + fsdev_int_enable(rhport); } -// Read from packet memory area (PMA) to user memory. -// - Packet memory must be either strictly 16-bit or 32-bit depending on FSDEV_BUS_32BIT -// - Uses unaligned for RAM (since M0 cannot access unaligned address) -static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, uint16_t nbytes) { - if (nbytes == 0) return true; - uint32_t n_read = nbytes / FSDEV_BUS_SIZE; - - fsdev_pma_buf_t* pma_buf = PMA_BUF_AT(src); - uint8_t *dst8 = (uint8_t *)dst; - - while (n_read--) { - fsdevbus_unaligned_write(dst8, (fsdev_bus_t ) pma_buf->value); - dst8 += FSDEV_BUS_SIZE; - pma_buf++; - } - - // odd bytes e.g 1 for 16-bit or 1-3 for 32-bit - uint16_t odd = nbytes & (FSDEV_BUS_SIZE - 1); - if (odd) { - fsdev_bus_t temp = pma_buf->value; - while (odd--) { - *dst8++ = (uint8_t) (temp & 0xfful); - temp >>= 8; - } - } - - return true; +void dcd_int_disable(uint8_t rhport) { + fsdev_int_disable(rhport); } -// Write to PMA from FIFO -static bool dcd_write_packet_memory_ff(tu_fifo_t *ff, uint16_t dst, uint16_t wNBytes) { - if (wNBytes == 0) return true; - - // Since we copy from a ring buffer FIFO, a wrap might occur making it necessary to conduct two copies - tu_fifo_buffer_info_t info; - tu_fifo_get_read_info(ff, &info); - - uint16_t cnt_lin = tu_min16(wNBytes, info.len_lin); - uint16_t cnt_wrap = tu_min16(wNBytes - cnt_lin, info.len_wrap); - uint16_t const cnt_total = cnt_lin + cnt_wrap; - - // We want to read from the FIFO and write it into the PMA, if LIN part is ODD and has WRAPPED part, - // last lin byte will be combined with wrapped part To ensure PMA is always access aligned - uint16_t lin_even = cnt_lin & ~(FSDEV_BUS_SIZE - 1); - uint16_t lin_odd = cnt_lin & (FSDEV_BUS_SIZE - 1); - uint8_t const *src8 = (uint8_t const*) info.ptr_lin; - - // write even linear part - dcd_write_packet_memory(dst, src8, lin_even); - dst += lin_even; - src8 += lin_even; - - if (lin_odd == 0) { - src8 = (uint8_t const*) info.ptr_wrap; - } else { - // Combine last linear bytes + first wrapped bytes to form fsdev bus width data - fsdev_bus_t temp = 0; - uint16_t i; - for(i = 0; i < lin_odd; i++) { - temp |= *src8++ << (i * 8); - } - - src8 = (uint8_t const*) info.ptr_wrap; - for(; i < FSDEV_BUS_SIZE && cnt_wrap > 0; i++, cnt_wrap--) { - temp |= *src8++ << (i * 8); - } - - dcd_write_packet_memory(dst, &temp, FSDEV_BUS_SIZE); - dst += FSDEV_BUS_SIZE; - } - - // write the rest of the wrapped part - dcd_write_packet_memory(dst, src8, cnt_wrap); - - tu_fifo_advance_read_pointer(ff, cnt_total); - return true; + #if defined(USB_BCDR_DPPU) || defined(SYSCFG_PMC_USB_PU) || defined(EXTEN_USBD_PU_EN) +void dcd_connect(uint8_t rhport) { + fsdev_connect(rhport); } -// Read from PMA to FIFO -static bool dcd_read_packet_memory_ff(tu_fifo_t *ff, uint16_t src, uint16_t wNBytes) { - if (wNBytes == 0) return true; - - // Since we copy into a ring buffer FIFO, a wrap might occur making it necessary to conduct two copies - // Check for first linear part - tu_fifo_buffer_info_t info; - tu_fifo_get_write_info(ff, &info); // We want to read from the FIFO - - uint16_t cnt_lin = tu_min16(wNBytes, info.len_lin); - uint16_t cnt_wrap = tu_min16(wNBytes - cnt_lin, info.len_wrap); - uint16_t cnt_total = cnt_lin + cnt_wrap; - - // We want to read from the FIFO and write it into the PMA, if LIN part is ODD and has WRAPPED part, - // last lin byte will be combined with wrapped part To ensure PMA is always access aligned - - uint16_t lin_even = cnt_lin & ~(FSDEV_BUS_SIZE - 1); - uint16_t lin_odd = cnt_lin & (FSDEV_BUS_SIZE - 1); - uint8_t *dst8 = (uint8_t *) info.ptr_lin; - - // read even linear part - dcd_read_packet_memory(dst8, src, lin_even); - dst8 += lin_even; - src += lin_even; - - if (lin_odd == 0) { - dst8 = (uint8_t *) info.ptr_wrap; - } else { - // Combine last linear bytes + first wrapped bytes to form fsdev bus width data - fsdev_bus_t temp; - dcd_read_packet_memory(&temp, src, FSDEV_BUS_SIZE); - src += FSDEV_BUS_SIZE; - - uint16_t i; - for (i = 0; i < lin_odd; i++) { - *dst8++ = (uint8_t) (temp & 0xfful); - temp >>= 8; - } - - dst8 = (uint8_t *) info.ptr_wrap; - for (; i < FSDEV_BUS_SIZE && cnt_wrap > 0; i++, cnt_wrap--) { - *dst8++ = (uint8_t) (temp & 0xfful); - temp >>= 8; - } - } - - // read the rest of the wrapped part - dcd_read_packet_memory(dst8, src, cnt_wrap); - - tu_fifo_advance_write_pointer(ff, cnt_total); - return true; +void dcd_disconnect(uint8_t rhport) { + fsdev_disconnect(rhport); } + #endif #endif diff --git a/src/portable/st/stm32_fsdev/fsdev_at32.h b/src/portable/st/stm32_fsdev/fsdev_at32.h index f7ee89995..107884370 100644 --- a/src/portable/st/stm32_fsdev/fsdev_at32.h +++ b/src/portable/st/stm32_fsdev/fsdev_at32.h @@ -35,7 +35,6 @@ #endif -#define FSDEV_PMA_SIZE (512u) #define FSDEV_USE_SBUF_ISO 0 #define FSDEV_REG_BASE (APB1PERIPH_BASE + 0x00005C00UL) #define FSDEV_PMA_BASE (APB1PERIPH_BASE + 0x00006000UL) @@ -44,114 +43,6 @@ #define CFG_TUD_FSDEV_DOUBLE_BUFFERED_ISO_EP 0 #endif -/**************************** ISTR interrupt events *************************/ -#define USB_ISTR_CTR ((uint16_t)0x8000U) /*!< Correct TRansfer (clear-only bit) */ -#define USB_ISTR_PMAOVR ((uint16_t)0x4000U) /*!< DMA OVeR/underrun (clear-only bit) */ -#define USB_ISTR_ERR ((uint16_t)0x2000U) /*!< ERRor (clear-only bit) */ -#define USB_ISTR_WKUP ((uint16_t)0x1000U) /*!< WaKe UP (clear-only bit) */ -#define USB_ISTR_SUSP ((uint16_t)0x0800U) /*!< SUSPend (clear-only bit) */ -#define USB_ISTR_RESET ((uint16_t)0x0400U) /*!< RESET (clear-only bit) */ -#define USB_ISTR_SOF ((uint16_t)0x0200U) /*!< Start Of Frame (clear-only bit) */ -#define USB_ISTR_ESOF ((uint16_t)0x0100U) /*!< Expected Start Of Frame (clear-only bit) */ -#define USB_ISTR_DIR ((uint16_t)0x0010U) /*!< DIRection of transaction (read-only bit) */ -#define USB_ISTR_EP_ID ((uint16_t)0x000FU) /*!< EndPoint IDentifier (read-only bit) */ - -/* Legacy defines */ -#define USB_ISTR_PMAOVRM USB_ISTR_PMAOVR - -#define USB_CLR_CTR (~USB_ISTR_CTR) /*!< clear Correct TRansfer bit */ -#define USB_CLR_PMAOVR (~USB_ISTR_PMAOVR) /*!< clear DMA OVeR/underrun bit*/ -#define USB_CLR_ERR (~USB_ISTR_ERR) /*!< clear ERRor bit */ -#define USB_CLR_WKUP (~USB_ISTR_WKUP) /*!< clear WaKe UP bit */ -#define USB_CLR_SUSP (~USB_ISTR_SUSP) /*!< clear SUSPend bit */ -#define USB_CLR_RESET (~USB_ISTR_RESET) /*!< clear RESET bit */ -#define USB_CLR_SOF (~USB_ISTR_SOF) /*!< clear Start Of Frame bit */ -#define USB_CLR_ESOF (~USB_ISTR_ESOF) /*!< clear Expected Start Of Frame bit */ - -/* Legacy defines */ -#define USB_CLR_PMAOVRM USB_CLR_PMAOVR - -/************************* CNTR control register bits definitions ***********/ -#define USB_CNTR_CTRM ((uint16_t)0x8000U) /*!< Correct TRansfer Mask */ -#define USB_CNTR_PMAOVR ((uint16_t)0x4000U) /*!< DMA OVeR/underrun Mask */ -#define USB_CNTR_ERRM ((uint16_t)0x2000U) /*!< ERRor Mask */ -#define USB_CNTR_WKUPM ((uint16_t)0x1000U) /*!< WaKe UP Mask */ -#define USB_CNTR_SUSPM ((uint16_t)0x0800U) /*!< SUSPend Mask */ -#define USB_CNTR_RESETM ((uint16_t)0x0400U) /*!< RESET Mask */ -#define USB_CNTR_SOFM ((uint16_t)0x0200U) /*!< Start Of Frame Mask */ -#define USB_CNTR_ESOFM ((uint16_t)0x0100U) /*!< Expected Start Of Frame Mask */ -#define USB_CNTR_RESUME ((uint16_t)0x0010U) /*!< RESUME request */ -#define USB_CNTR_FSUSP ((uint16_t)0x0008U) /*!< Force SUSPend */ -#define USB_CNTR_LPMODE ((uint16_t)0x0004U) /*!< Low-power MODE */ -#define USB_CNTR_PDWN ((uint16_t)0x0002U) /*!< Power DoWN */ -#define USB_CNTR_FRES ((uint16_t)0x0001U) /*!< Force USB RESet */ - -/* Legacy defines */ -#define USB_CNTR_PMAOVRM USB_CNTR_PMAOVR -#define USB_CNTR_LP_MODE USB_CNTR_LPMODE - -/******************** FNR Frame Number Register bit definitions ************/ -#define USB_FNR_RXDP ((uint16_t)0x8000U) /*!< status of D+ data line */ -#define USB_FNR_RXDM ((uint16_t)0x4000U) /*!< status of D- data line */ -#define USB_FNR_LCK ((uint16_t)0x2000U) /*!< LoCKed */ -#define USB_FNR_LSOF ((uint16_t)0x1800U) /*!< Lost SOF */ -#define USB_FNR_FN ((uint16_t)0x07FFU) /*!< Frame Number */ - -/******************** DADDR Device ADDRess bit definitions ****************/ -#define USB_DADDR_EF ((uint8_t)0x80U) /*!< USB device address Enable Function */ -#define USB_DADDR_ADD ((uint8_t)0x7FU) /*!< USB device address */ - -/****************************** Endpoint register *************************/ -#define USB_EP0R USB_BASE /*!< endpoint 0 register address */ -#define USB_EP1R (USB_BASE + 0x04U) /*!< endpoint 1 register address */ -#define USB_EP2R (USB_BASE + 0x08U) /*!< endpoint 2 register address */ -#define USB_EP3R (USB_BASE + 0x0CU) /*!< endpoint 3 register address */ -#define USB_EP4R (USB_BASE + 0x10U) /*!< endpoint 4 register address */ -#define USB_EP5R (USB_BASE + 0x14U) /*!< endpoint 5 register address */ -#define USB_EP6R (USB_BASE + 0x18U) /*!< endpoint 6 register address */ -#define USB_EP7R (USB_BASE + 0x1CU) /*!< endpoint 7 register address */ -/* bit positions */ -#define USB_EP_CTR_RX ((uint16_t)0x8000U) /*!< EndPoint Correct TRansfer RX */ -#define USB_EP_DTOG_RX ((uint16_t)0x4000U) /*!< EndPoint Data TOGGLE RX */ -#define USB_EPRX_STAT ((uint16_t)0x3000U) /*!< EndPoint RX STATus bit field */ -#define USB_EP_SETUP ((uint16_t)0x0800U) /*!< EndPoint SETUP */ -#define USB_EP_T_FIELD ((uint16_t)0x0600U) /*!< EndPoint TYPE */ -#define USB_EP_KIND ((uint16_t)0x0100U) /*!< EndPoint KIND */ -#define USB_EP_CTR_TX ((uint16_t)0x0080U) /*!< EndPoint Correct TRansfer TX */ -#define USB_EP_DTOG_TX ((uint16_t)0x0040U) /*!< EndPoint Data TOGGLE TX */ -#define USB_EPTX_STAT ((uint16_t)0x0030U) /*!< EndPoint TX STATus bit field */ -#define USB_EPADDR_FIELD ((uint16_t)0x000FU) /*!< EndPoint ADDRess FIELD */ - -/* EndPoint REGister MASK (no toggle fields) */ -#define USB_EPREG_MASK (USB_EP_CTR_RX|USB_EP_SETUP|USB_EP_T_FIELD|USB_EP_KIND|USB_EP_CTR_TX|USB_EPADDR_FIELD) - /*!< EP_TYPE[1:0] EndPoint TYPE */ -#define USB_EP_TYPE_MASK ((uint16_t)0x0600U) /*!< EndPoint TYPE Mask */ -#define USB_EP_BULK ((uint16_t)0x0000U) /*!< EndPoint BULK */ -#define USB_EP_CONTROL ((uint16_t)0x0200U) /*!< EndPoint CONTROL */ -#define USB_EP_ISOCHRONOUS ((uint16_t)0x0400U) /*!< EndPoint ISOCHRONOUS */ -#define USB_EP_INTERRUPT ((uint16_t)0x0600U) /*!< EndPoint INTERRUPT */ -#define USB_EP_T_MASK ((uint16_t) ~USB_EP_T_FIELD & USB_EPREG_MASK) - -#define USB_EPKIND_MASK ((uint16_t) ~USB_EP_KIND & USB_EPREG_MASK) /*!< EP_KIND EndPoint KIND */ - /*!< STAT_TX[1:0] STATus for TX transfer */ -#define USB_EP_TX_DIS ((uint16_t)0x0000U) /*!< EndPoint TX DISabled */ -#define USB_EP_TX_STALL ((uint16_t)0x0010U) /*!< EndPoint TX STALLed */ -#define USB_EP_TX_NAK ((uint16_t)0x0020U) /*!< EndPoint TX NAKed */ -#define USB_EP_TX_VALID ((uint16_t)0x0030U) /*!< EndPoint TX VALID */ -#define USB_EPTX_DTOG1 ((uint16_t)0x0010U) /*!< EndPoint TX Data TOGgle bit1 */ -#define USB_EPTX_DTOG2 ((uint16_t)0x0020U) /*!< EndPoint TX Data TOGgle bit2 */ -#define USB_EPTX_DTOGMASK (USB_EPTX_STAT|USB_EPREG_MASK) - /*!< STAT_RX[1:0] STATus for RX transfer */ -#define USB_EP_RX_DIS ((uint16_t)0x0000U) /*!< EndPoint RX DISabled */ -#define USB_EP_RX_STALL ((uint16_t)0x1000U) /*!< EndPoint RX STALLed */ -#define USB_EP_RX_NAK ((uint16_t)0x2000U) /*!< EndPoint RX NAKed */ -#define USB_EP_RX_VALID ((uint16_t)0x3000U) /*!< EndPoint RX VALID */ -#define USB_EPRX_DTOG1 ((uint16_t)0x1000U) /*!< EndPoint RX Data TOGgle bit1 */ -#define USB_EPRX_DTOG2 ((uint16_t)0x2000U) /*!< EndPoint RX Data TOGgle bit1 */ -#define USB_EPRX_DTOGMASK (USB_EPRX_STAT|USB_EPREG_MASK) - -#include "fsdev_type.h" - //--------------------------------------------------------------------+ // //--------------------------------------------------------------------+ @@ -168,16 +59,16 @@ enum { FSDEV_IRQ_NUM = TU_ARRAY_SIZE(fsdev_irq) }; #error "Unsupported MCU" #endif -void dcd_int_enable(uint8_t rhport) { +TU_ATTR_ALWAYS_INLINE static inline void fsdev_int_enable(uint8_t rhport) { (void)rhport; #if (CFG_TUSB_MCU == OPT_MCU_AT32F403A_407) || (CFG_TUSB_MCU == OPT_MCU_AT32F413) // AT32F403A/407 devices allow to remap the USB interrupt vectors from // shared USB/CAN IRQs to separate CAN and USB IRQs. // This dynamically checks if this remap is active to enable the right IRQs. if (CRM->intmap_bit.usbintmap) { - NVIC_DisableIRQ(USBFS_MAPH_IRQn); - NVIC_DisableIRQ(USBFS_MAPL_IRQn); - NVIC_DisableIRQ(USBFSWakeUp_IRQn); + NVIC_EnableIRQ(USBFS_MAPH_IRQn); + NVIC_EnableIRQ(USBFS_MAPL_IRQn); + NVIC_EnableIRQ(USBFSWakeUp_IRQn); } else #endif { @@ -187,7 +78,7 @@ void dcd_int_enable(uint8_t rhport) { } } -void dcd_int_disable(uint8_t rhport) { +TU_ATTR_ALWAYS_INLINE static inline void fsdev_int_disable(uint8_t rhport) { (void)rhport; #if (CFG_TUSB_MCU == OPT_MCU_AT32F403A_407) || (CFG_TUSB_MCU == OPT_MCU_AT32F413) // AT32F403A/407 devices allow to remap the USB interrupt vectors from @@ -206,20 +97,20 @@ void dcd_int_disable(uint8_t rhport) { } } -void dcd_disconnect(uint8_t rhport) { +TU_ATTR_ALWAYS_INLINE static inline void fsdev_disconnect(uint8_t rhport) { (void) rhport; /* disable usb phy */ - FSDEV_REG->CNTR |= USB_CNTR_PDWN; + *(volatile uint32_t*)(FSDEV_REG_BASE + 0x40) |= U_CNTR_PDWN; /* D+ 1.5k pull-up disable, USB->cfg_bit.puo = TRUE; */ - *(uint32_t *)(FSDEV_REG_BASE+0x60) |= (1u<<1); + *(volatile uint32_t *)(FSDEV_REG_BASE+0x60) |= (1u<<1); } -void dcd_connect(uint8_t rhport) { +TU_ATTR_ALWAYS_INLINE static inline void fsdev_connect(uint8_t rhport) { (void) rhport; /* enable usb phy */ - FSDEV_REG->CNTR &= ~USB_CNTR_PDWN; + *(volatile uint32_t*)(FSDEV_REG_BASE + 0x40) &= ~U_CNTR_PDWN; /* Dp 1.5k pull-up enable, USB->cfg_bit.puo = 0; */ - *(uint32_t *)(FSDEV_REG_BASE+0x60) &= ~(1u<<1); + *(volatile uint32_t *)(FSDEV_REG_BASE+0x60) &= ~(1u<<1); } #endif diff --git a/src/portable/st/stm32_fsdev/fsdev_ch32.h b/src/portable/st/stm32_fsdev/fsdev_ch32.h index ceebb6dab..b92bf3f58 100644 --- a/src/portable/st/stm32_fsdev/fsdev_ch32.h +++ b/src/portable/st/stm32_fsdev/fsdev_ch32.h @@ -53,7 +53,6 @@ #pragma GCC diagnostic pop #endif -#define FSDEV_PMA_SIZE (512u) #define FSDEV_USE_SBUF_ISO 0 #define FSDEV_REG_BASE (APB1PERIPH_BASE + 0x00005C00UL) #define FSDEV_PMA_BASE (APB1PERIPH_BASE + 0x00006000UL) @@ -62,113 +61,6 @@ #define CFG_TUD_FSDEV_DOUBLE_BUFFERED_ISO_EP 0 #endif -/**************************** ISTR interrupt events *************************/ -#define USB_ISTR_CTR ((uint16_t)0x8000U) /*!< Correct TRansfer (clear-only bit) */ -#define USB_ISTR_PMAOVR ((uint16_t)0x4000U) /*!< DMA OVeR/underrun (clear-only bit) */ -#define USB_ISTR_ERR ((uint16_t)0x2000U) /*!< ERRor (clear-only bit) */ -#define USB_ISTR_WKUP ((uint16_t)0x1000U) /*!< WaKe UP (clear-only bit) */ -#define USB_ISTR_SUSP ((uint16_t)0x0800U) /*!< SUSPend (clear-only bit) */ -#define USB_ISTR_RESET ((uint16_t)0x0400U) /*!< RESET (clear-only bit) */ -#define USB_ISTR_SOF ((uint16_t)0x0200U) /*!< Start Of Frame (clear-only bit) */ -#define USB_ISTR_ESOF ((uint16_t)0x0100U) /*!< Expected Start Of Frame (clear-only bit) */ -#define USB_ISTR_DIR ((uint16_t)0x0010U) /*!< DIRection of transaction (read-only bit) */ -#define USB_ISTR_EP_ID ((uint16_t)0x000FU) /*!< EndPoint IDentifier (read-only bit) */ - -/* Legacy defines */ -#define USB_ISTR_PMAOVRM USB_ISTR_PMAOVR - -#define USB_CLR_CTR (~USB_ISTR_CTR) /*!< clear Correct TRansfer bit */ -#define USB_CLR_PMAOVR (~USB_ISTR_PMAOVR) /*!< clear DMA OVeR/underrun bit*/ -#define USB_CLR_ERR (~USB_ISTR_ERR) /*!< clear ERRor bit */ -#define USB_CLR_WKUP (~USB_ISTR_WKUP) /*!< clear WaKe UP bit */ -#define USB_CLR_SUSP (~USB_ISTR_SUSP) /*!< clear SUSPend bit */ -#define USB_CLR_RESET (~USB_ISTR_RESET) /*!< clear RESET bit */ -#define USB_CLR_SOF (~USB_ISTR_SOF) /*!< clear Start Of Frame bit */ -#define USB_CLR_ESOF (~USB_ISTR_ESOF) /*!< clear Expected Start Of Frame bit */ - -/* Legacy defines */ -#define USB_CLR_PMAOVRM USB_CLR_PMAOVR - -/************************* CNTR control register bits definitions ***********/ -#define USB_CNTR_CTRM ((uint16_t)0x8000U) /*!< Correct TRansfer Mask */ -#define USB_CNTR_PMAOVR ((uint16_t)0x4000U) /*!< DMA OVeR/underrun Mask */ -#define USB_CNTR_ERRM ((uint16_t)0x2000U) /*!< ERRor Mask */ -#define USB_CNTR_WKUPM ((uint16_t)0x1000U) /*!< WaKe UP Mask */ -#define USB_CNTR_SUSPM ((uint16_t)0x0800U) /*!< SUSPend Mask */ -#define USB_CNTR_RESETM ((uint16_t)0x0400U) /*!< RESET Mask */ -#define USB_CNTR_SOFM ((uint16_t)0x0200U) /*!< Start Of Frame Mask */ -#define USB_CNTR_ESOFM ((uint16_t)0x0100U) /*!< Expected Start Of Frame Mask */ -#define USB_CNTR_RESUME ((uint16_t)0x0010U) /*!< RESUME request */ -#define USB_CNTR_FSUSP ((uint16_t)0x0008U) /*!< Force SUSPend */ -#define USB_CNTR_LPMODE ((uint16_t)0x0004U) /*!< Low-power MODE */ -#define USB_CNTR_PDWN ((uint16_t)0x0002U) /*!< Power DoWN */ -#define USB_CNTR_FRES ((uint16_t)0x0001U) /*!< Force USB RESet */ - -/* Legacy defines */ -#define USB_CNTR_PMAOVRM USB_CNTR_PMAOVR -#define USB_CNTR_LP_MODE USB_CNTR_LPMODE - -/******************** FNR Frame Number Register bit definitions ************/ -#define USB_FNR_RXDP ((uint16_t)0x8000U) /*!< status of D+ data line */ -#define USB_FNR_RXDM ((uint16_t)0x4000U) /*!< status of D- data line */ -#define USB_FNR_LCK ((uint16_t)0x2000U) /*!< LoCKed */ -#define USB_FNR_LSOF ((uint16_t)0x1800U) /*!< Lost SOF */ -#define USB_FNR_FN ((uint16_t)0x07FFU) /*!< Frame Number */ - -/******************** DADDR Device ADDRess bit definitions ****************/ -#define USB_DADDR_EF ((uint8_t)0x80U) /*!< USB device address Enable Function */ -#define USB_DADDR_ADD ((uint8_t)0x7FU) /*!< USB device address */ - -/****************************** Endpoint register *************************/ -#define USB_EP0R USB_BASE /*!< endpoint 0 register address */ -#define USB_EP1R (USB_BASE + 0x04U) /*!< endpoint 1 register address */ -#define USB_EP2R (USB_BASE + 0x08U) /*!< endpoint 2 register address */ -#define USB_EP3R (USB_BASE + 0x0CU) /*!< endpoint 3 register address */ -#define USB_EP4R (USB_BASE + 0x10U) /*!< endpoint 4 register address */ -#define USB_EP5R (USB_BASE + 0x14U) /*!< endpoint 5 register address */ -#define USB_EP6R (USB_BASE + 0x18U) /*!< endpoint 6 register address */ -#define USB_EP7R (USB_BASE + 0x1CU) /*!< endpoint 7 register address */ -/* bit positions */ -#define USB_EP_CTR_RX ((uint16_t)0x8000U) /*!< EndPoint Correct TRansfer RX */ -#define USB_EP_DTOG_RX ((uint16_t)0x4000U) /*!< EndPoint Data TOGGLE RX */ -#define USB_EPRX_STAT ((uint16_t)0x3000U) /*!< EndPoint RX STATus bit field */ -#define USB_EP_SETUP ((uint16_t)0x0800U) /*!< EndPoint SETUP */ -#define USB_EP_T_FIELD ((uint16_t)0x0600U) /*!< EndPoint TYPE */ -#define USB_EP_KIND ((uint16_t)0x0100U) /*!< EndPoint KIND */ -#define USB_EP_CTR_TX ((uint16_t)0x0080U) /*!< EndPoint Correct TRansfer TX */ -#define USB_EP_DTOG_TX ((uint16_t)0x0040U) /*!< EndPoint Data TOGGLE TX */ -#define USB_EPTX_STAT ((uint16_t)0x0030U) /*!< EndPoint TX STATus bit field */ -#define USB_EPADDR_FIELD ((uint16_t)0x000FU) /*!< EndPoint ADDRess FIELD */ - -/* EndPoint REGister MASK (no toggle fields) */ -#define USB_EPREG_MASK (USB_EP_CTR_RX|USB_EP_SETUP|USB_EP_T_FIELD|USB_EP_KIND|USB_EP_CTR_TX|USB_EPADDR_FIELD) - /*!< EP_TYPE[1:0] EndPoint TYPE */ -#define USB_EP_TYPE_MASK ((uint16_t)0x0600U) /*!< EndPoint TYPE Mask */ -#define USB_EP_BULK ((uint16_t)0x0000U) /*!< EndPoint BULK */ -#define USB_EP_CONTROL ((uint16_t)0x0200U) /*!< EndPoint CONTROL */ -#define USB_EP_ISOCHRONOUS ((uint16_t)0x0400U) /*!< EndPoint ISOCHRONOUS */ -#define USB_EP_INTERRUPT ((uint16_t)0x0600U) /*!< EndPoint INTERRUPT */ -#define USB_EP_T_MASK ((uint16_t) ~USB_EP_T_FIELD & USB_EPREG_MASK) - -#define USB_EPKIND_MASK ((uint16_t) ~USB_EP_KIND & USB_EPREG_MASK) /*!< EP_KIND EndPoint KIND */ - /*!< STAT_TX[1:0] STATus for TX transfer */ -#define USB_EP_TX_DIS ((uint16_t)0x0000U) /*!< EndPoint TX DISabled */ -#define USB_EP_TX_STALL ((uint16_t)0x0010U) /*!< EndPoint TX STALLed */ -#define USB_EP_TX_NAK ((uint16_t)0x0020U) /*!< EndPoint TX NAKed */ -#define USB_EP_TX_VALID ((uint16_t)0x0030U) /*!< EndPoint TX VALID */ -#define USB_EPTX_DTOG1 ((uint16_t)0x0010U) /*!< EndPoint TX Data TOGgle bit1 */ -#define USB_EPTX_DTOG2 ((uint16_t)0x0020U) /*!< EndPoint TX Data TOGgle bit2 */ -#define USB_EPTX_DTOGMASK (USB_EPTX_STAT|USB_EPREG_MASK) - /*!< STAT_RX[1:0] STATus for RX transfer */ -#define USB_EP_RX_DIS ((uint16_t)0x0000U) /*!< EndPoint RX DISabled */ -#define USB_EP_RX_STALL ((uint16_t)0x1000U) /*!< EndPoint RX STALLed */ -#define USB_EP_RX_NAK ((uint16_t)0x2000U) /*!< EndPoint RX NAKed */ -#define USB_EP_RX_VALID ((uint16_t)0x3000U) /*!< EndPoint RX VALID */ -#define USB_EPRX_DTOG1 ((uint16_t)0x1000U) /*!< EndPoint RX Data TOGgle bit1 */ -#define USB_EPRX_DTOG2 ((uint16_t)0x2000U) /*!< EndPoint RX Data TOGgle bit1 */ -#define USB_EPRX_DTOGMASK (USB_EPRX_STAT|USB_EPREG_MASK) - - //--------------------------------------------------------------------+ // //--------------------------------------------------------------------+ @@ -184,26 +76,26 @@ enum { FSDEV_IRQ_NUM = TU_ARRAY_SIZE(fsdev_irq) }; #error "Unsupported MCU" #endif -void dcd_int_enable(uint8_t rhport) { +TU_ATTR_ALWAYS_INLINE static inline void fsdev_int_enable(uint8_t rhport) { (void)rhport; for(uint8_t i=0; i < FSDEV_IRQ_NUM; i++) { NVIC_EnableIRQ(fsdev_irq[i]); } } -void dcd_int_disable(uint8_t rhport) { +TU_ATTR_ALWAYS_INLINE static inline void fsdev_int_disable(uint8_t rhport) { (void)rhport; for(uint8_t i=0; i < FSDEV_IRQ_NUM; i++) { NVIC_DisableIRQ(fsdev_irq[i]); } } -void dcd_disconnect(uint8_t rhport) { +TU_ATTR_ALWAYS_INLINE static inline void fsdev_disconnect(uint8_t rhport) { (void) rhport; EXTEN->EXTEN_CTR &= ~EXTEN_USBD_PU_EN; } -void dcd_connect(uint8_t rhport) { +TU_ATTR_ALWAYS_INLINE static inline void fsdev_connect(uint8_t rhport) { (void) rhport; EXTEN->EXTEN_CTR |= EXTEN_USBD_PU_EN; } diff --git a/src/portable/st/stm32_fsdev/fsdev_common.c b/src/portable/st/stm32_fsdev/fsdev_common.c new file mode 100644 index 000000000..7c4572a1e --- /dev/null +++ b/src/portable/st/stm32_fsdev/fsdev_common.c @@ -0,0 +1,116 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2024 Ha Thach (tinyusb.org) + * Copyright (c) 2025, HiFiPhile (Zixun LI) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#include "tusb_option.h" + +#if defined(TUP_USBIP_FSDEV) && (CFG_TUH_ENABLED || CFG_TUD_ENABLED) + +#include "fsdev_common.h" + +//--------------------------------------------------------------------+ +// Global +//--------------------------------------------------------------------+ + +// Reset the USB Core +void fsdev_core_reset(void) { + // Perform USB peripheral reset + FSDEV_REG->CNTR = U_CNTR_FRES | U_CNTR_PDWN; + for (volatile uint32_t i = 0; i < 200; i++) { // should be a few us + asm("NOP"); + } + + FSDEV_REG->CNTR &= ~U_CNTR_PDWN; + + // Wait startup time, for F042 and F070, this is <= 1 us. + for (volatile uint32_t i = 0; i < 200; i++) { // should be a few us + asm("NOP"); + } + + // Clear pending interrupts + FSDEV_REG->ISTR = 0; +} + +// De-initialize the USB Core +void fsdev_deinit(void) { + // Disable all interrupts and force USB reset + FSDEV_REG->CNTR = U_CNTR_FRES; + + // Clear pending interrupts + FSDEV_REG->ISTR = 0; + + // Put USB peripheral in power down mode + FSDEV_REG->CNTR = U_CNTR_FRES | U_CNTR_PDWN; + for (volatile uint32_t i = 0; i < 200; i++) { // should be a few us + asm("NOP"); + } +} + +//--------------------------------------------------------------------+ +// BTable Helper +//--------------------------------------------------------------------+ + +// Aligned buffer size according to hardware +uint16_t pma_align_buffer_size(uint16_t size, uint8_t* blsize, uint8_t* num_block) { + /* The STM32 full speed USB peripheral supports only a limited set of + * buffer sizes given by the RX buffer entry format in the USB_BTABLE. */ + uint16_t block_in_bytes; + if (size > 62) { + block_in_bytes = 32; + *blsize = 1; + *num_block = (uint8_t)tu_div_ceil(size, 32); + } else { + block_in_bytes = 2; + *blsize = 0; + *num_block = (uint8_t)tu_div_ceil(size, 2); + } + + return (*num_block) * block_in_bytes; +} + +// Set RX buffer size +void btable_set_rx_bufsize(uint32_t ep_id, uint8_t buf_id, uint16_t wCount) { + uint8_t blsize, num_block; + (void) pma_align_buffer_size(wCount, &blsize, &num_block); + + /* Encode into register. When BLSIZE==1, we need to subtract 1 block count */ + uint16_t bl_nb = (blsize << 15) | ((num_block - blsize) << 10); + if (bl_nb == 0) { + // zlp but 0 is invalid value, set blsize to 1 (32 bytes) + // Note: lower value can cause PMAOVR on setup with ch32v203 + bl_nb = 1 << 15; + } + +#ifdef CFG_TUSB_FSDEV_32BIT + uint32_t count_addr = FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr; + count_addr = (bl_nb << 16) | (count_addr & 0x0000FFFFu); + FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr = count_addr; +#else + FSDEV_BTABLE->ep16[ep_id][buf_id].count = bl_nb; +#endif +} + +#endif diff --git a/src/portable/st/stm32_fsdev/fsdev_common.h b/src/portable/st/stm32_fsdev/fsdev_common.h new file mode 100644 index 000000000..af84b8b97 --- /dev/null +++ b/src/portable/st/stm32_fsdev/fsdev_common.h @@ -0,0 +1,456 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) N Conrad + * Copyright (c) 2024, hathach (tinyusb.org) + * Copyright (c) 2025, HiFiPhile (Zixun LI) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +#ifndef TUSB_FSDEV_COMMON_H +#define TUSB_FSDEV_COMMON_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "common/tusb_common.h" + +#if CFG_TUD_ENABLED + #include "device/dcd.h" +#endif + +#if CFG_TUH_ENABLED + #include "host/hcd.h" +#endif + +//--------------------------------------------------------------------+ +// FSDEV Register Bit Definitions +// Vendor-independent definitions with U_ prefix to avoid conflicts. +// Based on the common USB FSDEV IP block register layout. +// Lower 16 bits are shared across all variants (STM32, CH32, AT32). +// Upper 16 bits (DRD extensions) only exist on 32-bit DRD MCUs. +//--------------------------------------------------------------------+ + +// EPnR / CHEPnR - Endpoint/Channel Register +// DTOG and STAT bits are toggle-on-write-1. CTR bits are clear-on-write-0. +// +// 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +// CTR_RX DTOG_RX STAT_RX[1:0] SETUP EP_TYPE[1:0] KIND CTR_TX DTOG_TX STAT_TX[1:0] EA[3:0] +// +// DRD 32-bit only (C0, G0, H5, U0, U5): +// 31:27 26 25 24 23 22 21 20 19 18 17 16 +// Rsvd ERR_RX ERR_TX LSEP NAK DEVADDR[6:0] +#define U_EP_CTR_RX 0x8000u +#define U_EP_DTOG_RX 0x4000u +#define U_EPRX_STAT 0x3000u +#define U_EP_SETUP 0x0800u +#define U_EP_T_FIELD 0x0600u +#define U_EP_KIND 0x0100u +#define U_EP_CTR_TX 0x0080u +#define U_EP_DTOG_TX 0x0040u +#define U_EPTX_STAT 0x0030u +#define U_EPADDR_FIELD 0x000Fu + +// DRD 32-bit upper bits +#define U_EP_ERRRX 0x04000000u +#define U_EP_ERRTX 0x02000000u +#define U_EP_LSEP 0x01000000u +#define U_EP_NAK 0x00800000u +#define U_EP_DEVADDR 0x007F0000u +#define U_EP_DEVADDR_Pos 16u + +// Endpoint types (EP_TYPE field values) +#define U_EP_BULK 0x0000u +#define U_EP_CONTROL 0x0200u +#define U_EP_ISOCHRONOUS 0x0400u +#define U_EP_INTERRUPT 0x0600u +#define U_EP_TYPE_MASK (U_EP_T_FIELD) + +// EP register mask components (non-toggle bits preserved during read-modify-write) +// Excludes DTOG_RX, STAT_RX, DTOG_TX, STAT_TX (toggle-on-write-1) +#define U_EPREG_MASK_16 (U_EP_CTR_RX | U_EP_SETUP | U_EP_T_FIELD | U_EP_KIND | U_EP_CTR_TX | U_EPADDR_FIELD) +#define U_EPREG_MASK_32 (U_EP_ERRRX | U_EP_ERRTX | U_EP_LSEP | U_EP_NAK | U_EP_DEVADDR | U_EPREG_MASK_16) + +// EP register mask selection based on bus width +#ifdef CFG_TUSB_FSDEV_32BIT + #define U_EPREG_MASK U_EPREG_MASK_32 +#else + #define U_EPREG_MASK U_EPREG_MASK_16 +#endif + +#define U_EPKIND_MASK ((uint32_t)(~U_EP_KIND) & U_EPREG_MASK) +#define U_EPTX_DTOGMASK (U_EPTX_STAT | U_EPREG_MASK) +#define U_EPRX_DTOGMASK (U_EPRX_STAT | U_EPREG_MASK) + +// Bit positions +#define U_EPTX_STAT_Pos 4u +#define U_EP_DTOG_TX_Pos 6u +#define U_EP_CTR_TX_Pos 7u + +// Data toggle helpers +#define U_EPTX_DTOG1 0x0010u +#define U_EPTX_DTOG2 0x0020u +#define U_EPRX_DTOG1 0x1000u +#define U_EPRX_DTOG2 0x2000u + +// CNTR - Control Register +// 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +// CTRM PMAOVRM ERRM WKUPM SUSPM RESETM SOFM ESOFM Rsvd Rsvd Rsvd RESUME FSUSP LPMODE PDWN FRES +// +// DRD 32-bit only: +// 31 30:16 +// HOST Rsvd +#define U_CNTR_CTRM 0x8000u +#define U_CNTR_PMAOVRM 0x4000u +#define U_CNTR_ERRM 0x2000u +#define U_CNTR_WKUPM 0x1000u +#define U_CNTR_SUSPM 0x0800u +#define U_CNTR_RESETM 0x0400u +#define U_CNTR_SOFM 0x0200u +#define U_CNTR_ESOFM 0x0100u +#define U_CNTR_RESUME 0x0010u +#define U_CNTR_FSUSP 0x0008u +#define U_CNTR_LPMODE 0x0004u +#define U_CNTR_PDWN 0x0002u +#define U_CNTR_FRES 0x0001u + +#define U_CNTR_HOST 0x80000000u // DRD: enable host mode +#define U_CNTR_DCON 0x0400u // DRD host: same bit as RESETM + +// ISTR - Interrupt Status Register +// 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +// CTR PMAOVR ERR WKUP SUSP RESET SOF ESOF Rsvd Rsvd Rsvd DIR EP_ID[3:0] +// +// DRD 32-bit only: +// 31 30 29 28:16 +// Rsvd LS_DCONN DCON_STAT Rsvd +#define U_ISTR_CTR 0x8000u +#define U_ISTR_PMAOVR 0x4000u +#define U_ISTR_ERR 0x2000u +#define U_ISTR_WKUP 0x1000u +#define U_ISTR_SUSP 0x0800u +#define U_ISTR_RESET 0x0400u +#define U_ISTR_SOF 0x0200u +#define U_ISTR_ESOF 0x0100u +#define U_ISTR_DIR 0x0010u +#define U_ISTR_EP_ID 0x000Fu + +#define U_ISTR_LS_DCONN 0x40000000u // DRD: low-speed device connected +#define U_ISTR_DCON_STAT 0x20000000u // DRD: device connection status +#define U_ISTR_DCON 0x0400u // DRD host: same bit as RESET + +// FNR - Frame Number Register (read-only) +// 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +// RXDP RXDM LCK[2:0] FN[10:0] +#define U_FNR_RXDP 0x8000u +#define U_FNR_RXDM 0x4000u +#define U_FNR_FN 0x07FFu + +// DADDR - Device Address Register +// 15:8 7 6 5 4 3 2 1 0 +// Rsvd EF ADD[6:0] +#define U_DADDR_EF 0x80u + +// LPMCSR - LPM Control and Status Register +// Supported: STM32 F0, L0, L4, G0, G4, C0, H5, U0, WB. Not on: F1, F3, AT32, CH32. +// 15:8 7 6 5 4 3 2 1 0 +// Rsvd BESL[3:0] Rsvd REMWAKE Rsvd LPMACK LMPEN +#define U_LPMCSR_LMPEN 0x0001u +#define U_LPMCSR_LPMACK 0x0002u +#define U_LPMCSR_REMWAKE 0x0008u +#define U_LPMCSR_BESL 0x00F0u + +// BCDR - Battery Charging Detector Register +// Supported: STM32 F0, L0, L4, G0, G4, C0, H5, U0, WB. Not on: F1, F3, AT32, CH32. +// 15 14:8 7 6 5 4 3 2 1 0 +// DPPU Rsvd PS2DET SDET PDET DCDET SDEN PDEN DCDEN BCDEN +#define U_BCDR_BCDEN 0x0001u +#define U_BCDR_DCDEN 0x0002u +#define U_BCDR_PDEN 0x0004u +#define U_BCDR_SDEN 0x0008u +#define U_BCDR_DCDET 0x0010u +#define U_BCDR_PDET 0x0020u +#define U_BCDR_SDET 0x0040u +#define U_BCDR_PS2DET 0x0080u +#define U_BCDR_DPPU 0x8000u + +// Channel status (DRD host mode, reuses STAT_TX/STAT_RX bit positions) +#define U_CH_TX_STTX 0x0030u +#define U_CH_TX_ACK_SBUF 0x0000u +#define U_CH_TX_STALL 0x0010u +#define U_CH_TX_NAK 0x0020u + +#define U_CH_RX_STRX 0x3000u +#define U_CH_RX_ACK_SBUF 0x0000u +#define U_CH_RX_STALL 0x1000u +#define U_CH_RX_NAK 0x2000u +#define U_CH_RX_VALID 0x3000u + +//--------------------------------------------------------------------+ +// Registers Typedef +//--------------------------------------------------------------------+ +// hardware limit endpoint +#define FSDEV_EP_COUNT 8 + +// The fsdev_bus_t type can be used for both register and PMA access necessities +#ifdef CFG_TUSB_FSDEV_32BIT +typedef uint32_t fsdev_bus_t; +#else +typedef uint16_t fsdev_bus_t; +#endif + +// volatile 32-bit aligned +#define _va32 volatile TU_ATTR_ALIGNED(4) + +typedef struct { + struct { + _va32 fsdev_bus_t reg; + } ep[FSDEV_EP_COUNT]; + + _va32 uint32_t RESERVED7[8]; // Reserved + _va32 fsdev_bus_t CNTR; // 40: Control register + _va32 fsdev_bus_t ISTR; // 44: Interrupt status register + _va32 fsdev_bus_t FNR; // 48: Frame number register + _va32 fsdev_bus_t DADDR; // 4C: Device address register + _va32 fsdev_bus_t BTABLE; // 50: Buffer Table address register + _va32 fsdev_bus_t LPMCSR; // 54: LPM Control and Status (not on F1, F3, AT32, CH32) + _va32 fsdev_bus_t BCDR; // 58: Battery Charging Detector (not on F1, F3, AT32, CH32) +} fsdev_regs_t; + +TU_VERIFY_STATIC(offsetof(fsdev_regs_t, CNTR) == 0x40, "Wrong offset"); +TU_VERIFY_STATIC(sizeof(fsdev_regs_t) == 0x5C, "Size is not correct"); + +#define FSDEV_REG ((fsdev_regs_t *)FSDEV_REG_BASE) + +//--------------------------------------------------------------------+ +// BTable and PMA Access +//--------------------------------------------------------------------+ + +// If sharing with CAN, one can set this to be non-zero to give CAN space where it wants it +// Both of these MUST be a multiple of 2, and are in byte units. +#ifndef FSDEV_BTABLE_BASE + #define FSDEV_BTABLE_BASE 0U +#endif +TU_VERIFY_STATIC((FSDEV_BTABLE_BASE & 0x7) == 0, "BTABLE base must be aligned to 8 bytes"); + +#define FSDEV_ADDR_DATA_RATIO (CFG_TUSB_FIFO_HWFIFO_ADDR_STRIDE/CFG_TUSB_FIFO_HWFIFO_DATA_STRIDE) + +// Need alignment when access address is 32 bit but data is only 16-bit +#if FSDEV_ADDR_DATA_RATIO == 2 + #define fsdev_addr_data_align TU_ATTR_ALIGNED(4) +#else + #define fsdev_addr_data_align +#endif + +enum { + BTABLE_BUF_TX = 0, + BTABLE_BUF_RX = 1 +}; + +// Buffer Table is located in Packet Memory Area (PMA) and therefore its address access is forced to either +// 16-bit or 32-bit depending on CFG_TUSB_FSDEV_32BIT. +// 0: TX (IN), 1: RX (OUT) +typedef union { + // data is strictly 16-bit access (address could be 32-bit aligned) + struct { + volatile fsdev_addr_data_align uint16_t addr; + volatile fsdev_addr_data_align uint16_t count; + } ep16[FSDEV_EP_COUNT][2]; + + // strictly 32-bit access + struct { + volatile uint32_t count_addr; + } ep32[FSDEV_EP_COUNT][2]; +} fsdev_btable_t; + +TU_VERIFY_STATIC(sizeof(fsdev_btable_t) == FSDEV_EP_COUNT * 8 * FSDEV_ADDR_DATA_RATIO, "size is not correct"); +TU_VERIFY_STATIC(FSDEV_BTABLE_BASE + FSDEV_EP_COUNT * 8 <= CFG_TUSB_FSDEV_PMA_SIZE, "BTABLE does not fit in PMA RAM"); + +#define FSDEV_BTABLE ((volatile fsdev_btable_t *)(FSDEV_PMA_BASE + FSDEV_ADDR_DATA_RATIO * FSDEV_BTABLE_BASE)) + +typedef struct { + volatile fsdev_addr_data_align fsdev_bus_t value; +} fsdev_pma_buf_t; + +#define PMA_BUF_AT(_addr) ((fsdev_pma_buf_t *)(FSDEV_PMA_BASE + FSDEV_ADDR_DATA_RATIO * (_addr))) + +//--------------------------------------------------------------------+ +// Vendor-specific includes +//--------------------------------------------------------------------+ +#if defined(TUP_USBIP_FSDEV_STM32) + #include "fsdev_stm32.h" +#elif defined(TUP_USBIP_FSDEV_CH32) + #include "fsdev_ch32.h" +#elif defined(TUP_USBIP_FSDEV_AT32) + #include "fsdev_at32.h" +#else + #error "Unknown USB IP" +#endif + +//--------------------------------------------------------------------+ +// Endpoint Helper +// - CTR is write 0 to clear +// - DTOG and STAT are write 1 to toggle +//--------------------------------------------------------------------+ +typedef enum { + EP_STAT_DISABLED = 0, + EP_STAT_STALL = 1, + EP_STAT_NAK = 2, + EP_STAT_VALID = 3 +} ep_stat_t; + +#define EP_STAT_MASK(_dir) (3u << (U_EPTX_STAT_Pos + ((_dir) == TUSB_DIR_IN ? 0 : 8))) +#define EP_DTOG_MASK(_dir) (1u << (U_EP_DTOG_TX_Pos + ((_dir) == TUSB_DIR_IN ? 0 : 8))) + +#define CH_STAT_MASK(_dir) (3u << (U_EPTX_STAT_Pos + ((_dir) == TUSB_DIR_IN ? 8 : 0))) +#define CH_DTOG_MASK(_dir) (1u << (U_EP_DTOG_TX_Pos + ((_dir) == TUSB_DIR_IN ? 8 : 0))) + +TU_ATTR_ALWAYS_INLINE static inline uint32_t ep_read(uint32_t ep_id) { + return FSDEV_REG->ep[ep_id].reg; +} + +TU_ATTR_ALWAYS_INLINE static inline void ep_write(uint32_t ep_id, uint32_t value, bool need_exclusive) { + if (need_exclusive) { + fsdev_int_disable(0); + } + + FSDEV_REG->ep[ep_id].reg = (fsdev_bus_t)value; + + if (need_exclusive) { + fsdev_int_enable(0); + } +} + +TU_ATTR_ALWAYS_INLINE static inline void ep_write_clear_ctr(uint32_t ep_id, tusb_dir_t dir) { + uint32_t reg = FSDEV_REG->ep[ep_id].reg; + reg |= U_EP_CTR_TX | U_EP_CTR_RX; + reg &= U_EPREG_MASK; + reg &= ~(1u << (U_EP_CTR_TX_Pos + (dir == TUSB_DIR_IN ? 0u : 8u))); + ep_write(ep_id, reg, false); +} + +TU_ATTR_ALWAYS_INLINE static inline void ep_change_status(uint32_t *reg, tusb_dir_t dir, ep_stat_t state) { + *reg ^= (state << (U_EPTX_STAT_Pos + (dir == TUSB_DIR_IN ? 0 : 8))); +} + +TU_ATTR_ALWAYS_INLINE static inline void ep_change_dtog(uint32_t *reg, tusb_dir_t dir, uint8_t state) { + *reg ^= (state << (U_EP_DTOG_TX_Pos + (dir == TUSB_DIR_IN ? 0 : 8))); +} + +TU_ATTR_ALWAYS_INLINE static inline bool ep_is_iso(uint32_t reg) { + return (reg & U_EP_TYPE_MASK) == U_EP_ISOCHRONOUS; +} + +//--------------------------------------------------------------------+ +// Channel Helper +// - Direction is opposite to endpoint direction +//--------------------------------------------------------------------+ + +TU_ATTR_ALWAYS_INLINE static inline uint32_t ch_read(uint32_t ch_id) { + return ep_read(ch_id); +} + +TU_ATTR_ALWAYS_INLINE static inline void ch_write(uint32_t ch_id, uint32_t value, bool need_exclusive) { + ep_write(ch_id, value, need_exclusive); +} + +TU_ATTR_ALWAYS_INLINE static inline void ch_write_clear_ctr(uint32_t ch_id, tusb_dir_t dir) { + uint32_t reg = FSDEV_REG->ep[ch_id].reg; + reg |= U_EP_CTR_TX | U_EP_CTR_RX; + reg &= U_EPREG_MASK; + reg &= ~(1u << (U_EP_CTR_TX_Pos + (dir == TUSB_DIR_IN ? 8u : 0u))); + ep_write(ch_id, reg, false); +} + +TU_ATTR_ALWAYS_INLINE static inline void ch_change_status(uint32_t *reg, tusb_dir_t dir, ep_stat_t state) { + *reg ^= (state << (U_EPTX_STAT_Pos + (dir == TUSB_DIR_IN ? 8 : 0))); +} + +TU_ATTR_ALWAYS_INLINE static inline void ch_change_dtog(uint32_t *reg, tusb_dir_t dir, uint8_t state) { + *reg ^= (state << (U_EP_DTOG_TX_Pos + (dir == TUSB_DIR_IN ? 8 : 0))); +} + +//--------------------------------------------------------------------+ +// BTable Helper +//--------------------------------------------------------------------+ + +TU_ATTR_ALWAYS_INLINE static inline uint32_t btable_get_addr(uint32_t ep_id, uint8_t buf_id) { +#ifdef CFG_TUSB_FSDEV_32BIT + return FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr & 0x0000FFFFu; +#else + return FSDEV_BTABLE->ep16[ep_id][buf_id].addr; +#endif +} + +TU_ATTR_ALWAYS_INLINE static inline void btable_set_addr(uint32_t ep_id, uint8_t buf_id, uint16_t addr) { +#ifdef CFG_TUSB_FSDEV_32BIT + uint32_t count_addr = FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr; + count_addr = (count_addr & 0xFFFF0000u) | (addr & 0x0000FFFCu); + + FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr = count_addr; +#else + FSDEV_BTABLE->ep16[ep_id][buf_id].addr = addr; +#endif +} + +TU_ATTR_ALWAYS_INLINE static inline uint16_t btable_get_count(uint32_t ep_id, uint8_t buf_id) { + uint16_t count; +#ifdef CFG_TUSB_FSDEV_32BIT + count = (FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr >> 16); +#else + count = FSDEV_BTABLE->ep16[ep_id][buf_id].count; +#endif + return count & 0x3FFU; +} + +TU_ATTR_ALWAYS_INLINE static inline void btable_set_count(uint32_t ep_id, uint8_t buf_id, uint16_t byte_count) { +#ifdef CFG_TUSB_FSDEV_32BIT + uint32_t count_addr = FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr; + count_addr = (count_addr & ~0x03FF0000u) | ((byte_count & 0x3FFu) << 16); + + FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr = count_addr; +#else + uint16_t cnt = FSDEV_BTABLE->ep16[ep_id][buf_id].count; + cnt = (cnt & ~0x3FFU) | (byte_count & 0x3FFU); + + FSDEV_BTABLE->ep16[ep_id][buf_id].count = cnt; +#endif +} + +// Reset the USB Core +void fsdev_core_reset(void); + +// De-initialize the USB Core +void fsdev_deinit(void); + +// Aligned buffer size according to hardware +uint16_t pma_align_buffer_size(uint16_t size, uint8_t *blsize, uint8_t *num_block); + +// Set RX buffer size +void btable_set_rx_bufsize(uint32_t ep_id, uint8_t buf_id, uint16_t wCount); + +#ifdef __cplusplus +} +#endif + +#endif /* TUSB_FSDEV_COMMON_H */ diff --git a/src/portable/st/stm32_fsdev/fsdev_stm32.h b/src/portable/st/stm32_fsdev/fsdev_stm32.h index 63b50f13d..93cdac808 100644 --- a/src/portable/st/stm32_fsdev/fsdev_stm32.h +++ b/src/portable/st/stm32_fsdev/fsdev_stm32.h @@ -32,10 +32,16 @@ #ifndef TUSB_FSDEV_STM32_H #define TUSB_FSDEV_STM32_H -#if CFG_TUSB_MCU == OPT_MCU_STM32F0 +#if CFG_TUSB_MCU == OPT_MCU_STM32C0 + #include "stm32c0xx.h" + #define FSDEV_HAS_SBUF_ISO 1 + +#elif CFG_TUSB_MCU == OPT_MCU_STM32C5 + #include "stm32c5xx.h" + #define FSDEV_HAS_SBUF_ISO 1 + +#elif CFG_TUSB_MCU == OPT_MCU_STM32F0 #include "stm32f0xx.h" - #define FSDEV_PMA_SIZE (1024u) - #define FSDEV_REG_BASE USB_BASE #define FSDEV_HAS_SBUF_ISO 0 // F0x2 models are crystal-less // All have internal D+ pull-up @@ -44,190 +50,67 @@ #elif CFG_TUSB_MCU == OPT_MCU_STM32F1 #include "stm32f1xx.h" - #define FSDEV_PMA_SIZE (512u) #define FSDEV_HAS_SBUF_ISO 0 // NO internal Pull-ups // *B, and *C: 2 x 16 bits/word - // F1 names this differently from the rest - #define USB_CNTR_LPMODE USB_CNTR_LP_MODE - -#elif defined(STM32F302xB) || defined(STM32F302xC) || \ - defined(STM32F303xB) || defined(STM32F303xC) || \ - defined(STM32F373xC) +#elif CFG_TUSB_MCU == OPT_MCU_STM32F3 #include "stm32f3xx.h" - #define FSDEV_PMA_SIZE (512u) - #define FSDEV_HAS_SBUF_ISO 0 - // NO internal Pull-ups - // *B, and *C: 1 x 16 bits/word - // PMA dedicated to USB (no sharing with CAN) - -#elif defined(STM32F302x6) || defined(STM32F302x8) || \ - defined(STM32F302xD) || defined(STM32F302xE) || \ - defined(STM32F303xD) || defined(STM32F303xE) - #include "stm32f3xx.h" - #define FSDEV_PMA_SIZE (1024u) - #define FSDEV_HAS_SBUF_ISO 0 - // NO internal Pull-ups - // *6, *8, *D, and *E: 2 x 16 bits/word LPM Support - // When CAN clock is enabled, USB can use first 768 bytes ONLY. - -#elif CFG_TUSB_MCU == OPT_MCU_STM32L0 - #include "stm32l0xx.h" - #define FSDEV_PMA_SIZE (1024u) - #define FSDEV_HAS_SBUF_ISO 0 - -#elif CFG_TUSB_MCU == OPT_MCU_STM32L1 - #include "stm32l1xx.h" - #define FSDEV_PMA_SIZE (512u) - #define FSDEV_HAS_SBUF_ISO 0 - -#elif CFG_TUSB_MCU == OPT_MCU_STM32G4 - #include "stm32g4xx.h" - #define FSDEV_PMA_SIZE (1024u) #define FSDEV_HAS_SBUF_ISO 0 + // NO internal Pull-ups. PMA dedicated to USB (no sharing with CAN) + // xB, and xC: 512 bytes + // x6, x8, xD, and xE: 1024 bytes + LPM Support. When CAN clock is enabled, USB can use the first 768 bytes ONLY. #elif CFG_TUSB_MCU == OPT_MCU_STM32G0 #include "stm32g0xx.h" - #define FSDEV_PMA_SIZE (2048u) #define FSDEV_HAS_SBUF_ISO 1 - #define USB USB_DRD_FS - - #define USB_EP_CTR_RX USB_EP_VTRX - #define USB_EP_CTR_TX USB_EP_VTTX - #define USB_EP_T_FIELD USB_CHEP_UTYPE - #define USB_EPREG_MASK USB_CHEP_REG_MASK - #define USB_EPTX_DTOGMASK USB_CHEP_TX_DTOGMASK - #define USB_EPRX_DTOGMASK USB_CHEP_RX_DTOGMASK - #define USB_EPTX_DTOG1 USB_CHEP_TX_DTOG1 - #define USB_EPTX_DTOG2 USB_CHEP_TX_DTOG2 - #define USB_EPRX_DTOG1 USB_CHEP_RX_DTOG1 - #define USB_EPRX_DTOG2 USB_CHEP_RX_DTOG2 - #define USB_EPRX_STAT USB_CH_RX_VALID - #define USB_EPKIND_MASK USB_EP_KIND_MASK - #define USB_CNTR_FRES USB_CNTR_USBRST - #define USB_CNTR_RESUME USB_CNTR_L2RES - #define USB_ISTR_EP_ID USB_ISTR_IDN - #define USB_EPADDR_FIELD USB_CHEP_ADDR - #define USB_CNTR_LPMODE USB_CNTR_SUSPRDY - #define USB_CNTR_FSUSP USB_CNTR_SUSPEN -#elif CFG_TUSB_MCU == OPT_MCU_STM32C0 - #include "stm32c0xx.h" - #define FSDEV_PMA_SIZE (2048u) - #define FSDEV_HAS_SBUF_ISO 1 - #define USB USB_DRD_FS - #define USB_EP_CTR_RX USB_CHEP_VTRX - #define USB_EP_CTR_TX USB_CHEP_VTTX - #define USB_EPREG_MASK USB_CHEP_REG_MASK - #define USB_CNTR_FRES USB_CNTR_USBRST - #define USB_CNTR_RESUME USB_CNTR_L2RES - #define USB_ISTR_EP_ID USB_ISTR_IDN - #define USB_EPADDR_FIELD USB_CHEP_ADDR - #define USB_CNTR_LPMODE USB_CNTR_SUSPRDY - #define USB_CNTR_FSUSP USB_CNTR_SUSPEN +#elif CFG_TUSB_MCU == OPT_MCU_STM32G4 + #include "stm32g4xx.h" + #define FSDEV_HAS_SBUF_ISO 0 #elif CFG_TUSB_MCU == OPT_MCU_STM32H5 #include "stm32h5xx.h" - #define FSDEV_PMA_SIZE (2048u) #define FSDEV_HAS_SBUF_ISO 1 - #define USB USB_DRD_FS - #define USB_EP_CTR_RX USB_EP_VTRX - #define USB_EP_CTR_TX USB_EP_VTTX - #define USB_EP_T_FIELD USB_CHEP_UTYPE - #define USB_EPREG_MASK USB_CHEP_REG_MASK - #define USB_EPTX_DTOGMASK USB_CHEP_TX_DTOGMASK - #define USB_EPRX_DTOGMASK USB_CHEP_RX_DTOGMASK - #define USB_EPTX_DTOG1 USB_CHEP_TX_DTOG1 - #define USB_EPTX_DTOG2 USB_CHEP_TX_DTOG2 - #define USB_EPRX_DTOG1 USB_CHEP_RX_DTOG1 - #define USB_EPRX_DTOG2 USB_CHEP_RX_DTOG2 - #define USB_EPRX_STAT USB_CH_RX_VALID - #define USB_EPKIND_MASK USB_EP_KIND_MASK - #define USB_CNTR_FRES USB_CNTR_USBRST - #define USB_CNTR_RESUME USB_CNTR_L2RES - #define USB_ISTR_EP_ID USB_ISTR_IDN - #define USB_EPADDR_FIELD USB_CHEP_ADDR - #define USB_CNTR_LPMODE USB_CNTR_SUSPRDY - #define USB_CNTR_FSUSP USB_CNTR_SUSPEN +#elif CFG_TUSB_MCU == OPT_MCU_STM32L0 + #include "stm32l0xx.h" + #define FSDEV_HAS_SBUF_ISO 0 -#elif CFG_TUSB_MCU == OPT_MCU_STM32WB - #include "stm32wbxx.h" - #define FSDEV_PMA_SIZE (1024u) +#elif CFG_TUSB_MCU == OPT_MCU_STM32L1 + #include "stm32l1xx.h" #define FSDEV_HAS_SBUF_ISO 0 - /* ST provided header has incorrect value of USB_PMAADDR */ - #define FSDEV_PMA_BASE USB1_PMAADDR #elif CFG_TUSB_MCU == OPT_MCU_STM32L4 #include "stm32l4xx.h" - #define FSDEV_PMA_SIZE (1024u) #define FSDEV_HAS_SBUF_ISO 0 #elif CFG_TUSB_MCU == OPT_MCU_STM32L5 #include "stm32l5xx.h" - #define FSDEV_PMA_SIZE (1024u) #define FSDEV_HAS_SBUF_ISO 0 #ifndef USB_PMAADDR #define USB_PMAADDR (USB_BASE + (USB_PMAADDR_NS - USB_BASE_NS)) #endif +#elif CFG_TUSB_MCU == OPT_MCU_STM32U0 + #include "stm32u0xx.h" + #define FSDEV_HAS_SBUF_ISO 1 + +#elif CFG_TUSB_MCU == OPT_MCU_STM32U3 + #include "stm32u3xx.h" + #define FSDEV_HAS_SBUF_ISO 1 + #elif CFG_TUSB_MCU == OPT_MCU_STM32U5 #include "stm32u5xx.h" - #define FSDEV_PMA_SIZE (2048u) #define FSDEV_HAS_SBUF_ISO 1 - #define USB USB_DRD_FS - #define USB_EP_CTR_RX USB_EP_VTRX - #define USB_EP_CTR_TX USB_EP_VTTX - #define USB_EP_T_FIELD USB_CHEP_UTYPE - #define USB_EPREG_MASK USB_CHEP_REG_MASK - #define USB_EPTX_DTOGMASK USB_CHEP_TX_DTOGMASK - #define USB_EPRX_DTOGMASK USB_CHEP_RX_DTOGMASK - #define USB_EPTX_DTOG1 USB_CHEP_TX_DTOG1 - #define USB_EPTX_DTOG2 USB_CHEP_TX_DTOG2 - #define USB_EPRX_DTOG1 USB_CHEP_RX_DTOG1 - #define USB_EPRX_DTOG2 USB_CHEP_RX_DTOG2 - #define USB_EPRX_STAT USB_CH_RX_VALID - #define USB_EPKIND_MASK USB_EP_KIND_MASK - #define USB_CNTR_FRES USB_CNTR_USBRST - #define USB_CNTR_RESUME USB_CNTR_L2RES - #define USB_ISTR_EP_ID USB_ISTR_IDN - #define USB_EPADDR_FIELD USB_CHEP_ADDR - #define USB_CNTR_LPMODE USB_CNTR_SUSPRDY - #define USB_CNTR_FSUSP USB_CNTR_SUSPEN - -#elif CFG_TUSB_MCU == OPT_MCU_STM32U0 - #include "stm32u0xx.h" - #define FSDEV_PMA_SIZE (1024u) - #define FSDEV_BUS_32BIT - // Disable SBUF_ISO on U0 for now due to bad performance (audio glitching) +#elif CFG_TUSB_MCU == OPT_MCU_STM32WB + #include "stm32wbxx.h" #define FSDEV_HAS_SBUF_ISO 0 - #define USB USB_DRD_FS - - #define USB_EP_CTR_RX USB_EP_VTRX - #define USB_EP_CTR_TX USB_EP_VTTX - #define USB_EP_T_FIELD USB_CHEP_UTYPE - #define USB_EPREG_MASK USB_CHEP_REG_MASK - #define USB_EPTX_DTOGMASK USB_CHEP_TX_DTOGMASK - #define USB_EPRX_DTOGMASK USB_CHEP_RX_DTOGMASK - #define USB_EPTX_DTOG1 USB_CHEP_TX_DTOG1 - #define USB_EPTX_DTOG2 USB_CHEP_TX_DTOG2 - #define USB_EPRX_DTOG1 USB_CHEP_RX_DTOG1 - #define USB_EPRX_DTOG2 USB_CHEP_RX_DTOG2 - #define USB_EPRX_STAT USB_CH_RX_VALID - #define USB_EPKIND_MASK USB_EP_KIND_MASK - #define USB_CNTR_FRES USB_CNTR_USBRST - #define USB_CNTR_RESUME USB_CNTR_L2RES - #define USB_ISTR_EP_ID USB_ISTR_IDN - #define USB_EPADDR_FIELD USB_CHEP_ADDR - #define USB_CNTR_LPMODE USB_CNTR_SUSPRDY - #define USB_CNTR_FSUSP USB_CNTR_SUSPEN #else #error You are using an untested or unimplemented STM32 variant. Please update the driver. - // This includes U0 #endif //--------------------------------------------------------------------+ @@ -255,16 +138,6 @@ #endif #endif -// This checks if the device has "LPM" -#if defined(USB_ISTR_L1REQ) -#define USB_ISTR_L1REQ_FORCED (USB_ISTR_L1REQ) -#else -#define USB_ISTR_L1REQ_FORCED ((uint16_t)0x0000U) -#endif - -#define USB_ISTR_ALL_EVENTS (USB_ISTR_PMAOVR | USB_ISTR_ERR | USB_ISTR_WKUP | USB_ISTR_SUSP | \ - USB_ISTR_RESET | USB_ISTR_SOF | USB_ISTR_ESOF | USB_ISTR_L1REQ_FORCED ) - #ifndef FSDEV_HAS_SBUF_ISO #error "FSDEV_HAS_SBUF_ISO not defined" #endif @@ -274,7 +147,7 @@ // - Enable double buffering on devices with >1KB Packet Memory Area (PMA) // to improve isochronous transfer reliability and performance // - Disable on devices with limited PMA to conserve memory space - #if FSDEV_PMA_SIZE > 1024u + #if CFG_TUSB_FSDEV_PMA_SIZE > 1024u #define CFG_TUD_FSDEV_DOUBLE_BUFFERED_ISO_EP 1 #else #define CFG_TUD_FSDEV_DOUBLE_BUFFERED_ISO_EP 0 @@ -295,56 +168,53 @@ #define FSDEV_USE_SBUF_ISO 0 #endif -//--------------------------------------------------------------------+ -// -//--------------------------------------------------------------------+ - +// STM32L1 calls it USB_FS_WKUP_IRQn; alias so the commented USBWakeUp_IRQn below +// can be uncommented as-is. #if TU_CHECK_MCU(OPT_MCU_STM32L1) && !defined(USBWakeUp_IRQn) #define USBWakeUp_IRQn USB_FS_WKUP_IRQn #endif +// USB interrupt vectors to enable in NVIC. The EXTI-line USB wakeup interrupt +// (USBWakeUp_IRQn, and USBWakeUp_RMP_IRQn on F3) is left commented out: resume is +// handled in-band via ISTR.WKUP in the USB_LP/HP ISR; the EXTI line is only needed to +// wake the core from STOP mode, which this driver does not implement (it never arms or +// clears that EXTI line, so enabling its NVIC vector can only spuriously fire/freeze). +// TODO: uncomment USBWakeUp_IRQn (+ arm/clear its EXTI line) when adding STOP-mode wakeup. static const IRQn_Type fsdev_irq[] = { - #if TU_CHECK_MCU(OPT_MCU_STM32F0, OPT_MCU_STM32L0, OPT_MCU_STM32L4) + #if TU_CHECK_MCU(OPT_MCU_STM32F0, OPT_MCU_STM32L0, OPT_MCU_STM32L4, OPT_MCU_STM32U5) USB_IRQn, - #elif CFG_TUSB_MCU == OPT_MCU_STM32F1 - USB_HP_CAN1_TX_IRQn, - USB_LP_CAN1_RX0_IRQn, - USBWakeUp_IRQn, - #elif CFG_TUSB_MCU == OPT_MCU_STM32F3 - // USB remap handles dcd functions - USB_HP_CAN_TX_IRQn, - USB_LP_CAN_RX0_IRQn, - USBWakeUp_IRQn, + #elif TU_CHECK_MCU(OPT_MCU_STM32L5, OPT_MCU_STM32U3) + USB_FS_IRQn, + #elif TU_CHECK_MCU(OPT_MCU_STM32C0, OPT_MCU_STM32C5, OPT_MCU_STM32H5, OPT_MCU_STM32U0) + USB_DRD_FS_IRQn, #elif CFG_TUSB_MCU == OPT_MCU_STM32G0 #ifdef STM32G0B0xx USB_IRQn, #else USB_UCPD1_2_IRQn, #endif - #elif CFG_TUSB_MCU == OPT_MCU_STM32C0 - USB_DRD_FS_IRQn, + #elif CFG_TUSB_MCU == OPT_MCU_STM32F1 + USB_HP_CAN1_TX_IRQn, + USB_LP_CAN1_RX0_IRQn, + //USBWakeUp_IRQn, + #elif CFG_TUSB_MCU == OPT_MCU_STM32F3 + USB_HP_CAN_TX_IRQn, + USB_LP_CAN_RX0_IRQn, + //USBWakeUp_IRQn, #elif TU_CHECK_MCU(OPT_MCU_STM32G4, OPT_MCU_STM32L1) USB_HP_IRQn, USB_LP_IRQn, - USBWakeUp_IRQn, - #elif CFG_TUSB_MCU == OPT_MCU_STM32H5 - USB_DRD_FS_IRQn, - #elif CFG_TUSB_MCU == OPT_MCU_STM32L5 - USB_FS_IRQn, + //USBWakeUp_IRQn, #elif CFG_TUSB_MCU == OPT_MCU_STM32WB USB_HP_IRQn, USB_LP_IRQn, - #elif CFG_TUSB_MCU == OPT_MCU_STM32U5 - USB_IRQn, - #elif CFG_TUSB_MCU == OPT_MCU_STM32U0 - USB_DRD_FS_IRQn, #else #error Unknown arch in USB driver #endif }; enum { FSDEV_IRQ_NUM = TU_ARRAY_SIZE(fsdev_irq) }; -void dcd_int_enable(uint8_t rhport) { +TU_ATTR_ALWAYS_INLINE static inline void fsdev_int_enable(uint8_t rhport) { (void)rhport; // forces write to RAM before allowing ISR to execute @@ -357,7 +227,7 @@ void dcd_int_enable(uint8_t rhport) { if (SYSCFG->CFGR1 & SYSCFG_CFGR1_USB_IT_RMP) { NVIC_EnableIRQ(USB_HP_IRQn); NVIC_EnableIRQ(USB_LP_IRQn); - NVIC_EnableIRQ(USBWakeUp_RMP_IRQn); + //NVIC_EnableIRQ(USBWakeUp_RMP_IRQn); } else #endif { @@ -367,7 +237,7 @@ void dcd_int_enable(uint8_t rhport) { } } -void dcd_int_disable(uint8_t rhport) { +TU_ATTR_ALWAYS_INLINE static inline void fsdev_int_disable(uint8_t rhport) { (void)rhport; #if CFG_TUSB_MCU == OPT_MCU_STM32F3 && defined(SYSCFG_CFGR1_USB_IT_RMP) @@ -377,7 +247,7 @@ void dcd_int_disable(uint8_t rhport) { if (SYSCFG->CFGR1 & SYSCFG_CFGR1_USB_IT_RMP) { NVIC_DisableIRQ(USB_HP_IRQn); NVIC_DisableIRQ(USB_LP_IRQn); - NVIC_DisableIRQ(USBWakeUp_RMP_IRQn); + //NVIC_DisableIRQ(USBWakeUp_RMP_IRQn); } else #endif { @@ -389,31 +259,91 @@ void dcd_int_disable(uint8_t rhport) { // CMSIS has a membar after disabling interrupts } -// Define only on MCU with internal pull-up. BSP can define on MCU without internal PU. +//--------------------------------------------------------------------+ +// STM32 FSDEV PMA Buffer Description Table errata workaround +//--------------------------------------------------------------------+ + +#ifdef CFG_TUSB_FSDEV_32BIT +/* Errata: Buffer description table update completes after CTR interrupt triggers + * https://www.st.com/resource/en/errata_sheet/es0561-stm32h503cbebkbrb-device-errata-stmicroelectronics.pdf + * https://www.st.com/resource/en/errata_sheet/es0587-stm32u535xx-and-stm32u545xx-device-errata-stmicroelectronics.pdf + * + * CTR may trigger before final PMA SRAM accesses complete on OUT transfers. + * Insert delay before reading PMA count/data. + * Max CPU frequency in Hz, used to derive conservative FSDEV PMA delay defaults. + */ +#if CFG_TUSB_MCU == OPT_MCU_STM32H5 + #define FSDEV_STM32_CPU_HZ 250000000U +#elif CFG_TUSB_MCU == OPT_MCU_STM32U5 + #define FSDEV_STM32_CPU_HZ 160000000U +#elif CFG_TUSB_MCU == OPT_MCU_STM32U3 + #define FSDEV_STM32_CPU_HZ 96000000U +#elif CFG_TUSB_MCU == OPT_MCU_STM32U0 + #define FSDEV_STM32_CPU_HZ 56000000U +#elif CFG_TUSB_MCU == OPT_MCU_STM32G0 + #define FSDEV_STM32_CPU_HZ 64000000U +#elif CFG_TUSB_MCU == OPT_MCU_STM32C0 + #define FSDEV_STM32_CPU_HZ 48000000U +#elif CFG_TUSB_MCU == OPT_MCU_STM32C5 + #define FSDEV_STM32_CPU_HZ 144000000U +#endif + +// 11 cycles / 800ns = ~13750000 cycles per second, used to derive conservative FSDEV PMA delay defaults +#ifndef CFG_TUSB_FSDEV_BTABLE_FS_DELAY_COUNT + #define CFG_TUSB_FSDEV_BTABLE_FS_DELAY_COUNT (FSDEV_STM32_CPU_HZ / 13750000U) +#endif + +// 11 cycles / 6.4us = ~1718750 cycles per second, used to derive conservative FSDEV PMA delay defaults +#ifndef CFG_TUSB_FSDEV_BTABLE_LS_DELAY_COUNT + #define CFG_TUSB_FSDEV_BTABLE_LS_DELAY_COUNT (FSDEV_STM32_CPU_HZ / 1718750U) +#endif + +/** + * LDR from SP-relative: 2 cycles + * SUBS: 1 cycle + * STR to SP-relative: 2 cycles + * LDR from SP-relative: 2 cycles + * CMP: 1 cycle + * BNE: + * taken: 3 cycles total (often shown as 1 + pipeline refill) + * not taken: 1 cycle + * Total cycles if delay is needed: 11 cycles + */ +TU_ATTR_ALWAYS_INLINE static inline void fsdev_btable_workaround_delay(bool low_speed) { + volatile uint32_t cycle_count = low_speed ? CFG_TUSB_FSDEV_BTABLE_LS_DELAY_COUNT : CFG_TUSB_FSDEV_BTABLE_FS_DELAY_COUNT; + while (cycle_count > 0U) { + cycle_count--; + } +} +#endif + +//--------------------------------------------------------------------+ +// Connect / Disconnect +//--------------------------------------------------------------------+ + #if defined(USB_BCDR_DPPU) -void dcd_disconnect(uint8_t rhport) { +TU_ATTR_ALWAYS_INLINE static inline void fsdev_disconnect(uint8_t rhport) { (void)rhport; - USB->BCDR &= ~(USB_BCDR_DPPU); + FSDEV_REG->BCDR &= ~U_BCDR_DPPU; } -void dcd_connect(uint8_t rhport) { +TU_ATTR_ALWAYS_INLINE static inline void fsdev_connect(uint8_t rhport) { (void)rhport; - USB->BCDR |= USB_BCDR_DPPU; + FSDEV_REG->BCDR |= U_BCDR_DPPU; } #elif defined(SYSCFG_PMC_USB_PU) // works e.g. on STM32L151 -void dcd_disconnect(uint8_t rhport) { +TU_ATTR_ALWAYS_INLINE static inline void fsdev_disconnect(uint8_t rhport) { (void)rhport; SYSCFG->PMC &= ~(SYSCFG_PMC_USB_PU); } -void dcd_connect(uint8_t rhport) { +TU_ATTR_ALWAYS_INLINE static inline void fsdev_connect(uint8_t rhport) { (void)rhport; SYSCFG->PMC |= SYSCFG_PMC_USB_PU; } #endif - #endif /* TUSB_FSDEV_STM32_H */ diff --git a/src/portable/st/stm32_fsdev/fsdev_type.h b/src/portable/st/stm32_fsdev/fsdev_type.h deleted file mode 100644 index cf36576bb..000000000 --- a/src/portable/st/stm32_fsdev/fsdev_type.h +++ /dev/null @@ -1,307 +0,0 @@ -/* - * The MIT License (MIT) - * - * Copyright(c) N Conrad - * Copyright(c) 2024, hathach (tinyusb.org) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * This file is part of the TinyUSB stack. - */ - -#ifndef TUSB_FSDEV_TYPE_H -#define TUSB_FSDEV_TYPE_H - -#ifdef __cplusplus - extern "C" { -#endif - -#include "stdint.h" - -// If sharing with CAN, one can set this to be non-zero to give CAN space where it wants it -// Both of these MUST be a multiple of 2, and are in byte units. -#ifndef FSDEV_BTABLE_BASE -#define FSDEV_BTABLE_BASE 0U -#endif - -TU_VERIFY_STATIC(FSDEV_BTABLE_BASE % 8 == 0, "BTABLE base must be aligned to 8 bytes"); - -// FSDEV_PMA_SIZE is PMA buffer size in bytes. -// - 512-byte devices, access with a stride of two words (use every other 16-bit address) -// - 1024-byte devices, access with a stride of one word (use every 16-bit address) -// - 2048-byte devices, access with 32-bit address - -// For purposes of accessing the packet -#if FSDEV_PMA_SIZE == 512 - // 1x16 bit / word access scheme - #define FSDEV_PMA_STRIDE 2 - #define pma_access_scheme TU_ATTR_ALIGNED(4) -#elif FSDEV_PMA_SIZE == 1024 - // 2x16 bit / word access scheme - #define FSDEV_PMA_STRIDE 1 - #define pma_access_scheme -#elif FSDEV_PMA_SIZE == 2048 - // 32 bit access scheme - #define FSDEV_BUS_32BIT - #define FSDEV_PMA_STRIDE 1 - #define pma_access_scheme -#endif - -// The fsdev_bus_t type can be used for both register and PMA access necessities -#ifdef FSDEV_BUS_32BIT - typedef uint32_t fsdev_bus_t; - #define fsdevbus_unaligned_read(_addr) tu_unaligned_read32(_addr) - #define fsdevbus_unaligned_write(_addr, _value) tu_unaligned_write32(_addr, _value) -#else - typedef uint16_t fsdev_bus_t; - #define fsdevbus_unaligned_read(_addr) tu_unaligned_read16(_addr) - #define fsdevbus_unaligned_write(_addr, _value) tu_unaligned_write16(_addr, _value) -#endif - -enum { - FSDEV_BUS_SIZE = sizeof(fsdev_bus_t), -}; - -//--------------------------------------------------------------------+ -// BTable Typedef -//--------------------------------------------------------------------+ -enum { - BTABLE_BUF_TX = 0, - BTABLE_BUF_RX = 1 -}; - -// hardware limit endpoint -#define FSDEV_EP_COUNT 8 - -// Buffer Table is located in Packet Memory Area (PMA) and therefore its address access is forced to either -// 16-bit or 32-bit depending on FSDEV_BUS_32BIT. -// 0: TX (IN), 1: RX (OUT) -typedef union { - // data is strictly 16-bit access (address could be 32-bit aligned) - struct { - volatile pma_access_scheme uint16_t addr; - volatile pma_access_scheme uint16_t count; - } ep16[FSDEV_EP_COUNT][2]; - - // strictly 32-bit access - struct { - volatile uint32_t count_addr; - } ep32[FSDEV_EP_COUNT][2]; -} fsdev_btable_t; - -TU_VERIFY_STATIC(sizeof(fsdev_btable_t) == FSDEV_EP_COUNT*8*FSDEV_PMA_STRIDE, "size is not correct"); -TU_VERIFY_STATIC(FSDEV_BTABLE_BASE + FSDEV_EP_COUNT*8 <= FSDEV_PMA_SIZE, "BTABLE does not fit in PMA RAM"); - -#define FSDEV_BTABLE ((volatile fsdev_btable_t*) (FSDEV_PMA_BASE + FSDEV_PMA_STRIDE*(FSDEV_BTABLE_BASE))) - -typedef struct { - volatile pma_access_scheme fsdev_bus_t value; -} fsdev_pma_buf_t; - -#define PMA_BUF_AT(_addr) ((fsdev_pma_buf_t*) (FSDEV_PMA_BASE + FSDEV_PMA_STRIDE*(_addr))) - -//--------------------------------------------------------------------+ -// Registers Typedef -//--------------------------------------------------------------------+ - -// volatile 32-bit aligned -#define _va32 volatile TU_ATTR_ALIGNED(4) - -typedef struct { - struct { - _va32 fsdev_bus_t reg; - }ep[FSDEV_EP_COUNT]; - - _va32 uint32_t RESERVED7[8]; // Reserved - _va32 fsdev_bus_t CNTR; // 40: Control register - _va32 fsdev_bus_t ISTR; // 44: Interrupt status register - _va32 fsdev_bus_t FNR; // 48: Frame number register - _va32 fsdev_bus_t DADDR; // 4C: Device address register - _va32 fsdev_bus_t BTABLE; // 50: Buffer Table address register (16-bit only) - _va32 fsdev_bus_t LPMCSR; // 54: LPM Control and Status Register (32-bit only) - _va32 fsdev_bus_t BCDR; // 58: Battery Charging Detector Register (32-bit only) -} fsdev_regs_t; - -TU_VERIFY_STATIC(offsetof(fsdev_regs_t, CNTR) == 0x40, "Wrong offset"); -TU_VERIFY_STATIC(sizeof(fsdev_regs_t) == 0x5C, "Size is not correct"); - -#define FSDEV_REG ((fsdev_regs_t*) FSDEV_REG_BASE) - - -#ifndef USB_EPTX_STAT -#define USB_EPTX_STAT 0x0030U -#endif - -#ifndef USB_EPRX_STAT -#define USB_EPRX_STAT 0x3000U -#endif - -#ifndef USB_EPTX_STAT_Pos -#define USB_EPTX_STAT_Pos 4u -#endif - -#ifndef USB_EP_DTOG_TX_Pos -#define USB_EP_DTOG_TX_Pos 6u -#endif - -#ifndef USB_EP_CTR_TX_Pos -#define USB_EP_CTR_TX_Pos 7u -#endif - -typedef enum { - EP_STAT_DISABLED = 0, - EP_STAT_STALL = 1, - EP_STAT_NAK = 2, - EP_STAT_VALID = 3 -}ep_stat_t; - -#define EP_STAT_MASK(_dir) (3u << (USB_EPTX_STAT_Pos + ((_dir) == TUSB_DIR_IN ? 0 : 8))) -#define EP_DTOG_MASK(_dir) (1u << (USB_EP_DTOG_TX_Pos + ((_dir) == TUSB_DIR_IN ? 0 : 8))) - -//--------------------------------------------------------------------+ -// Endpoint Helper -// - CTR is write 0 to clear -// - DTOG and STAT are write 1 to toggle -//--------------------------------------------------------------------+ - -TU_ATTR_ALWAYS_INLINE static inline uint32_t ep_read(uint32_t ep_id) { - return FSDEV_REG->ep[ep_id].reg; -} - -TU_ATTR_ALWAYS_INLINE static inline void ep_write(uint32_t ep_id, uint32_t value, bool need_exclusive) { - if (need_exclusive) { - dcd_int_disable(0); - } - - FSDEV_REG->ep[ep_id].reg = (fsdev_bus_t) value; - - if (need_exclusive) { - dcd_int_enable(0); - } -} - -TU_ATTR_ALWAYS_INLINE static inline void ep_write_clear_ctr(uint32_t ep_id, tusb_dir_t dir) { - uint32_t reg = FSDEV_REG->ep[ep_id].reg; - reg |= USB_EP_CTR_TX | USB_EP_CTR_RX; - reg &= USB_EPREG_MASK; - reg &= ~(1 << (USB_EP_CTR_TX_Pos + (dir == TUSB_DIR_IN ? 0 : 8))); - ep_write(ep_id, reg, false); -} - -TU_ATTR_ALWAYS_INLINE static inline void ep_change_status(uint32_t* reg, tusb_dir_t dir, ep_stat_t state) { - *reg ^= (state << (USB_EPTX_STAT_Pos + (dir == TUSB_DIR_IN ? 0 : 8))); -} - -TU_ATTR_ALWAYS_INLINE static inline void ep_change_dtog(uint32_t* reg, tusb_dir_t dir, uint8_t state) { - *reg ^= (state << (USB_EP_DTOG_TX_Pos + (dir == TUSB_DIR_IN ? 0 : 8))); -} - -TU_ATTR_ALWAYS_INLINE static inline bool ep_is_iso(uint32_t reg) { - return (reg & USB_EP_TYPE_MASK) == USB_EP_ISOCHRONOUS; -} - -//--------------------------------------------------------------------+ -// BTable Helper -//--------------------------------------------------------------------+ - -TU_ATTR_ALWAYS_INLINE static inline uint32_t btable_get_addr(uint32_t ep_id, uint8_t buf_id) { -#ifdef FSDEV_BUS_32BIT - return FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr & 0x0000FFFFu; -#else - return FSDEV_BTABLE->ep16[ep_id][buf_id].addr; -#endif -} - -TU_ATTR_ALWAYS_INLINE static inline void btable_set_addr(uint32_t ep_id, uint8_t buf_id, uint16_t addr) { -#ifdef FSDEV_BUS_32BIT - uint32_t count_addr = FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr; - count_addr = (count_addr & 0xFFFF0000u) | (addr & 0x0000FFFCu); - FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr = count_addr; -#else - FSDEV_BTABLE->ep16[ep_id][buf_id].addr = addr; -#endif -} - -TU_ATTR_ALWAYS_INLINE static inline uint16_t btable_get_count(uint32_t ep_id, uint8_t buf_id) { - uint16_t count; -#ifdef FSDEV_BUS_32BIT - count = (FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr >> 16); -#else - count = FSDEV_BTABLE->ep16[ep_id][buf_id].count; -#endif - return count & 0x3FFU; -} - -TU_ATTR_ALWAYS_INLINE static inline void btable_set_count(uint32_t ep_id, uint8_t buf_id, uint16_t byte_count) { -#ifdef FSDEV_BUS_32BIT - uint32_t count_addr = FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr; - count_addr = (count_addr & ~0x03FF0000u) | ((byte_count & 0x3FFu) << 16); - FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr = count_addr; -#else - uint16_t cnt = FSDEV_BTABLE->ep16[ep_id][buf_id].count; - cnt = (cnt & ~0x3FFU) | (byte_count & 0x3FFU); - FSDEV_BTABLE->ep16[ep_id][buf_id].count = cnt; -#endif -} - -/* Aligned buffer size according to hardware */ -TU_ATTR_ALWAYS_INLINE static inline uint16_t pma_align_buffer_size(uint16_t size, uint8_t* blsize, uint8_t* num_block) { - /* The STM32 full speed USB peripheral supports only a limited set of - * buffer sizes given by the RX buffer entry format in the USB_BTABLE. */ - uint16_t block_in_bytes; - if (size > 62) { - block_in_bytes = 32; - *blsize = 1; - *num_block = tu_div_ceil(size, 32); - } else { - block_in_bytes = 2; - *blsize = 0; - *num_block = tu_div_ceil(size, 2); - } - - return (*num_block) * block_in_bytes; -} - -TU_ATTR_ALWAYS_INLINE static inline void btable_set_rx_bufsize(uint32_t ep_id, uint8_t buf_id, uint16_t wCount) { - uint8_t blsize, num_block; - (void) pma_align_buffer_size(wCount, &blsize, &num_block); - - /* Encode into register. When BLSIZE==1, we need to subtract 1 block count */ - uint16_t bl_nb = (blsize << 15) | ((num_block - blsize) << 10); - if (bl_nb == 0) { - // zlp but 0 is invalid value, set blsize to 1 (32 bytes) - // Note: lower value can cause PMAOVR on setup with ch32v203 - bl_nb = 1 << 15; - } - -#ifdef FSDEV_BUS_32BIT - uint32_t count_addr = FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr; - count_addr = (bl_nb << 16) | (count_addr & 0x0000FFFFu); - FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr = count_addr; -#else - FSDEV_BTABLE->ep16[ep_id][buf_id].count = bl_nb; -#endif - -} - -#ifdef __cplusplus - } -#endif - -#endif diff --git a/src/portable/st/stm32_fsdev/hcd_stm32_fsdev.c b/src/portable/st/stm32_fsdev/hcd_stm32_fsdev.c new file mode 100644 index 000000000..f9201651a --- /dev/null +++ b/src/portable/st/stm32_fsdev/hcd_stm32_fsdev.c @@ -0,0 +1,835 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2025 HiFiPhile (Zixun LI) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file is part of the TinyUSB stack. + */ + +/********************************************** + * This driver provides USB Host controller support for STM32 MCUs with "USB A"/"PCD"/"HCD" peripheral. + * This covers these MCU families: + * + * C0 2048 byte buffer; 32-bit bus; host mode + * G0 2048 byte buffer; 32-bit bus; host mode + * U3 2048 byte buffer; 32-bit bus; host mode + * H5 2048 byte buffer; 32-bit bus; host mode + * U535, U545 2048 byte buffer; 32-bit bus; host mode + * + */ + +#include "tusb_option.h" + +#if CFG_TUH_ENABLED && defined(TUP_USBIP_FSDEV) && defined(TUP_USBIP_FSDEV_DRD) + +#include "host/hcd.h" +#include "host/usbh.h" +#include "fsdev_common.h" + +//--------------------------------------------------------------------+ +// MACRO CONSTANT TYPEDEF +//--------------------------------------------------------------------+ + +// Debug level for FSDEV +#define FSDEV_DEBUG 3 + +// Max number of endpoints application can open, can be larger than FSDEV_EP_COUNT +#ifndef CFG_TUH_FSDEV_ENDPOINT_MAX + #define CFG_TUH_FSDEV_ENDPOINT_MAX 16u +#endif + +TU_VERIFY_STATIC(CFG_TUH_FSDEV_ENDPOINT_MAX <= 255, "currently only use 8-bit for index"); + +enum { + HCD_XFER_ERROR_MAX = 3, + HCD_XFER_NAK_MAX = 15, + HCD_XFER_NAK_DEFAULT = 3, +}; + +// Host driver struct for each opened endpoint +typedef struct { + uint8_t *buffer; + uint16_t buflen; + uint16_t queued_len; + uint16_t max_packet_size; + uint8_t dev_addr; + uint8_t ep_addr; + uint8_t ep_type; + uint8_t interval; + struct TU_ATTR_PACKED { + uint8_t ls_pre : 1; + uint8_t allocated : 1; + uint8_t next_setup : 1; + uint8_t pid : 1; + }; +} hcd_endpoint_t; + +// Channel direction state +typedef struct { + hcd_endpoint_t* edpt; + struct TU_ATTR_PACKED { + uint8_t allocated : 1; + uint8_t retry : 3; + uint8_t nak : 4; // Max NAK count in current frame + }; +} hcd_channel_dir_t; + +// Additional info for each channel when it is active +typedef struct { + uint8_t dev_addr; + uint8_t ep_num; + uint8_t ep_type; + hcd_channel_dir_t out, in; +} hcd_channel_t; + +static struct { + hcd_channel_t channel[FSDEV_EP_COUNT]; + hcd_endpoint_t edpt[CFG_TUH_FSDEV_ENDPOINT_MAX]; + bool connected; +} _hcd_data; + +static tuh_configure_fsdev_t _tuh_cfg = { + .max_nak = HCD_XFER_NAK_DEFAULT, +}; + +//--------------------------------------------------------------------+ +// Prototypes +//--------------------------------------------------------------------+ + +static uint8_t endpoint_alloc(void); +static uint8_t endpoint_find(uint8_t dev_addr, uint8_t ep_addr); +static uint32_t hcd_pma_alloc(uint8_t channel, tusb_dir_t dir, uint16_t len); +static uint8_t channel_alloc(uint8_t dev_addr, uint8_t ep_addr, uint8_t ep_type); +static bool edpt_xfer_kickoff(uint8_t ep_id); +static bool channel_xfer_start(uint8_t ch_id, tusb_dir_t dir); +static void edpoint_close(uint8_t ep_id); +static void port_status_handler(uint8_t rhport, bool in_isr); +static void ch_handle_ack(uint8_t ch_id, uint32_t ch_reg, tusb_dir_t dir); +static void ch_handle_nak(uint8_t ch_id, uint32_t ch_reg, tusb_dir_t dir); +static void ch_handle_stall(uint8_t ch_id, uint32_t ch_reg, tusb_dir_t dir); +static void ch_handle_error(uint8_t ch_id, uint32_t ch_reg, tusb_dir_t dir); + +//--------------------------------------------------------------------+ +// Inline Functions +//--------------------------------------------------------------------+ + +static inline void endpoint_dealloc(hcd_endpoint_t* edpt) { + edpt->allocated = 0; +} + +static inline void channel_dealloc(hcd_channel_t* ch, tusb_dir_t dir) { + if (dir == TUSB_DIR_OUT) { + ch->out.allocated = 0; + } else { + ch->in.allocated = 0; + } +} + +// Write channel state in specified direction +static inline void channel_write_status(uint8_t ch_id, uint32_t ch_reg, tusb_dir_t dir, ep_stat_t state, bool need_exclusive) { + ch_reg &= U_EPREG_MASK | CH_STAT_MASK(dir); + ch_change_status(&ch_reg, dir, state); + ch_write(ch_id, ch_reg, need_exclusive); +} + +static inline uint16_t channel_get_rx_count(uint8_t ch_id) { + uint32_t ch_reg = ch_read(ch_id); + const bool is_low_speed = (FSDEV_REG->ISTR & U_ISTR_LS_DCONN) || (ch_reg & U_EP_LSEP); + fsdev_btable_workaround_delay(is_low_speed); + + return btable_get_count(ch_id, BTABLE_BUF_RX); +} + +//--------------------------------------------------------------------+ +// Controller API +//--------------------------------------------------------------------+ + +// Optional HCD configuration, called by tuh_configure() +bool hcd_configure(uint8_t rhport, uint32_t cfg_id, const void* cfg_param) { + (void) rhport; + TU_VERIFY(cfg_id == TUH_CFGID_FSDEV && cfg_param != NULL); + + tuh_configure_param_t const* cfg = (tuh_configure_param_t const*) cfg_param; + _tuh_cfg.max_nak = tu_min8(cfg->fsdev.max_nak, HCD_XFER_NAK_MAX); + return true; +} + +// Initialize controller to host mode +bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { + (void) rh_init; + + fsdev_core_reset(); + + FSDEV_REG->CNTR = U_CNTR_HOST; // Enable USB in Host mode + + tu_memclr(&_hcd_data, sizeof(_hcd_data)); + + // Clear pending interrupts + // Normally no interrupts should be pending here since we just reset the core, + // but device mode suspend needs to cleared by WKUP flag + FSDEV_REG->ISTR = 0; + + // Enable interrupts for host mode + FSDEV_REG->CNTR |= U_CNTR_DCON | U_CNTR_CTRM | U_CNTR_SOFM | U_CNTR_ERRM | U_CNTR_PMAOVRM; + + // Initialize port state + _hcd_data.connected = false; + + fsdev_connect(rhport); + + // If DCON_STAT is already set, the controller sometimes misses the initial connection interrupt + if (FSDEV_REG->ISTR & U_ISTR_DCON_STAT) { + tusb_time_delay_ms_api(2); + port_status_handler(rhport, false); + } + + return true; +} + +bool hcd_deinit(uint8_t rhport) { + (void)rhport; + + fsdev_disconnect(rhport); + + fsdev_deinit(); + + return true; +} + +//--------------------------------------------------------------------+ +// Interrupt Helper Functions +//--------------------------------------------------------------------+ + +static inline void sof_handler(void) { + // Reset NAK counters for all active channels + for (uint8_t ch_id = 0; ch_id < FSDEV_EP_COUNT; ch_id++) { + hcd_channel_t* channel = &_hcd_data.channel[ch_id]; + if (channel->out.allocated) { + channel->out.nak = 0; + } + if (channel->in.allocated) { + channel->in.nak = 0; + } + } +} + +static void port_status_handler(uint8_t rhport, bool in_isr) { + uint32_t const fnr_reg = FSDEV_REG->FNR; + uint32_t const istr_reg = FSDEV_REG->ISTR; + // SE0 detected USB Disconnected state + if ((fnr_reg & (U_FNR_RXDP | U_FNR_RXDM)) == 0U) { + _hcd_data.connected = false; + hcd_event_device_remove(rhport, in_isr); + return; + } + + if (!_hcd_data.connected) { + // J-state or K-state detected & LastState=Disconnected + if (((fnr_reg & U_FNR_RXDP) != 0U) || ((istr_reg & U_ISTR_LS_DCONN) != 0U)) { + _hcd_data.connected = true; + hcd_event_device_attach(rhport, in_isr); + } + } else { + // J-state or K-state detected & lastState=Connected: a Missed disconnection is detected + if (((fnr_reg & U_FNR_RXDP) != 0U) || ((istr_reg & U_ISTR_LS_DCONN) != 0U)) { + _hcd_data.connected = false; + hcd_event_device_remove(rhport, in_isr); + } + } +} + +// Handle ACK response +static void ch_handle_ack(uint8_t ch_id, uint32_t ch_reg, tusb_dir_t dir) { + uint8_t const ep_num = ch_reg & U_EPADDR_FIELD; + uint8_t const daddr = (ch_reg & U_EP_DEVADDR) >> U_EP_DEVADDR_Pos; + + uint8_t ep_id = endpoint_find(daddr, ep_num | (dir == TUSB_DIR_IN ? TUSB_DIR_IN_MASK : 0)); + if (ep_id == TUSB_INDEX_INVALID_8) { + return; + } + + hcd_endpoint_t *edpt = &_hcd_data.edpt[ep_id]; + hcd_channel_t *channel = &_hcd_data.channel[ch_id]; + + if (dir == TUSB_DIR_OUT) { + // OUT/TX direction + if (edpt->buflen != edpt->queued_len) { + // More data to send + uint16_t const len = tu_min16(edpt->buflen - edpt->queued_len, edpt->max_packet_size); + uint16_t pma_addr = (uint16_t) btable_get_addr(ch_id, BTABLE_BUF_TX); + tu_hwfifo_write(PMA_BUF_AT(pma_addr), &(edpt->buffer[edpt->queued_len]), len, NULL); + btable_set_count(ch_id, BTABLE_BUF_TX, len); + edpt->queued_len += len; + channel_write_status(ch_id, ch_reg, TUSB_DIR_OUT, EP_STAT_VALID, false); + channel->out.nak = 0; + } else { + // Transfer complete + channel_dealloc(channel, TUSB_DIR_OUT); + edpt->pid = (ch_reg & U_EP_DTOG_TX) ? 1 : 0; + hcd_event_xfer_complete(daddr, ep_num, edpt->queued_len, XFER_RESULT_SUCCESS, true); + } + } else { + // IN/RX direction + uint16_t const rx_count = channel_get_rx_count(ch_id); + uint16_t pma_addr = (uint16_t) btable_get_addr(ch_id, BTABLE_BUF_RX); + tu_hwfifo_read(PMA_BUF_AT(pma_addr), edpt->buffer + edpt->queued_len, rx_count, NULL); + edpt->queued_len += rx_count; + + if ((rx_count < edpt->max_packet_size) || (edpt->queued_len >= edpt->buflen)) { + // Transfer complete (short packet or all bytes received) + channel_dealloc(channel, TUSB_DIR_IN); + edpt->pid = (ch_reg & U_EP_DTOG_RX) ? 1 : 0; + hcd_event_xfer_complete(daddr, ep_num | TUSB_DIR_IN_MASK, edpt->queued_len, XFER_RESULT_SUCCESS, true); + } else { + // More data expected + uint16_t const cnt = tu_min16(edpt->buflen - edpt->queued_len, edpt->max_packet_size); + btable_set_rx_bufsize(ch_id, BTABLE_BUF_RX, cnt); + channel_write_status(ch_id, ch_reg, TUSB_DIR_IN, EP_STAT_VALID, false); + channel->in.nak = 0; + } + } +} + +// Handle NAK response +static void ch_handle_nak(uint8_t ch_id, uint32_t ch_reg, tusb_dir_t dir) { + uint8_t const ep_num = ch_reg & U_EPADDR_FIELD; + uint8_t const daddr = (ch_reg & U_EP_DEVADDR) >> U_EP_DEVADDR_Pos; + + uint8_t ep_id = endpoint_find(daddr, ep_num | (dir == TUSB_DIR_IN ? TUSB_DIR_IN_MASK : 0)); + if (ep_id == TUSB_INDEX_INVALID_8) return; + + hcd_endpoint_t* edpt = &_hcd_data.edpt[ep_id]; + // Retry non-periodic transfer immediately if NAK count not exceeded + // Periodic transfer will be retried by next frame automatically + if (edpt->ep_type == TUSB_XFER_CONTROL || edpt->ep_type == TUSB_XFER_BULK) { + hcd_channel_dir_t* channel_dir = + (dir == TUSB_DIR_OUT) ? &(_hcd_data.channel[ch_id].out) : &(_hcd_data.channel[ch_id].in); + if (channel_dir->nak < HCD_XFER_NAK_MAX) { + channel_dir->nak++; + } + if (channel_dir->nak < _tuh_cfg.max_nak || _tuh_cfg.max_nak == 0) { + channel_write_status(ch_id, ch_reg, dir, EP_STAT_VALID, false); + } + } +} + +// Handle STALL response +static void ch_handle_stall(uint8_t ch_id, uint32_t ch_reg, tusb_dir_t dir) { + uint8_t const ep_num = ch_reg & U_EPADDR_FIELD; + uint8_t const daddr = (ch_reg & U_EP_DEVADDR) >> U_EP_DEVADDR_Pos; + + uint8_t ep_id = endpoint_find(daddr, ep_num | (dir == TUSB_DIR_IN ? TUSB_DIR_IN_MASK : 0)); + if (ep_id == TUSB_INDEX_INVALID_8) return; + + hcd_endpoint_t* edpt = &_hcd_data.edpt[ep_id]; + hcd_channel_t* channel = &_hcd_data.channel[ch_id]; + channel_dealloc(channel, dir); + + channel_write_status(ch_id, ch_reg, dir, EP_STAT_DISABLED, false); + + hcd_event_xfer_complete(daddr, ep_num | (dir == TUSB_DIR_IN ? TUSB_DIR_IN_MASK : 0), + edpt->queued_len, XFER_RESULT_STALLED, true); +} + +// Handle error response +static void ch_handle_error(uint8_t ch_id, uint32_t ch_reg, tusb_dir_t dir) { + uint8_t const ep_num = ch_reg & U_EPADDR_FIELD; + uint8_t const daddr = (ch_reg & U_EP_DEVADDR) >> U_EP_DEVADDR_Pos; + + uint8_t ep_id = endpoint_find(daddr, ep_num | (dir == TUSB_DIR_IN ? TUSB_DIR_IN_MASK : 0)); + if (ep_id == TUSB_INDEX_INVALID_8) return; + + hcd_endpoint_t* edpt = &_hcd_data.edpt[ep_id]; + hcd_channel_t* channel = &_hcd_data.channel[ch_id]; + + ch_reg &= U_EPREG_MASK | CH_STAT_MASK(dir); + ch_reg &= ~(dir == TUSB_DIR_OUT ? U_EP_ERRTX : U_EP_ERRRX); + + hcd_channel_dir_t* channel_dir = + (dir == TUSB_DIR_OUT) ? &(_hcd_data.channel[ch_id].out) : &(_hcd_data.channel[ch_id].in); + if (channel_dir->retry < HCD_XFER_ERROR_MAX) { + // Retry + channel_dir->retry++; + ch_change_status(&ch_reg, dir, EP_STAT_VALID); + } else { + // Failed after retries + channel_dealloc(channel, dir); + ch_change_status(&ch_reg, dir, EP_STAT_DISABLED); + hcd_event_xfer_complete(daddr, ep_num | (dir == TUSB_DIR_IN ? TUSB_DIR_IN_MASK : 0), + edpt->queued_len, XFER_RESULT_FAILED, true); + } + ch_write(ch_id, ch_reg, false); +} + +// Handle CTR interrupt for the TX/OUT direction +static inline void handle_ctr_tx(uint32_t ch_id) { + uint32_t ch_reg = ch_read(ch_id) | U_EP_CTR_TX | U_EP_CTR_RX; + hcd_channel_t* channel = &_hcd_data.channel[ch_id]; + TU_VERIFY(channel->out.allocated == 1,); + + if ((ch_reg & U_EP_ERRTX) == 0U) { + // No error + if ((ch_reg & U_CH_TX_STTX) == U_CH_TX_ACK_SBUF) { + ch_handle_ack(ch_id, ch_reg, TUSB_DIR_OUT); + } else if ((ch_reg & U_CH_TX_STTX) == U_CH_TX_NAK) { + ch_handle_nak(ch_id, ch_reg, TUSB_DIR_OUT); + } else if ((ch_reg & U_CH_TX_STTX) == U_CH_TX_STALL) { + ch_handle_stall(ch_id, ch_reg, TUSB_DIR_OUT); + } + } else { + ch_handle_error(ch_id, ch_reg, TUSB_DIR_OUT); + } +} + +// Handle CTR interrupt for the RX/IN direction +static inline void handle_ctr_rx(uint32_t ch_id) { + uint32_t ch_reg = ch_read(ch_id) | U_EP_CTR_TX | U_EP_CTR_RX; + hcd_channel_t* channel = &_hcd_data.channel[ch_id]; + TU_VERIFY(channel->in.allocated == 1,); + + if ((ch_reg & U_EP_ERRRX) == 0U) { + // No error + if ((ch_reg & U_CH_RX_STRX) == U_CH_RX_ACK_SBUF) { + ch_handle_ack(ch_id, ch_reg, TUSB_DIR_IN); + } else if ((ch_reg & U_CH_RX_STRX) == U_CH_RX_NAK) { + ch_handle_nak(ch_id, ch_reg, TUSB_DIR_IN); + } else if ((ch_reg & U_CH_RX_STRX) == U_CH_RX_STALL){ + ch_handle_stall(ch_id, ch_reg, TUSB_DIR_IN); + } + } else { + ch_handle_error(ch_id, ch_reg, TUSB_DIR_IN); + } +} + +// Interrupt Handler +void hcd_int_handler(uint8_t rhport, bool in_isr) { + uint32_t int_status = FSDEV_REG->ISTR; + + // Start of Frame + if (int_status & U_ISTR_SOF) { + FSDEV_REG->ISTR = (fsdev_bus_t)~U_ISTR_SOF; + sof_handler(); + } + + // Port Change Detected (Connection/Disconnection) + if (int_status & U_ISTR_DCON) { + FSDEV_REG->ISTR = (fsdev_bus_t)~U_ISTR_DCON; + port_status_handler(rhport, in_isr); + } + + // Handle transfer complete (CTR) + while (FSDEV_REG->ISTR & U_ISTR_CTR) { + uint32_t const ch_id = FSDEV_REG->ISTR & U_ISTR_EP_ID; + uint32_t const ch_reg = ch_read(ch_id); + + if (ch_reg & U_EP_CTR_RX) { + ch_write_clear_ctr(ch_id, TUSB_DIR_IN); + handle_ctr_rx(ch_id); + } + + if (ch_reg & U_EP_CTR_TX) { + ch_write_clear_ctr(ch_id, TUSB_DIR_OUT); + handle_ctr_tx(ch_id); + } + } + + if (int_status & U_ISTR_ERR) { + FSDEV_REG->ISTR = (fsdev_bus_t)~U_ISTR_ERR; + // TODO: Handle error + } + + if (int_status & U_ISTR_PMAOVR) { + TU_BREAKPOINT(); + FSDEV_REG->ISTR = (fsdev_bus_t)~U_ISTR_PMAOVR; + } +} + +// Enable USB interrupt +void hcd_int_enable(uint8_t rhport) { + fsdev_int_enable(rhport); +} + +// Disable USB interrupt +void hcd_int_disable(uint8_t rhport) { + fsdev_int_disable(rhport); +} + +// Get frame number (1ms) +uint32_t hcd_frame_number(uint8_t rhport) { + (void) rhport; + return FSDEV_REG->FNR & U_FNR_FN; +} + +//--------------------------------------------------------------------+ +// Port API +//--------------------------------------------------------------------+ + +// Get the current connect status of roothub port +bool hcd_port_connect_status(uint8_t rhport) { + (void) rhport; + return _hcd_data.connected; +} + +// Reset USB bus on the port +void hcd_port_reset(uint8_t rhport) { + (void) rhport; + FSDEV_REG->CNTR |= U_CNTR_FRES; +} + +// Complete bus reset sequence +void hcd_port_reset_end(uint8_t rhport) { + (void) rhport; + FSDEV_REG->CNTR &= ~U_CNTR_FRES; +} + +// Get port link speed +tusb_speed_t hcd_port_speed_get(uint8_t rhport) { + (void) rhport; + if ((FSDEV_REG->ISTR & U_ISTR_LS_DCONN) != 0U) { + return TUSB_SPEED_LOW; + } else { + return TUSB_SPEED_FULL; + } +} + +// HCD closes all opened endpoints belonging to this device +void hcd_device_close(uint8_t rhport, uint8_t dev_addr) { + (void) rhport; + + // Close all endpoints for this device + for(uint32_t i = 0; i < CFG_TUH_FSDEV_ENDPOINT_MAX; i++) { + hcd_endpoint_t* edpt = &_hcd_data.edpt[i]; + if (edpt->allocated == 1 && edpt->dev_addr == dev_addr) { + edpoint_close(i); + } + } + +} + +//--------------------------------------------------------------------+ +// Endpoints API +//--------------------------------------------------------------------+ + +// Open an endpoint +bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const *ep_desc) { + (void) rhport; + + uint8_t const ep_addr = ep_desc->bEndpointAddress; + uint16_t const packet_size = tu_edpt_packet_size(ep_desc); + uint8_t const ep_type = ep_desc->bmAttributes.xfer; + + uint8_t const ep_id = endpoint_alloc(); + TU_ASSERT(ep_id != TUSB_INDEX_INVALID_8); + + hcd_endpoint_t* edpt = &_hcd_data.edpt[ep_id]; + edpt->dev_addr = dev_addr; + edpt->ep_addr = ep_addr; + edpt->ep_type = ep_type; + edpt->max_packet_size = packet_size; + edpt->interval = ep_desc->bInterval; + edpt->pid = 0; + edpt->ls_pre = (hcd_port_speed_get(rhport) == TUSB_SPEED_FULL && tuh_speed_get(dev_addr) == TUSB_SPEED_LOW) ? 1 : 0; + + return true; +} + +bool hcd_edpt_close(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) { + (void) rhport; + + uint8_t const ep_id = endpoint_find(dev_addr, ep_addr); + TU_ASSERT(ep_id != TUSB_INDEX_INVALID_8); + + edpoint_close(ep_id); + + return true; +} + +// Submit a transfer +bool hcd_edpt_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr, uint8_t *buffer, uint16_t buflen) { + (void) rhport; + + TU_LOG(FSDEV_DEBUG, "hcd_edpt_xfer addr=%u ep=0x%02X len=%u\r\n", dev_addr, ep_addr, buflen); + + uint8_t const ep_id = endpoint_find(dev_addr, ep_addr); + TU_ASSERT(ep_id != TUSB_INDEX_INVALID_8); + + hcd_endpoint_t *edpt = &_hcd_data.edpt[ep_id]; + + edpt->buffer = buffer; + edpt->buflen = buflen; + edpt->queued_len = 0; + + uint8_t const ep_num = tu_edpt_number(ep_addr); + if (ep_num == 0) { + // update ep_dir since control endpoint can switch direction + edpt->ep_addr = ep_addr; + } + + return edpt_xfer_kickoff(ep_id); +} + +// Abort a queued transfer +bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) { + (void) rhport; + + uint8_t const ep_id = endpoint_find(dev_addr, ep_addr); + TU_ASSERT(ep_id != TUSB_INDEX_INVALID_8); + tusb_dir_t const dir = tu_edpt_dir(ep_addr); + + for (uint8_t i = 0; i < FSDEV_EP_COUNT; i++) { + hcd_channel_t* channel = &_hcd_data.channel[i]; + uint8_t const allocated = (dir == TUSB_DIR_OUT) ? channel->out.allocated : channel->in.allocated; + + if (allocated == 1 && + channel->dev_addr == dev_addr && + channel->ep_num == tu_edpt_number(ep_addr)) { + channel_dealloc(channel, dir); + uint32_t ch_reg = ch_read(i) | U_EP_CTR_TX | U_EP_CTR_RX; + channel_write_status(i, ch_reg, dir, EP_STAT_DISABLED, true); + } + } + + return true; +} + +// Submit a special transfer to send 8-byte Setup Packet +bool hcd_setup_send(uint8_t rhport, uint8_t dev_addr, uint8_t const setup_packet[8]) { + (void) rhport; + + uint8_t const ep_id = endpoint_find(dev_addr, 0); + TU_ASSERT(ep_id != TUSB_INDEX_INVALID_8); + + hcd_endpoint_t *edpt = &_hcd_data.edpt[ep_id]; + edpt->next_setup = true; + edpt->pid = 0; + + return hcd_edpt_xfer(rhport, dev_addr, 0, (uint8_t*)(uintptr_t) setup_packet, 8); +} + +// Clear stall, data toggle is also reset to DATA0 +bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) { + (void) rhport; + + uint8_t const ep_id = endpoint_find(dev_addr, ep_addr); + TU_ASSERT(ep_id != TUSB_INDEX_INVALID_8); + + hcd_endpoint_t *edpt = &_hcd_data.edpt[ep_id]; + edpt->pid = 0; + + return true; +} + +//--------------------------------------------------------------------+ +// Helper Functions +//--------------------------------------------------------------------+ + +static uint8_t endpoint_alloc(void) { + for (uint32_t i = 0; i < CFG_TUH_FSDEV_ENDPOINT_MAX; i++) { + hcd_endpoint_t* edpt = &_hcd_data.edpt[i]; + if (edpt->allocated == 0) { + edpt->allocated = 1; + return i; + } + } + return TUSB_INDEX_INVALID_8; +} + +static uint8_t endpoint_find(uint8_t dev_addr, uint8_t ep_addr) { + uint8_t const ep_num = tu_edpt_number(ep_addr); + tusb_dir_t const ep_dir = tu_edpt_dir(ep_addr); + + for (uint32_t i = 0; i < (uint32_t)CFG_TUH_FSDEV_ENDPOINT_MAX; i++) { + hcd_endpoint_t* edpt = &_hcd_data.edpt[i]; + tusb_dir_t const dir = tu_edpt_dir(edpt->ep_addr); + uint8_t const num = tu_edpt_number(edpt->ep_addr); + // Match both ep_num and ep_dir, or match ep_num 0 (control endpoint) + if (edpt->allocated == 1 && edpt->dev_addr == dev_addr && num == ep_num && + (dir == ep_dir || ep_num == 0)) { + return i; + } + } + return TUSB_INDEX_INVALID_8; +} + +// close an opened endpoint +static void edpoint_close(uint8_t ep_id) { + hcd_endpoint_t* edpt = &_hcd_data.edpt[ep_id]; + endpoint_dealloc(edpt); + + // disable active channel belong to this endpoint + for (uint8_t i = 0; i < FSDEV_EP_COUNT; i++) { + hcd_channel_t* channel = &_hcd_data.channel[i]; + uint32_t ch_reg = ch_read(i) | U_EP_CTR_TX | U_EP_CTR_RX; + if (channel->out.allocated == 1 && channel->out.edpt == edpt) { + channel_dealloc(channel, TUSB_DIR_OUT); + channel_write_status(i, ch_reg, TUSB_DIR_OUT, EP_STAT_DISABLED, true); + } + if (channel->in.allocated == 1 && channel->in.edpt == edpt) { + channel_dealloc(channel, TUSB_DIR_IN); + channel_write_status(i, ch_reg, TUSB_DIR_IN, EP_STAT_DISABLED, true); + } + } +} + +// Allocate PMA buffer +static uint32_t hcd_pma_alloc(uint8_t channel, tusb_dir_t dir, uint16_t len) { + (void) len; + // Simple static allocation as we are unlikely to handle ISO endpoints in host mode + // We just give each channel two buffers of max packet size (64 bytes) for IN and OUT + + uint16_t addr = FSDEV_BTABLE_BASE + 8 * FSDEV_EP_COUNT; + addr += channel * TUSB_EPSIZE_BULK_FS * 2 + (dir == TUSB_DIR_IN ? TUSB_EPSIZE_BULK_FS : 0); + + TU_ASSERT(addr <= CFG_TUSB_FSDEV_PMA_SIZE, 0xFFFF); + + return addr; +} + +// Allocate hardware channel +static uint8_t channel_alloc(uint8_t dev_addr, uint8_t ep_addr, uint8_t ep_type) { + uint8_t const ep_num = tu_edpt_number(ep_addr); + tusb_dir_t const dir = tu_edpt_dir(ep_addr); + + // Find channel allocate for same ep_num but other direction + tusb_dir_t const other_dir = (dir == TUSB_DIR_IN) ? TUSB_DIR_OUT : TUSB_DIR_IN; + for (uint8_t i = 0; i < FSDEV_EP_COUNT; i++) { + uint8_t const allocated_dir = (dir == TUSB_DIR_OUT) ? _hcd_data.channel[i].out.allocated : _hcd_data.channel[i].in.allocated; + uint8_t const allocated_other = (other_dir == TUSB_DIR_OUT) ? _hcd_data.channel[i].out.allocated : _hcd_data.channel[i].in.allocated; + if (allocated_dir == 0 && + allocated_other == 1 && + _hcd_data.channel[i].dev_addr == dev_addr && + _hcd_data.channel[i].ep_num == ep_num && + _hcd_data.channel[i].ep_type == ep_type) { + if (dir == TUSB_DIR_OUT) { + _hcd_data.channel[i].out.allocated = 1; + _hcd_data.channel[i].out.retry = 0; + } else { + _hcd_data.channel[i].in.allocated = 1; + _hcd_data.channel[i].in.retry = 0; + } + return i; + } + } + + // Find free channel + for (uint8_t i = 0; i < FSDEV_EP_COUNT; i++) { + if (_hcd_data.channel[i].out.allocated == 0 && _hcd_data.channel[i].in.allocated == 0) { + _hcd_data.channel[i].dev_addr = dev_addr; + _hcd_data.channel[i].ep_num = ep_num; + _hcd_data.channel[i].ep_type = ep_type; + if (dir == TUSB_DIR_OUT) { + _hcd_data.channel[i].out.allocated = 1; + _hcd_data.channel[i].out.retry = 0; + } else { + _hcd_data.channel[i].in.allocated = 1; + _hcd_data.channel[i].in.retry = 0; + } + return i; + } + } + + // Allocation failed + return TUSB_INDEX_INVALID_8; +} + +// kick-off transfer with an endpoint +static bool edpt_xfer_kickoff(uint8_t ep_id) { + hcd_endpoint_t* edpt = &_hcd_data.edpt[ep_id]; + uint8_t ch_id = channel_alloc(edpt->dev_addr, edpt->ep_addr, edpt->ep_type); + TU_ASSERT(ch_id != TUSB_INDEX_INVALID_8); // all channel are in used + + tusb_dir_t const dir = tu_edpt_dir(edpt->ep_addr); + hcd_channel_t* channel = &_hcd_data.channel[ch_id]; + if (dir == TUSB_DIR_OUT) { + channel->out.edpt = edpt; + } else { + channel->in.edpt = edpt; + } + + return channel_xfer_start(ch_id, dir); +} + +static bool channel_xfer_start(uint8_t ch_id, tusb_dir_t dir) { + hcd_channel_t* channel = &_hcd_data.channel[ch_id]; + hcd_endpoint_t* edpt = (dir == TUSB_DIR_OUT) ? channel->out.edpt : channel->in.edpt; + + uint32_t ch_reg = ch_read(ch_id) & ~U_EPREG_MASK; + ch_reg |= tu_edpt_number(edpt->ep_addr) | edpt->dev_addr << U_EP_DEVADDR_Pos | + U_EP_CTR_TX | U_EP_CTR_RX; + + // Set type + switch (edpt->ep_type) { + case TUSB_XFER_BULK: + ch_reg |= U_EP_BULK; + break; + case TUSB_XFER_INTERRUPT: + ch_reg |= U_EP_INTERRUPT; + break; + + case TUSB_XFER_CONTROL: + ch_reg |= U_EP_CONTROL; + break; + + default: + // Note: ISO endpoint is unsupported + TU_ASSERT(false); + } + + /* Create a packet memory buffer area. */ + uint16_t pma_addr = hcd_pma_alloc(ch_id, dir, edpt->max_packet_size); + btable_set_addr(ch_id, dir == TUSB_DIR_OUT ? BTABLE_BUF_TX : BTABLE_BUF_RX, pma_addr); + + if (dir == TUSB_DIR_OUT) { + uint16_t const len = tu_min16(edpt->buflen - edpt->queued_len, edpt->max_packet_size); + tu_hwfifo_write(PMA_BUF_AT(pma_addr), &(edpt->buffer[edpt->queued_len]), len, NULL); + btable_set_count(ch_id, BTABLE_BUF_TX, len); + + edpt->queued_len += len; + } else { + btable_set_rx_bufsize(ch_id, BTABLE_BUF_RX, edpt->max_packet_size); + } + + if (edpt->ls_pre == 1) { + ch_reg |= U_EP_LSEP; + } else { + ch_reg &= ~U_EP_LSEP; + } + + // Setup DATA/STATUS phase start with DATA1 + if (tu_edpt_number(edpt->ep_addr) == 0) { + edpt->pid = 1; + } + + if (edpt->next_setup) { + edpt->next_setup = false; + ch_reg |= U_EP_SETUP; + edpt->pid = 0; + } + + ch_change_status(&ch_reg, dir, EP_STAT_VALID); + ch_change_dtog(&ch_reg, dir, edpt->pid); + ch_reg &= U_EPREG_MASK | CH_STAT_MASK(dir) | CH_DTOG_MASK(dir); + ch_write(ch_id, ch_reg, true); + + return true; +} + +#endif |
