summaryrefslogtreecommitdiff
path: root/src/portable/st
diff options
context:
space:
mode:
authorHiFiPhile <[email protected]>2026-06-22 21:30:58 +0200
committerHiFiPhile <[email protected]>2026-06-22 21:30:58 +0200
commit693cdce08e14833f26f4e8a1f26e4fd546be4c35 (patch)
tree7667d2223dc32d9f21b5b60ab200e64ed46e3e20 /src/portable/st
parent41e9eaa65a935136085d78ec4b99c81ff991b560 (diff)
parentcd3561bf158afd5a5718904b8139a338d1e3b67c (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.c660
-rw-r--r--src/portable/st/stm32_fsdev/fsdev_at32.h131
-rw-r--r--src/portable/st/stm32_fsdev/fsdev_ch32.h116
-rw-r--r--src/portable/st/stm32_fsdev/fsdev_common.c116
-rw-r--r--src/portable/st/stm32_fsdev/fsdev_common.h456
-rw-r--r--src/portable/st/stm32_fsdev/fsdev_stm32.h322
-rw-r--r--src/portable/st/stm32_fsdev/fsdev_type.h307
-rw-r--r--src/portable/st/stm32_fsdev/hcd_stm32_fsdev.c835
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