From d32a6521256594b41e4c54d0dbf50470573ba995 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 9 Apr 2026 10:35:01 +0000 Subject: Refactor STM32 FSDEV PMA errata delay into common helper Agent-Logs-Url: https://github.com/hathach/tinyusb/sessions/3ec0d5b6-cb8e-48ff-8606-3371beb1efcb Co-authored-by: HiFiPhile <4375114+HiFiPhile@users.noreply.github.com> --- src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c | 20 +----------- src/portable/st/stm32_fsdev/fsdev_common.c | 17 ++++++++++ src/portable/st/stm32_fsdev/fsdev_common.h | 23 ++++++++++++++ src/portable/st/stm32_fsdev/fsdev_stm32.h | 14 +++++++++ src/portable/st/stm32_fsdev/hcd_stm32_fsdev.c | 45 ++------------------------- 5 files changed, 57 insertions(+), 62 deletions(-) (limited to 'src') diff --git a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c index 8b4719b21..c8bb0e827 100644 --- a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c +++ b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c @@ -394,25 +394,7 @@ void dcd_int_handler(uint8_t rhport) { if (ep_reg & U_EP_CTR_RX) { #ifdef CFG_TUSB_FSDEV_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) - } + fsdev_btable_workaround_delay(false); #endif if (ep_reg & U_EP_SETUP) { diff --git a/src/portable/st/stm32_fsdev/fsdev_common.c b/src/portable/st/stm32_fsdev/fsdev_common.c index 003bcd069..3f3973a9d 100644 --- a/src/portable/st/stm32_fsdev/fsdev_common.c +++ b/src/portable/st/stm32_fsdev/fsdev_common.c @@ -113,4 +113,21 @@ void btable_set_rx_bufsize(uint32_t ep_id, uint8_t buf_id, uint16_t wCount) { #endif } +/* STM32 FSDEV PMA Buffer Description Table errata workaround: + * - ES0561 (STM32H503), ES0587 (STM32U535/U545) + * - CTR may trigger before final PMA SRAM accesses complete on OUT transfers. + * - Insert delay before reading PMA count/data. + */ +void fsdev_btable_workaround_delay(bool low_speed) { +#if defined(TUP_USBIP_FSDEV_STM32) && defined(CFG_TUSB_FSDEV_32BIT) + uint32_t cycle_count = low_speed ? CFG_TUSB_FSDEV_BTABLE_LS_DELAY_COUNT : CFG_TUSB_FSDEV_BTABLE_FS_DELAY_COUNT; + volatile uint32_t delay_count = cycle_count; + while (delay_count > 0U) { + delay_count--; // each count take 3 cycles (1 for sub, jump, and compare) + } +#else + (void) low_speed; +#endif +} + #endif diff --git a/src/portable/st/stm32_fsdev/fsdev_common.h b/src/portable/st/stm32_fsdev/fsdev_common.h index 140ff1d61..bf4941794 100644 --- a/src/portable/st/stm32_fsdev/fsdev_common.h +++ b/src/portable/st/stm32_fsdev/fsdev_common.h @@ -307,6 +307,26 @@ typedef struct { #error "Unknown USB IP" #endif +#if defined(TUP_USBIP_FSDEV_STM32) && defined(CFG_TUSB_FSDEV_32BIT) + #ifndef CFG_TUSB_FSDEV_BTABLE_FS_DELAY_COUNT + #if defined(FSDEV_STM32_CPU_MHZ) + #define CFG_TUSB_FSDEV_BTABLE_FS_DELAY_COUNT (FSDEV_STM32_CPU_MHZ / 4U) + #else + // Keep conservative default and allow board/application override. + #define CFG_TUSB_FSDEV_BTABLE_FS_DELAY_COUNT 20U + #endif + #endif + + #ifndef CFG_TUSB_FSDEV_BTABLE_LS_DELAY_COUNT + #if defined(FSDEV_STM32_CPU_MHZ) + #define CFG_TUSB_FSDEV_BTABLE_LS_DELAY_COUNT (FSDEV_STM32_CPU_MHZ * 2U) + #else + // Keep conservative default and allow board/application override. + #define CFG_TUSB_FSDEV_BTABLE_LS_DELAY_COUNT 20U + #endif + #endif +#endif + //--------------------------------------------------------------------+ // Endpoint Helper // - CTR is write 0 to clear @@ -449,6 +469,9 @@ uint16_t pma_align_buffer_size(uint16_t size, uint8_t *blsize, uint8_t *num_bloc // Set RX buffer size void btable_set_rx_bufsize(uint32_t ep_id, uint8_t buf_id, uint16_t wCount); +// STM32 FSDEV PMA Buffer Description Table errata workaround delay. +void fsdev_btable_workaround_delay(bool low_speed); + #ifdef __cplusplus } #endif diff --git a/src/portable/st/stm32_fsdev/fsdev_stm32.h b/src/portable/st/stm32_fsdev/fsdev_stm32.h index a63592c5d..3f726c2ec 100644 --- a/src/portable/st/stm32_fsdev/fsdev_stm32.h +++ b/src/portable/st/stm32_fsdev/fsdev_stm32.h @@ -138,6 +138,20 @@ #error "FSDEV_HAS_SBUF_ISO not defined" #endif +#ifndef FSDEV_STM32_CPU_MHZ + #if CFG_TUSB_MCU == OPT_MCU_STM32H5 + #define FSDEV_STM32_CPU_MHZ 250U + #elif CFG_TUSB_MCU == OPT_MCU_STM32U5 + #define FSDEV_STM32_CPU_MHZ 160U + #elif CFG_TUSB_MCU == OPT_MCU_STM32U3 + #define FSDEV_STM32_CPU_MHZ 96U + #elif CFG_TUSB_MCU == OPT_MCU_STM32G0 + #define FSDEV_STM32_CPU_MHZ 64U + #elif CFG_TUSB_MCU == OPT_MCU_STM32C0 + #define FSDEV_STM32_CPU_MHZ 48U + #endif +#endif + #ifndef CFG_TUD_FSDEV_DOUBLE_BUFFERED_ISO_EP // Default configuration for double-buffered isochronous endpoints: // - Enable double buffering on devices with >1KB Packet Memory Area (PMA) diff --git a/src/portable/st/stm32_fsdev/hcd_stm32_fsdev.c b/src/portable/st/stm32_fsdev/hcd_stm32_fsdev.c index 18685dbdc..c41228919 100644 --- a/src/portable/st/stm32_fsdev/hcd_stm32_fsdev.c +++ b/src/portable/st/stm32_fsdev/hcd_stm32_fsdev.c @@ -58,20 +58,6 @@ TU_VERIFY_STATIC(CFG_TUH_FSDEV_ENDPOINT_MAX <= 255, "currently only use 8-bit for index"); -#if CFG_TUSB_MCU == OPT_MCU_STM32H5 - #define CPU_FREQUENCY_MHZ 250U -#elif CFG_TUSB_MCU == OPT_MCU_STM32U5 - #define CPU_FREQUENCY_MHZ 160U -#elif CFG_TUSB_MCU == OPT_MCU_STM32U3 - #define CPU_FREQUENCY_MHZ 96U -#elif CFG_TUSB_MCU == OPT_MCU_STM32G0 - #define CPU_FREQUENCY_MHZ 64U -#elif CFG_TUSB_MCU == OPT_MCU_STM32C0 - #define CPU_FREQUENCY_MHZ 48U -#else - #error "CPU_FREQUENCY_MHZ not defined for this STM32 MCU" -#endif - enum { HCD_XFER_ERROR_MAX = 3, HCD_XFER_NAK_MAX = 15, @@ -165,35 +151,8 @@ static inline void channel_write_status(uint8_t ch_id, uint32_t ch_reg, tusb_dir } static inline uint16_t channel_get_rx_count(uint8_t ch_id) { - /* 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 - * - * Note: this errata may also apply to G0, U5, H5 etc. - * - * We choose the delay count based on max CPU frequency (in MHz) to ensure the delay is at least the required time. - */ - uint32_t ch_reg = ch_read(ch_id); - if (FSDEV_REG->ISTR & U_ISTR_LS_DCONN || ch_reg & U_EP_LSEP) { - // Low speed mode: 6.4 us delay -> about 2 cycles per MHz - volatile uint32_t cycle_count = CPU_FREQUENCY_MHZ * 2U; - while (cycle_count > 0U) { - cycle_count--; // each count take 3 cycles (1 for sub, jump, and compare) - } - } else { - // Full speed mode: 800 ns delay -> about 0.25 cycles per MHz - volatile uint32_t cycle_count = CPU_FREQUENCY_MHZ / 4U; - while (cycle_count > 0U) { - cycle_count--; // each count take 3 cycles (1 for sub, jump, and compare) - } - } + fsdev_btable_workaround_delay((FSDEV_REG->ISTR & U_ISTR_LS_DCONN) || (ch_reg & U_EP_LSEP)); return btable_get_count(ch_id, BTABLE_BUF_RX); } @@ -238,7 +197,7 @@ bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { // If DCON_STAT is already set, the controller sometimes misses the initial connection interrupt if (FSDEV_REG->ISTR & U_ISTR_DCON_STAT) { // Wait DP/DM stabilize time - volatile uint32_t cycle_count = CPU_FREQUENCY_MHZ / 4U; + volatile uint32_t cycle_count = CFG_TUSB_FSDEV_BTABLE_FS_DELAY_COUNT; while (cycle_count > 0U) { cycle_count--; } -- cgit v1.3.1 From b2c36e0000b48f05e161f76fa880b810cf9405b4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 9 Apr 2026 10:39:02 +0000 Subject: Document and polish shared FSDEV errata delay helper Agent-Logs-Url: https://github.com/hathach/tinyusb/sessions/3ec0d5b6-cb8e-48ff-8606-3371beb1efcb Co-authored-by: HiFiPhile <4375114+HiFiPhile@users.noreply.github.com> --- src/portable/st/stm32_fsdev/fsdev_common.c | 2 +- src/portable/st/stm32_fsdev/fsdev_common.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/portable/st/stm32_fsdev/fsdev_common.c b/src/portable/st/stm32_fsdev/fsdev_common.c index 3f3973a9d..4b7c1ad5f 100644 --- a/src/portable/st/stm32_fsdev/fsdev_common.c +++ b/src/portable/st/stm32_fsdev/fsdev_common.c @@ -123,7 +123,7 @@ void fsdev_btable_workaround_delay(bool low_speed) { uint32_t cycle_count = low_speed ? CFG_TUSB_FSDEV_BTABLE_LS_DELAY_COUNT : CFG_TUSB_FSDEV_BTABLE_FS_DELAY_COUNT; volatile uint32_t delay_count = cycle_count; while (delay_count > 0U) { - delay_count--; // each count take 3 cycles (1 for sub, jump, and compare) + delay_count--; // each count takes 3 cycles (1 for sub, jump, and compare) } #else (void) low_speed; diff --git a/src/portable/st/stm32_fsdev/fsdev_common.h b/src/portable/st/stm32_fsdev/fsdev_common.h index bf4941794..2da80fd73 100644 --- a/src/portable/st/stm32_fsdev/fsdev_common.h +++ b/src/portable/st/stm32_fsdev/fsdev_common.h @@ -469,7 +469,8 @@ uint16_t pma_align_buffer_size(uint16_t size, uint8_t *blsize, uint8_t *num_bloc // Set RX buffer size void btable_set_rx_bufsize(uint32_t ep_id, uint8_t buf_id, uint16_t wCount); -// STM32 FSDEV PMA Buffer Description Table errata workaround delay. +// Delay helper for STM32 FSDEV PMA Buffer Description Table errata (ES0561/ES0587). +// Low-speed path uses CFG_TUSB_FSDEV_BTABLE_LS_DELAY_COUNT, otherwise full-speed count. void fsdev_btable_workaround_delay(bool low_speed); #ifdef __cplusplus -- cgit v1.3.1 From e03a8aa6b1005d33f6b47a7c07f21b18444627d2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 9 Apr 2026 10:40:24 +0000 Subject: Restore compile-time guard for STM32 FSDEV delay defaults Agent-Logs-Url: https://github.com/hathach/tinyusb/sessions/3ec0d5b6-cb8e-48ff-8606-3371beb1efcb Co-authored-by: HiFiPhile <4375114+HiFiPhile@users.noreply.github.com> --- src/portable/st/stm32_fsdev/fsdev_common.h | 6 ++---- src/portable/st/stm32_fsdev/fsdev_stm32.h | 4 ++++ 2 files changed, 6 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/portable/st/stm32_fsdev/fsdev_common.h b/src/portable/st/stm32_fsdev/fsdev_common.h index 2da80fd73..ab07ca0e9 100644 --- a/src/portable/st/stm32_fsdev/fsdev_common.h +++ b/src/portable/st/stm32_fsdev/fsdev_common.h @@ -312,8 +312,7 @@ typedef struct { #if defined(FSDEV_STM32_CPU_MHZ) #define CFG_TUSB_FSDEV_BTABLE_FS_DELAY_COUNT (FSDEV_STM32_CPU_MHZ / 4U) #else - // Keep conservative default and allow board/application override. - #define CFG_TUSB_FSDEV_BTABLE_FS_DELAY_COUNT 20U + #error "Define CFG_TUSB_FSDEV_BTABLE_FS_DELAY_COUNT or FSDEV_STM32_CPU_MHZ for STM32 FSDEV 32-bit" #endif #endif @@ -321,8 +320,7 @@ typedef struct { #if defined(FSDEV_STM32_CPU_MHZ) #define CFG_TUSB_FSDEV_BTABLE_LS_DELAY_COUNT (FSDEV_STM32_CPU_MHZ * 2U) #else - // Keep conservative default and allow board/application override. - #define CFG_TUSB_FSDEV_BTABLE_LS_DELAY_COUNT 20U + #error "Define CFG_TUSB_FSDEV_BTABLE_LS_DELAY_COUNT or FSDEV_STM32_CPU_MHZ for STM32 FSDEV 32-bit" #endif #endif #endif diff --git a/src/portable/st/stm32_fsdev/fsdev_stm32.h b/src/portable/st/stm32_fsdev/fsdev_stm32.h index 3f726c2ec..a6827cc60 100644 --- a/src/portable/st/stm32_fsdev/fsdev_stm32.h +++ b/src/portable/st/stm32_fsdev/fsdev_stm32.h @@ -145,10 +145,14 @@ #define FSDEV_STM32_CPU_MHZ 160U #elif CFG_TUSB_MCU == OPT_MCU_STM32U3 #define FSDEV_STM32_CPU_MHZ 96U + #elif CFG_TUSB_MCU == OPT_MCU_STM32U0 + #define FSDEV_STM32_CPU_MHZ 56U #elif CFG_TUSB_MCU == OPT_MCU_STM32G0 #define FSDEV_STM32_CPU_MHZ 64U #elif CFG_TUSB_MCU == OPT_MCU_STM32C0 #define FSDEV_STM32_CPU_MHZ 48U + #elif defined(CFG_TUSB_FSDEV_32BIT) + #error "FSDEV_STM32_CPU_MHZ not defined for this STM32 MCU" #endif #endif -- cgit v1.3.1 From 2f4792f9e1b5a6f06e6306fe0d0ebce641914791 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 9 Apr 2026 10:41:14 +0000 Subject: Decouple DCON stabilize delay from BTABLE errata macro Agent-Logs-Url: https://github.com/hathach/tinyusb/sessions/3ec0d5b6-cb8e-48ff-8606-3371beb1efcb Co-authored-by: HiFiPhile <4375114+HiFiPhile@users.noreply.github.com> --- src/portable/st/stm32_fsdev/hcd_stm32_fsdev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/portable/st/stm32_fsdev/hcd_stm32_fsdev.c b/src/portable/st/stm32_fsdev/hcd_stm32_fsdev.c index c41228919..50ff975fc 100644 --- a/src/portable/st/stm32_fsdev/hcd_stm32_fsdev.c +++ b/src/portable/st/stm32_fsdev/hcd_stm32_fsdev.c @@ -197,7 +197,7 @@ bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { // If DCON_STAT is already set, the controller sometimes misses the initial connection interrupt if (FSDEV_REG->ISTR & U_ISTR_DCON_STAT) { // Wait DP/DM stabilize time - volatile uint32_t cycle_count = CFG_TUSB_FSDEV_BTABLE_FS_DELAY_COUNT; + volatile uint32_t cycle_count = FSDEV_STM32_CPU_MHZ / 4U; while (cycle_count > 0U) { cycle_count--; } -- cgit v1.3.1 From 84943dbbd54e119248f3585aa1fe9c6496af7069 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 9 Apr 2026 10:42:01 +0000 Subject: Simplify delay loop comment in shared FSDEV helper Agent-Logs-Url: https://github.com/hathach/tinyusb/sessions/3ec0d5b6-cb8e-48ff-8606-3371beb1efcb Co-authored-by: HiFiPhile <4375114+HiFiPhile@users.noreply.github.com> --- src/portable/st/stm32_fsdev/fsdev_common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/portable/st/stm32_fsdev/fsdev_common.c b/src/portable/st/stm32_fsdev/fsdev_common.c index 4b7c1ad5f..e6c95e12d 100644 --- a/src/portable/st/stm32_fsdev/fsdev_common.c +++ b/src/portable/st/stm32_fsdev/fsdev_common.c @@ -123,7 +123,7 @@ void fsdev_btable_workaround_delay(bool low_speed) { uint32_t cycle_count = low_speed ? CFG_TUSB_FSDEV_BTABLE_LS_DELAY_COUNT : CFG_TUSB_FSDEV_BTABLE_FS_DELAY_COUNT; volatile uint32_t delay_count = cycle_count; while (delay_count > 0U) { - delay_count--; // each count takes 3 cycles (1 for sub, jump, and compare) + delay_count--; } #else (void) low_speed; -- cgit v1.3.1 From ab32a2cd2b7472966b309521d80b15488393af0e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 9 Apr 2026 10:42:47 +0000 Subject: Clarify STM32U0 delay-default frequency usage Agent-Logs-Url: https://github.com/hathach/tinyusb/sessions/3ec0d5b6-cb8e-48ff-8606-3371beb1efcb Co-authored-by: HiFiPhile <4375114+HiFiPhile@users.noreply.github.com> --- src/portable/st/stm32_fsdev/fsdev_stm32.h | 1 + 1 file changed, 1 insertion(+) (limited to 'src') diff --git a/src/portable/st/stm32_fsdev/fsdev_stm32.h b/src/portable/st/stm32_fsdev/fsdev_stm32.h index a6827cc60..5950efbe3 100644 --- a/src/portable/st/stm32_fsdev/fsdev_stm32.h +++ b/src/portable/st/stm32_fsdev/fsdev_stm32.h @@ -146,6 +146,7 @@ #elif CFG_TUSB_MCU == OPT_MCU_STM32U3 #define FSDEV_STM32_CPU_MHZ 96U #elif CFG_TUSB_MCU == OPT_MCU_STM32U0 + // Used by STM32 FSDEV PMA delay defaults as a conservative max CPU frequency. #define FSDEV_STM32_CPU_MHZ 56U #elif CFG_TUSB_MCU == OPT_MCU_STM32G0 #define FSDEV_STM32_CPU_MHZ 64U -- cgit v1.3.1 From 41575d2de943393f6cc7e5228ab904614fadce28 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 9 Apr 2026 10:44:08 +0000 Subject: Polish FSDEV delay code readability Agent-Logs-Url: https://github.com/hathach/tinyusb/sessions/3ec0d5b6-cb8e-48ff-8606-3371beb1efcb Co-authored-by: HiFiPhile <4375114+HiFiPhile@users.noreply.github.com> --- src/portable/st/stm32_fsdev/fsdev_stm32.h | 2 +- src/portable/st/stm32_fsdev/hcd_stm32_fsdev.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/portable/st/stm32_fsdev/fsdev_stm32.h b/src/portable/st/stm32_fsdev/fsdev_stm32.h index 5950efbe3..5e383eb97 100644 --- a/src/portable/st/stm32_fsdev/fsdev_stm32.h +++ b/src/portable/st/stm32_fsdev/fsdev_stm32.h @@ -139,6 +139,7 @@ #endif #ifndef FSDEV_STM32_CPU_MHZ + // Max CPU frequency in MHz, used to derive conservative FSDEV PMA delay defaults. #if CFG_TUSB_MCU == OPT_MCU_STM32H5 #define FSDEV_STM32_CPU_MHZ 250U #elif CFG_TUSB_MCU == OPT_MCU_STM32U5 @@ -146,7 +147,6 @@ #elif CFG_TUSB_MCU == OPT_MCU_STM32U3 #define FSDEV_STM32_CPU_MHZ 96U #elif CFG_TUSB_MCU == OPT_MCU_STM32U0 - // Used by STM32 FSDEV PMA delay defaults as a conservative max CPU frequency. #define FSDEV_STM32_CPU_MHZ 56U #elif CFG_TUSB_MCU == OPT_MCU_STM32G0 #define FSDEV_STM32_CPU_MHZ 64U diff --git a/src/portable/st/stm32_fsdev/hcd_stm32_fsdev.c b/src/portable/st/stm32_fsdev/hcd_stm32_fsdev.c index 50ff975fc..18cf302d8 100644 --- a/src/portable/st/stm32_fsdev/hcd_stm32_fsdev.c +++ b/src/portable/st/stm32_fsdev/hcd_stm32_fsdev.c @@ -152,7 +152,8 @@ static inline void channel_write_status(uint8_t ch_id, uint32_t ch_reg, tusb_dir static inline uint16_t channel_get_rx_count(uint8_t ch_id) { uint32_t ch_reg = ch_read(ch_id); - fsdev_btable_workaround_delay((FSDEV_REG->ISTR & U_ISTR_LS_DCONN) || (ch_reg & U_EP_LSEP)); + 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); } -- cgit v1.3.1 From 048bb5ca690311a24cae69fd177bd2f4721f27b6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 10 Apr 2026 10:53:03 +0000 Subject: Make fsdev errata delay helper static inline Agent-Logs-Url: https://github.com/hathach/tinyusb/sessions/8588a09c-d4e5-4284-8944-47ae4e2a2a1f Co-authored-by: HiFiPhile <4375114+HiFiPhile@users.noreply.github.com> --- src/portable/st/stm32_fsdev/fsdev_common.c | 17 ----------------- src/portable/st/stm32_fsdev/fsdev_common.h | 12 +++++++++++- 2 files changed, 11 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/portable/st/stm32_fsdev/fsdev_common.c b/src/portable/st/stm32_fsdev/fsdev_common.c index e6c95e12d..003bcd069 100644 --- a/src/portable/st/stm32_fsdev/fsdev_common.c +++ b/src/portable/st/stm32_fsdev/fsdev_common.c @@ -113,21 +113,4 @@ void btable_set_rx_bufsize(uint32_t ep_id, uint8_t buf_id, uint16_t wCount) { #endif } -/* STM32 FSDEV PMA Buffer Description Table errata workaround: - * - ES0561 (STM32H503), ES0587 (STM32U535/U545) - * - CTR may trigger before final PMA SRAM accesses complete on OUT transfers. - * - Insert delay before reading PMA count/data. - */ -void fsdev_btable_workaround_delay(bool low_speed) { -#if defined(TUP_USBIP_FSDEV_STM32) && defined(CFG_TUSB_FSDEV_32BIT) - uint32_t cycle_count = low_speed ? CFG_TUSB_FSDEV_BTABLE_LS_DELAY_COUNT : CFG_TUSB_FSDEV_BTABLE_FS_DELAY_COUNT; - volatile uint32_t delay_count = cycle_count; - while (delay_count > 0U) { - delay_count--; - } -#else - (void) low_speed; -#endif -} - #endif diff --git a/src/portable/st/stm32_fsdev/fsdev_common.h b/src/portable/st/stm32_fsdev/fsdev_common.h index ab07ca0e9..f5cfa93c8 100644 --- a/src/portable/st/stm32_fsdev/fsdev_common.h +++ b/src/portable/st/stm32_fsdev/fsdev_common.h @@ -469,7 +469,17 @@ void btable_set_rx_bufsize(uint32_t ep_id, uint8_t buf_id, uint16_t wCount); // Delay helper for STM32 FSDEV PMA Buffer Description Table errata (ES0561/ES0587). // Low-speed path uses CFG_TUSB_FSDEV_BTABLE_LS_DELAY_COUNT, otherwise full-speed count. -void fsdev_btable_workaround_delay(bool low_speed); +TU_ATTR_ALWAYS_INLINE static inline void fsdev_btable_workaround_delay(bool low_speed) { +#if defined(TUP_USBIP_FSDEV_STM32) && defined(CFG_TUSB_FSDEV_32BIT) + uint32_t cycle_count = low_speed ? CFG_TUSB_FSDEV_BTABLE_LS_DELAY_COUNT : CFG_TUSB_FSDEV_BTABLE_FS_DELAY_COUNT; + volatile uint32_t delay_count = cycle_count; + while (delay_count > 0U) { + delay_count--; + } +#else + (void) low_speed; +#endif +} #ifdef __cplusplus } -- cgit v1.3.1 From 54e1973906c6152ffd5d650d4f8fb146b4fa9068 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 10 Apr 2026 10:53:41 +0000 Subject: Restore errata context comment on inline delay helper Agent-Logs-Url: https://github.com/hathach/tinyusb/sessions/8588a09c-d4e5-4284-8944-47ae4e2a2a1f Co-authored-by: HiFiPhile <4375114+HiFiPhile@users.noreply.github.com> --- src/portable/st/stm32_fsdev/fsdev_common.h | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/portable/st/stm32_fsdev/fsdev_common.h b/src/portable/st/stm32_fsdev/fsdev_common.h index f5cfa93c8..95854d63d 100644 --- a/src/portable/st/stm32_fsdev/fsdev_common.h +++ b/src/portable/st/stm32_fsdev/fsdev_common.h @@ -467,8 +467,13 @@ uint16_t pma_align_buffer_size(uint16_t size, uint8_t *blsize, uint8_t *num_bloc // Set RX buffer size void btable_set_rx_bufsize(uint32_t ep_id, uint8_t buf_id, uint16_t wCount); -// Delay helper for STM32 FSDEV PMA Buffer Description Table errata (ES0561/ES0587). -// Low-speed path uses CFG_TUSB_FSDEV_BTABLE_LS_DELAY_COUNT, otherwise full-speed count. +/* STM32 FSDEV PMA Buffer Description Table errata workaround: + * - ES0561 (STM32H503), ES0587 (STM32U535/U545) + * - CTR may trigger before final PMA SRAM accesses complete on OUT transfers. + * - Insert delay before reading PMA count/data. + * + * Low-speed path uses CFG_TUSB_FSDEV_BTABLE_LS_DELAY_COUNT, otherwise full-speed count. + */ TU_ATTR_ALWAYS_INLINE static inline void fsdev_btable_workaround_delay(bool low_speed) { #if defined(TUP_USBIP_FSDEV_STM32) && defined(CFG_TUSB_FSDEV_32BIT) uint32_t cycle_count = low_speed ? CFG_TUSB_FSDEV_BTABLE_LS_DELAY_COUNT : CFG_TUSB_FSDEV_BTABLE_FS_DELAY_COUNT; -- cgit v1.3.1 From 37ace45508fb32568e78ba3410d708db359a46d4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 10 Apr 2026 11:11:31 +0000 Subject: Allow STM32 FSDEV delay override macros to satisfy 32-bit guard Agent-Logs-Url: https://github.com/hathach/tinyusb/sessions/b35badc2-444a-4329-b136-f314591cf693 Co-authored-by: HiFiPhile <4375114+HiFiPhile@users.noreply.github.com> --- src/portable/st/stm32_fsdev/fsdev_stm32.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/portable/st/stm32_fsdev/fsdev_stm32.h b/src/portable/st/stm32_fsdev/fsdev_stm32.h index 5e383eb97..d2a7a6caa 100644 --- a/src/portable/st/stm32_fsdev/fsdev_stm32.h +++ b/src/portable/st/stm32_fsdev/fsdev_stm32.h @@ -152,8 +152,9 @@ #define FSDEV_STM32_CPU_MHZ 64U #elif CFG_TUSB_MCU == OPT_MCU_STM32C0 #define FSDEV_STM32_CPU_MHZ 48U - #elif defined(CFG_TUSB_FSDEV_32BIT) - #error "FSDEV_STM32_CPU_MHZ not defined for this STM32 MCU" + #elif defined(CFG_TUSB_FSDEV_32BIT) && \ + (!defined(CFG_TUSB_FSDEV_BTABLE_FS_DELAY_COUNT) || !defined(CFG_TUSB_FSDEV_BTABLE_LS_DELAY_COUNT)) + #error "Define FSDEV_STM32_CPU_MHZ or both CFG_TUSB_FSDEV_BTABLE_{FS,LS}_DELAY_COUNT for this STM32 MCU" #endif #endif -- cgit v1.3.1 From 35045e0346f64fe3b19e94c8e9c8d2eb908fc5d6 Mon Sep 17 00:00:00 2001 From: HiFiPhile Date: Fri, 10 Apr 2026 13:33:13 +0200 Subject: debloat the workaround Signed-off-by: HiFiPhile --- src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c | 2 +- src/portable/st/stm32_fsdev/fsdev_common.h | 37 ----------------- src/portable/st/stm32_fsdev/fsdev_stm32.h | 60 ++++++++++++++++++--------- src/portable/st/stm32_fsdev/hcd_stm32_fsdev.c | 6 +-- 4 files changed, 42 insertions(+), 63 deletions(-) (limited to 'src') diff --git a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c index c8bb0e827..3ef0819bd 100644 --- a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c +++ b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c @@ -393,7 +393,7 @@ void dcd_int_handler(uint8_t rhport) { const uint32_t ep_reg = ep_read(ep_id); if (ep_reg & U_EP_CTR_RX) { - #ifdef CFG_TUSB_FSDEV_32BIT + #if defined(TUP_USBIP_FSDEV_STM32) && defined(CFG_TUSB_FSDEV_32BIT) fsdev_btable_workaround_delay(false); #endif diff --git a/src/portable/st/stm32_fsdev/fsdev_common.h b/src/portable/st/stm32_fsdev/fsdev_common.h index 95854d63d..140ff1d61 100644 --- a/src/portable/st/stm32_fsdev/fsdev_common.h +++ b/src/portable/st/stm32_fsdev/fsdev_common.h @@ -307,24 +307,6 @@ typedef struct { #error "Unknown USB IP" #endif -#if defined(TUP_USBIP_FSDEV_STM32) && defined(CFG_TUSB_FSDEV_32BIT) - #ifndef CFG_TUSB_FSDEV_BTABLE_FS_DELAY_COUNT - #if defined(FSDEV_STM32_CPU_MHZ) - #define CFG_TUSB_FSDEV_BTABLE_FS_DELAY_COUNT (FSDEV_STM32_CPU_MHZ / 4U) - #else - #error "Define CFG_TUSB_FSDEV_BTABLE_FS_DELAY_COUNT or FSDEV_STM32_CPU_MHZ for STM32 FSDEV 32-bit" - #endif - #endif - - #ifndef CFG_TUSB_FSDEV_BTABLE_LS_DELAY_COUNT - #if defined(FSDEV_STM32_CPU_MHZ) - #define CFG_TUSB_FSDEV_BTABLE_LS_DELAY_COUNT (FSDEV_STM32_CPU_MHZ * 2U) - #else - #error "Define CFG_TUSB_FSDEV_BTABLE_LS_DELAY_COUNT or FSDEV_STM32_CPU_MHZ for STM32 FSDEV 32-bit" - #endif - #endif -#endif - //--------------------------------------------------------------------+ // Endpoint Helper // - CTR is write 0 to clear @@ -467,25 +449,6 @@ uint16_t pma_align_buffer_size(uint16_t size, uint8_t *blsize, uint8_t *num_bloc // Set RX buffer size void btable_set_rx_bufsize(uint32_t ep_id, uint8_t buf_id, uint16_t wCount); -/* STM32 FSDEV PMA Buffer Description Table errata workaround: - * - ES0561 (STM32H503), ES0587 (STM32U535/U545) - * - CTR may trigger before final PMA SRAM accesses complete on OUT transfers. - * - Insert delay before reading PMA count/data. - * - * Low-speed path uses CFG_TUSB_FSDEV_BTABLE_LS_DELAY_COUNT, otherwise full-speed count. - */ -TU_ATTR_ALWAYS_INLINE static inline void fsdev_btable_workaround_delay(bool low_speed) { -#if defined(TUP_USBIP_FSDEV_STM32) && defined(CFG_TUSB_FSDEV_32BIT) - uint32_t cycle_count = low_speed ? CFG_TUSB_FSDEV_BTABLE_LS_DELAY_COUNT : CFG_TUSB_FSDEV_BTABLE_FS_DELAY_COUNT; - volatile uint32_t delay_count = cycle_count; - while (delay_count > 0U) { - delay_count--; - } -#else - (void) low_speed; -#endif -} - #ifdef __cplusplus } #endif diff --git a/src/portable/st/stm32_fsdev/fsdev_stm32.h b/src/portable/st/stm32_fsdev/fsdev_stm32.h index d2a7a6caa..79052b489 100644 --- a/src/portable/st/stm32_fsdev/fsdev_stm32.h +++ b/src/portable/st/stm32_fsdev/fsdev_stm32.h @@ -138,26 +138,6 @@ #error "FSDEV_HAS_SBUF_ISO not defined" #endif -#ifndef FSDEV_STM32_CPU_MHZ - // Max CPU frequency in MHz, used to derive conservative FSDEV PMA delay defaults. - #if CFG_TUSB_MCU == OPT_MCU_STM32H5 - #define FSDEV_STM32_CPU_MHZ 250U - #elif CFG_TUSB_MCU == OPT_MCU_STM32U5 - #define FSDEV_STM32_CPU_MHZ 160U - #elif CFG_TUSB_MCU == OPT_MCU_STM32U3 - #define FSDEV_STM32_CPU_MHZ 96U - #elif CFG_TUSB_MCU == OPT_MCU_STM32U0 - #define FSDEV_STM32_CPU_MHZ 56U - #elif CFG_TUSB_MCU == OPT_MCU_STM32G0 - #define FSDEV_STM32_CPU_MHZ 64U - #elif CFG_TUSB_MCU == OPT_MCU_STM32C0 - #define FSDEV_STM32_CPU_MHZ 48U - #elif defined(CFG_TUSB_FSDEV_32BIT) && \ - (!defined(CFG_TUSB_FSDEV_BTABLE_FS_DELAY_COUNT) || !defined(CFG_TUSB_FSDEV_BTABLE_LS_DELAY_COUNT)) - #error "Define FSDEV_STM32_CPU_MHZ or both CFG_TUSB_FSDEV_BTABLE_{FS,LS}_DELAY_COUNT for this STM32 MCU" - #endif -#endif - #ifndef CFG_TUD_FSDEV_DOUBLE_BUFFERED_ISO_EP // Default configuration for double-buffered isochronous endpoints: // - Enable double buffering on devices with >1KB Packet Memory Area (PMA) @@ -271,6 +251,46 @@ TU_ATTR_ALWAYS_INLINE static inline void fsdev_int_disable(uint8_t rhport) { // CMSIS has a membar after disabling interrupts } +//--------------------------------------------------------------------+ +// STM32 FSDEV PMA Buffer Description Table errata workaround +//--------------------------------------------------------------------+ + +#ifdef CFG_TUSB_FSDEV_32BIT +// ES0561 (STM32H503), ES0587 (STM32U535/U545) +// CTR may trigger before final PMA SRAM accesses complete on OUT transfers. +// Insert delay before reading PMA count/data. +// Max CPU frequency in MHz, used to derive conservative FSDEV PMA delay defaults. +#if CFG_TUSB_MCU == OPT_MCU_STM32H5 + #define FSDEV_STM32_CPU_MHZ 250U +#elif CFG_TUSB_MCU == OPT_MCU_STM32U5 + #define FSDEV_STM32_CPU_MHZ 160U +#elif CFG_TUSB_MCU == OPT_MCU_STM32U3 + #define FSDEV_STM32_CPU_MHZ 96U +#elif CFG_TUSB_MCU == OPT_MCU_STM32U0 + #define FSDEV_STM32_CPU_MHZ 56U +#elif CFG_TUSB_MCU == OPT_MCU_STM32G0 + #define FSDEV_STM32_CPU_MHZ 64U +#elif CFG_TUSB_MCU == OPT_MCU_STM32C0 + #define FSDEV_STM32_CPU_MHZ 48U +#endif + +#ifndef CFG_TUSB_FSDEV_BTABLE_FS_DELAY_COUNT + #define CFG_TUSB_FSDEV_BTABLE_FS_DELAY_COUNT (FSDEV_STM32_CPU_MHZ / 4U) +#endif + +#ifndef CFG_TUSB_FSDEV_BTABLE_LS_DELAY_COUNT + #define CFG_TUSB_FSDEV_BTABLE_LS_DELAY_COUNT (FSDEV_STM32_CPU_MHZ * 2U) +#endif + +TU_ATTR_ALWAYS_INLINE static inline void fsdev_btable_workaround_delay(bool low_speed) { + uint32_t cycle_count = low_speed ? CFG_TUSB_FSDEV_BTABLE_LS_DELAY_COUNT : CFG_TUSB_FSDEV_BTABLE_FS_DELAY_COUNT; + volatile uint32_t delay_count = cycle_count; + while (delay_count > 0U) { + delay_count--; + } +} +#endif + //--------------------------------------------------------------------+ // Connect / Disconnect //--------------------------------------------------------------------+ diff --git a/src/portable/st/stm32_fsdev/hcd_stm32_fsdev.c b/src/portable/st/stm32_fsdev/hcd_stm32_fsdev.c index 18cf302d8..f9201651a 100644 --- a/src/portable/st/stm32_fsdev/hcd_stm32_fsdev.c +++ b/src/portable/st/stm32_fsdev/hcd_stm32_fsdev.c @@ -197,11 +197,7 @@ bool hcd_init(uint8_t rhport, const tusb_rhport_init_t* rh_init) { // If DCON_STAT is already set, the controller sometimes misses the initial connection interrupt if (FSDEV_REG->ISTR & U_ISTR_DCON_STAT) { - // Wait DP/DM stabilize time - volatile uint32_t cycle_count = FSDEV_STM32_CPU_MHZ / 4U; - while (cycle_count > 0U) { - cycle_count--; - } + tusb_time_delay_ms_api(2); port_status_handler(rhport, false); } -- cgit v1.3.1 From ed37ad8ce2c7175aacb7dbc6348b4328835a2ff2 Mon Sep 17 00:00:00 2001 From: Brent Kowal Date: Fri, 10 Apr 2026 14:25:28 -0400 Subject: Fix musb RXRDY Clearing Resolves an issue in the musb handle_xfer_out function where not all execution paths cleared the MUSB_RXCSRL1_RXRDY bit, causing the RX interface to hang and no longer communicate with the host. Signed-off-by: Brent Kowal --- src/portable/mentor/musb/dcd_musb.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/portable/mentor/musb/dcd_musb.c b/src/portable/mentor/musb/dcd_musb.c index 339048473..64f9ebacf 100644 --- a/src/portable/mentor/musb/dcd_musb.c +++ b/src/portable/mentor/musb/dcd_musb.c @@ -230,13 +230,19 @@ static bool handle_xfer_out(uint8_t rhport, uint_fast8_t ep_addr) musb_ep_csr_t* ep_csr = get_ep_csr(musb_regs, epnum); // TU_LOG1(" RXCSRL%d = %x\r\n", epnum_minus1 + 1, ep_csr->rx_csrl); - TU_ASSERT(ep_csr->rx_csrl & MUSB_RXCSRL1_RXRDY); + //Fail gracefully. Spurious interrupt. + if (!(ep_csr->rx_csrl & MUSB_RXCSRL1_RXRDY)) return false; + + void *buf = pipe->buf; + if (buf == NULL) { + ep_csr->rx_csrl = MUSB_RXCSRL1_FLUSH; + return false; + } const unsigned mps = ep_csr->rx_maxp; const unsigned rem = pipe->remaining; const unsigned vld = ep_csr->rx_count; const unsigned len = TU_MIN(TU_MIN(rem, mps), vld); - void *buf = pipe->buf; volatile void *fifo_ptr = &musb_regs->fifo[epnum]; if (len) { if (_dcd.pipe_buf_is_fifo[TUSB_DIR_OUT] & TU_BIT(epnum_minus1)) { @@ -247,11 +253,12 @@ static bool handle_xfer_out(uint8_t rhport, uint_fast8_t ep_addr) } pipe->remaining = rem - len; } + + ep_csr->rx_csrl = 0; /* Always Clear RXRDY bit */ if ((len < mps) || (rem == len)) { pipe->buf = NULL; return NULL != buf; } - ep_csr->rx_csrl = 0; /* Clear RXRDY bit */ return false; } -- cgit v1.3.1 From 34aded88edd5b1403bb35b531985953502a0faab Mon Sep 17 00:00:00 2001 From: Fan DANG Date: Mon, 13 Apr 2026 08:56:36 +0800 Subject: fix(device): big-endian host support for SETUP packet handling 1. Add TU_LITTLE_ENDIAN_BITFIELD / TU_BIG_ENDIAN_BITFIELD macros in tusb_compiler.h (GCC and IAR), following Linux kernel style. 2. Update bmAttributes (tusb_desc_endpoint_t) and bmRequestType_bit (tusb_control_request_t) in tusb_types.h to use these macros with explicit #error fallback if undefined. 3. Add tu_le16toh() conversion in dcd_event_setup_received() for wValue/wIndex/wLength. Tested on CIU98320B (big-endian ARM Cortex-M, full-speed HID keyboard). --- src/common/tusb_compiler.h | 4 ++++ src/common/tusb_types.h | 17 +++++++++++++++++ src/device/dcd.h | 5 +++++ 3 files changed, 26 insertions(+) (limited to 'src') diff --git a/src/common/tusb_compiler.h b/src/common/tusb_compiler.h index f20834cea..e66bcc5ea 100644 --- a/src/common/tusb_compiler.h +++ b/src/common/tusb_compiler.h @@ -167,8 +167,10 @@ // For TI ARM compiler, __BYTE_ORDER__ is not defined for MSP430 but still LE #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ || defined(__MSP430__) #define TU_BYTE_ORDER TU_LITTLE_ENDIAN + #define TU_LITTLE_ENDIAN_BITFIELD #else #define TU_BYTE_ORDER TU_BIG_ENDIAN + #define TU_BIG_ENDIAN_BITFIELD #endif // Unfortunately XC16 doesn't provide builtins for 32bit endian conversion @@ -212,8 +214,10 @@ // Endian conversion use well-known host to network (big endian) naming #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ #define TU_BYTE_ORDER TU_LITTLE_ENDIAN + #define TU_LITTLE_ENDIAN_BITFIELD #else #define TU_BYTE_ORDER TU_BIG_ENDIAN + #define TU_BIG_ENDIAN_BITFIELD #endif #define TU_BSWAP16(u16) (__iar_builtin_REV16(u16)) diff --git a/src/common/tusb_types.h b/src/common/tusb_types.h index a18f9feb7..b02e90eae 100644 --- a/src/common/tusb_types.h +++ b/src/common/tusb_types.h @@ -409,10 +409,19 @@ typedef struct TU_ATTR_PACKED { uint8_t bEndpointAddress ; // The address of the endpoint struct TU_ATTR_PACKED { +#if defined(TU_LITTLE_ENDIAN_BITFIELD) uint8_t xfer : 2; // Control, ISO, Bulk, Interrupt uint8_t sync : 2; // None, Asynchronous, Adaptive, Synchronous uint8_t usage : 2; // Data, Feedback, Implicit feedback uint8_t : 2; +#elif defined(TU_BIG_ENDIAN_BITFIELD) + uint8_t : 2; + uint8_t usage : 2; + uint8_t sync : 2; + uint8_t xfer : 2; +#else + #error "Please define TU_LITTLE_ENDIAN_BITFIELD or TU_BIG_ENDIAN_BITFIELD" +#endif } bmAttributes; uint16_t wMaxPacketSize ; // Bit 10..0 : max packet size, bit 12..11 additional transaction per highspeed micro-frame @@ -522,9 +531,17 @@ typedef struct TU_ATTR_PACKED { typedef struct TU_ATTR_PACKED { union { struct TU_ATTR_PACKED { +#if defined(TU_LITTLE_ENDIAN_BITFIELD) uint8_t recipient : 5; ///< Recipient type tusb_request_recipient_t. uint8_t type : 2; ///< Request type tusb_request_type_t. uint8_t direction : 1; ///< Direction type. tusb_dir_t +#elif defined(TU_BIG_ENDIAN_BITFIELD) + uint8_t direction : 1; ///< Direction type. tusb_dir_t + uint8_t type : 2; ///< Request type tusb_request_type_t. + uint8_t recipient : 5; ///< Recipient type tusb_request_recipient_t. +#else + #error "Please define TU_LITTLE_ENDIAN_BITFIELD or TU_BIG_ENDIAN_BITFIELD" +#endif } bmRequestType_bit; uint8_t bmRequestType; diff --git a/src/device/dcd.h b/src/device/dcd.h index 850c37bc2..f861eb258 100644 --- a/src/device/dcd.h +++ b/src/device/dcd.h @@ -219,6 +219,11 @@ TU_ATTR_ALWAYS_INLINE static inline void dcd_event_setup_received(uint8_t rhport event.rhport = rhport; event.event_id = DCD_EVENT_SETUP_RECEIVED; (void) memcpy(&event.setup_received, setup, sizeof(tusb_control_request_t)); + // USB wire format is little-endian. Convert multi-byte fields to host byte order + // so the stack always sees correct values regardless of CPU endianness. + event.setup_received.wValue = tu_le16toh(event.setup_received.wValue); + event.setup_received.wIndex = tu_le16toh(event.setup_received.wIndex); + event.setup_received.wLength = tu_le16toh(event.setup_received.wLength); dcd_event_handler(&event, in_isr); } -- cgit v1.3.1 From ce9864a0bcad0b22b1494466b7ef379e1b688319 Mon Sep 17 00:00:00 2001 From: Fan DANG Date: Mon, 13 Apr 2026 10:14:20 +0800 Subject: introduce two more macros to follow tinyusb's style --- src/common/tusb_compiler.h | 11 +++++++---- src/common/tusb_types.h | 12 ++++++------ 2 files changed, 13 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/common/tusb_compiler.h b/src/common/tusb_compiler.h index e66bcc5ea..4ed14dcfb 100644 --- a/src/common/tusb_compiler.h +++ b/src/common/tusb_compiler.h @@ -66,6 +66,9 @@ #define TU_LITTLE_ENDIAN (0x12u) #define TU_BIG_ENDIAN (0x21u) +#define TU_BITFIELD_LE (0x34u) +#define TU_BITFIELD_BE (0x43u) + /*------------------------------------------------------------------*/ /* Count number of arguments of __VA_ARGS__ * - reference www.stackoverflow.com/questions/2124339/c-preprocessor-va-args-number-of-arguments @@ -167,10 +170,10 @@ // For TI ARM compiler, __BYTE_ORDER__ is not defined for MSP430 but still LE #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ || defined(__MSP430__) #define TU_BYTE_ORDER TU_LITTLE_ENDIAN - #define TU_LITTLE_ENDIAN_BITFIELD + #define TU_BITFIELD_ORDER TU_BITFIELD_LE #else #define TU_BYTE_ORDER TU_BIG_ENDIAN - #define TU_BIG_ENDIAN_BITFIELD + #define TU_BITFIELD_ORDER TU_BITFIELD_BE #endif // Unfortunately XC16 doesn't provide builtins for 32bit endian conversion @@ -214,10 +217,10 @@ // Endian conversion use well-known host to network (big endian) naming #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ #define TU_BYTE_ORDER TU_LITTLE_ENDIAN - #define TU_LITTLE_ENDIAN_BITFIELD + #define TU_BITFIELD_ORDER TU_BITFIELD_LE #else #define TU_BYTE_ORDER TU_BIG_ENDIAN - #define TU_BIG_ENDIAN_BITFIELD + #define TU_BITFIELD_ORDER TU_BITFIELD_BE #endif #define TU_BSWAP16(u16) (__iar_builtin_REV16(u16)) diff --git a/src/common/tusb_types.h b/src/common/tusb_types.h index b02e90eae..70c73b27d 100644 --- a/src/common/tusb_types.h +++ b/src/common/tusb_types.h @@ -409,18 +409,18 @@ typedef struct TU_ATTR_PACKED { uint8_t bEndpointAddress ; // The address of the endpoint struct TU_ATTR_PACKED { -#if defined(TU_LITTLE_ENDIAN_BITFIELD) +#if (TU_BITFIELD_ORDER == TU_BITFIELD_LE) uint8_t xfer : 2; // Control, ISO, Bulk, Interrupt uint8_t sync : 2; // None, Asynchronous, Adaptive, Synchronous uint8_t usage : 2; // Data, Feedback, Implicit feedback uint8_t : 2; -#elif defined(TU_BIG_ENDIAN_BITFIELD) +#elif (TU_BITFIELD_ORDER == TU_BITFIELD_BE) uint8_t : 2; uint8_t usage : 2; uint8_t sync : 2; uint8_t xfer : 2; #else - #error "Please define TU_LITTLE_ENDIAN_BITFIELD or TU_BIG_ENDIAN_BITFIELD" + #error "Please define TU_BITFIELD_ORDER as TU_BITFIELD_LE or TU_BITFIELD_BE" #endif } bmAttributes; @@ -531,16 +531,16 @@ typedef struct TU_ATTR_PACKED { typedef struct TU_ATTR_PACKED { union { struct TU_ATTR_PACKED { -#if defined(TU_LITTLE_ENDIAN_BITFIELD) +#if (TU_BITFIELD_ORDER == TU_BITFIELD_LE) uint8_t recipient : 5; ///< Recipient type tusb_request_recipient_t. uint8_t type : 2; ///< Request type tusb_request_type_t. uint8_t direction : 1; ///< Direction type. tusb_dir_t -#elif defined(TU_BIG_ENDIAN_BITFIELD) +#elif (TU_BITFIELD_ORDER == TU_BITFIELD_BE) uint8_t direction : 1; ///< Direction type. tusb_dir_t uint8_t type : 2; ///< Request type tusb_request_type_t. uint8_t recipient : 5; ///< Recipient type tusb_request_recipient_t. #else - #error "Please define TU_LITTLE_ENDIAN_BITFIELD or TU_BIG_ENDIAN_BITFIELD" + #error "Please define TU_BITFIELD_ORDER as TU_BITFIELD_LE or TU_BITFIELD_BE" #endif } bmRequestType_bit; -- cgit v1.3.1 From a6dcc3f089bd4468949ea9e138fea9f80025577e Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 17 Apr 2026 00:10:44 +0700 Subject: fix some Wconversion warnings --- .gitignore | 1 + hw/bsp/board.c | 4 +-- hw/bsp/mm32/family.c | 6 ++-- hw/bsp/samd11/family.c | 4 ++- hw/bsp/samd2x_l2x/family.c | 2 +- hw/bsp/samg/family.c | 4 ++- hw/bsp/tm4c/family.c | 2 +- src/common/tusb_fifo.c | 8 ++--- src/common/tusb_types.h | 4 +-- src/portable/chipidea/ci_fs/dcd_ci_fs.c | 14 ++++----- src/portable/dialog/da146xx/dcd_da146xx.c | 42 +++++++++++++++------------ src/portable/mentor/musb/dcd_musb.c | 22 +++++++------- src/portable/mentor/musb/musb_type.h | 2 +- src/portable/microchip/samg/dcd_samg.c | 4 +-- src/portable/nordic/nrf5x/dcd_nrf5x.c | 4 ++- src/portable/nuvoton/nuc120/dcd_nuc120.c | 8 ++--- src/portable/nuvoton/nuc121/dcd_nuc121.c | 8 +++-- src/portable/nuvoton/nuc505/dcd_nuc505.c | 6 ++-- src/portable/nxp/lpc17_40/dcd_lpc17_40.c | 4 +-- src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c | 10 +++---- src/portable/renesas/rusb2/dcd_rusb2.c | 34 ++++++++++++---------- src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c | 18 ++++++------ src/portable/st/stm32_fsdev/fsdev_common.c | 4 +-- src/portable/st/stm32_fsdev/fsdev_common.h | 4 +-- src/portable/synopsys/dwc2/dcd_dwc2.c | 6 ++-- 25 files changed, 122 insertions(+), 103 deletions(-) (limited to 'src') diff --git a/.gitignore b/.gitignore index 162f9a019..b833191f8 100644 --- a/.gitignore +++ b/.gitignore @@ -55,3 +55,4 @@ Release BrowseInfo .cmake_build README_processed.rst +.worktrees diff --git a/hw/bsp/board.c b/hw/bsp/board.c index ae58bb5fc..65b44e5f2 100644 --- a/hw/bsp/board.c +++ b/hw/bsp/board.c @@ -51,12 +51,12 @@ int sys_read(int fhdl, char *buf, size_t count) TU_ATTR_USED; int sys_write(int fhdl, const char *buf, size_t count) { (void) fhdl; - return (int) SEGGER_RTT_Write(0, buf, (int) count); + return (int) SEGGER_RTT_Write(0, buf, (unsigned) count); } int sys_read(int fhdl, char *buf, size_t count) { (void) fhdl; - int rd = (int) SEGGER_RTT_Read(0, buf, count); + int rd = (int) SEGGER_RTT_Read(0, buf, (unsigned) count); return (rd > 0) ? rd : -1; } #endif diff --git a/hw/bsp/mm32/family.c b/hw/bsp/mm32/family.c index 651c9496e..14a17f6c5 100644 --- a/hw/bsp/mm32/family.c +++ b/hw/bsp/mm32/family.c @@ -59,11 +59,11 @@ void OTG_FS_IRQHandler(void) { void USB_DeviceClockInit(void) { /* Select USBCLK source */ // RCC_USBCLKConfig(RCC_USBCLKSource_PLLCLK_Div1); - RCC->CFGR &= ~(0x3 << 22); - RCC->CFGR |= (0x1 << 22); + RCC->CFGR &= ~(0x3U << 22); + RCC->CFGR |= (0x1U << 22); /* Enable USB clock */ - RCC->AHB2ENR |= 0x1 << 7; + RCC->AHB2ENR |= 0x1U << 7; } void board_init(void) { diff --git a/hw/bsp/samd11/family.c b/hw/bsp/samd11/family.c index 0c987b85a..0864aa3dd 100644 --- a/hw/bsp/samd11/family.c +++ b/hw/bsp/samd11/family.c @@ -34,6 +34,8 @@ #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-qual" +#pragma GCC diagnostic ignored "-Wconversion" +#pragma GCC diagnostic ignored "-Wsign-conversion" #endif #include "hal/include/hal_gpio.h" @@ -66,7 +68,7 @@ void USB_Handler(void) //--------------------------------------------------------------------+ /* Referenced GCLKs, should be initialized firstly */ -#define _GCLK_INIT_1ST (1 << 0 | 1 << 1) +#define _GCLK_INIT_1ST (1u << 0 | 1u << 1) /* Not referenced GCLKs, initialized last */ #define _GCLK_INIT_LAST (~_GCLK_INIT_1ST) diff --git a/hw/bsp/samd2x_l2x/family.c b/hw/bsp/samd2x_l2x/family.c index 11b6343cd..306ea2308 100644 --- a/hw/bsp/samd2x_l2x/family.c +++ b/hw/bsp/samd2x_l2x/family.c @@ -79,7 +79,7 @@ #ifdef SAMD21_FAMILY /* Referenced GCLKs, should be initialized firstly */ -#define _GCLK_INIT_1ST (1 << 0 | 1 << 1) +#define _GCLK_INIT_1ST (1u << 0 | 1u << 1) /* Not referenced GCLKs, initialized last */ #define _GCLK_INIT_LAST (~_GCLK_INIT_1ST) #endif diff --git a/hw/bsp/samg/family.c b/hw/bsp/samg/family.c index 519068986..9edc43b51 100644 --- a/hw/bsp/samg/family.c +++ b/hw/bsp/samg/family.c @@ -27,15 +27,17 @@ manufacturer: Microchip */ -#include "sam.h" // Suppress warning caused by mcu driver #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-qual" #pragma GCC diagnostic ignored "-Wredundant-decls" +#pragma GCC diagnostic ignored "-Wconversion" +#pragma GCC diagnostic ignored "-Wsign-conversion" #endif +#include "sam.h" #include "peripheral_clk_config.h" #include "hal/include/hal_init.h" #include "hal/include/hpl_usart_sync.h" diff --git a/hw/bsp/tm4c/family.c b/hw/bsp/tm4c/family.c index 6988a264e..fe4737e50 100644 --- a/hw/bsp/tm4c/family.c +++ b/hw/bsp/tm4c/family.c @@ -36,7 +36,7 @@ static void board_uart_init(void) { // BAUDRATE = 115200, with SystemCoreClock = 50 Mhz refer manual for calculation // - BRDI = SystemCoreClock / (16* baud) // - BRDF = int(fraction*64 + 0.5) - UART0->CTL &= ~(1 << 0); // Disable UART0 by clearing UARTEN bit in the UARTCTL register + UART0->CTL &= ~(1U << 0); // Disable UART0 by clearing UARTEN bit in the UARTCTL register UART0->IBRD = 27; // Write the integer portion of the BRD to the UARTIRD register UART0->FBRD = 8; // Write the fractional portion of the BRD to the UARTFBRD registerer diff --git a/src/common/tusb_fifo.c b/src/common/tusb_fifo.c index 06d25d131..a8ac99fd2 100644 --- a/src/common/tusb_fifo.c +++ b/src/common/tusb_fifo.c @@ -281,14 +281,14 @@ static void hwff_push_n(const tu_fifo_t *f, const void *app_buf, uint16_t n, uin // Write full words to the linear part of the buffer const uint8_t data_stride = access_mode->data_stride; const uint32_t odd_mask = data_stride - 1; - uint16_t lin_even = lin_bytes & ~odd_mask; + uint16_t lin_even = (uint16_t)(lin_bytes & ~odd_mask); tu_hwfifo_read(hwfifo, ff_buf, lin_even, access_mode); HWFIFO_ADDR_NEXT_N(hwfifo, const, lin_even * HWFIFO_ADDR_DATA_RATIO); ff_buf += lin_even; // There could be an odd 1 byte (16bit) or 1-3 bytes (32bit) before the wrap-around boundary // combine it with the wrapped part to form a full word for data stride - const uint8_t lin_odd = lin_bytes & odd_mask; + const uint8_t lin_odd = (uint8_t)(lin_bytes & odd_mask); if (lin_odd > 0) { const uint8_t wrap_odd = (uint8_t)tu_min16(wrap_bytes, data_stride - lin_odd); uint8_t buf_temp[4]; @@ -338,13 +338,13 @@ static void hwff_pull_n(const tu_fifo_t *f, void *app_buf, uint16_t n, uint16_t // Read full words from linear part const uint8_t data_stride = access_mode->data_stride; const uint32_t odd_mask = data_stride - 1; - uint16_t lin_even = lin_bytes & ~odd_mask; + uint16_t lin_even = (uint16_t)(lin_bytes & ~odd_mask); tu_hwfifo_write(hwfifo, ff_buf, lin_even, access_mode); HWFIFO_ADDR_NEXT_N(hwfifo, , lin_even * HWFIFO_ADDR_DATA_RATIO); ff_buf += lin_even; // There could be odd 1 byte (16bit) or 1-3 bytes (32bit) before the wrap-around boundary - const uint8_t lin_odd = lin_bytes & odd_mask; + const uint8_t lin_odd = (uint8_t)(lin_bytes & odd_mask); if (lin_odd > 0) { const uint8_t wrap_odd = (uint8_t)tu_min16(wrap_bytes, data_stride - lin_odd); diff --git a/src/common/tusb_types.h b/src/common/tusb_types.h index a18f9feb7..d8b6a8823 100644 --- a/src/common/tusb_types.h +++ b/src/common/tusb_types.h @@ -100,8 +100,8 @@ typedef enum { } tusb_xfer_type_t; typedef enum { - TUSB_DIR_OUT = 0, - TUSB_DIR_IN = 1, + TUSB_DIR_OUT = 0u, + TUSB_DIR_IN = 1u, TUSB_EPNUM_MASK = 0x0F, TUSB_DIR_IN_MASK = 0x80 diff --git a/src/portable/chipidea/ci_fs/dcd_ci_fs.c b/src/portable/chipidea/ci_fs/dcd_ci_fs.c index 312a98299..62df1a6d5 100644 --- a/src/portable/chipidea/ci_fs/dcd_ci_fs.c +++ b/src/portable/chipidea/ci_fs/dcd_ci_fs.c @@ -360,7 +360,7 @@ static bool edpt_open(uint8_t rhport, uint8_t ep_addr, uint16_t max_packet_size, unsigned val = USB_ENDPT_EPCTLDIS_MASK; val |= (xfer != TUSB_XFER_ISOCHRONOUS) ? USB_ENDPT_EPHSHK_MASK : 0; val |= dir ? USB_ENDPT_EPTXEN_MASK : USB_ENDPT_EPRXEN_MASK; - CI_REG->EP[epn].CTL |= val; + CI_REG->EP[epn].CTL |= (uint8_t)val; if (xfer != TUSB_XFER_ISOCHRONOUS) { bd[odd].dts = 1; @@ -434,11 +434,11 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t t buffer_descriptor_t *next = ep->odd ? bd - 1: bd + 1; /* When total_bytes is greater than the max packet size, * it prepares to the next transfer to avoid NAK in advance. */ - next->bc = total_bytes >= 2 * mps ? mps: total_bytes - mps; + next->bc = (total_bytes >= 2 * mps) ? mps : (total_bytes - mps); next->addr = buffer + mps; next->own = 1; } - bd->bc = total_bytes >= mps ? mps: total_bytes; + bd->bc = (total_bytes >= mps ? mps : total_bytes); bd->addr = buffer; __DSB(); bd->own = 1; /* This bit must be set last */ @@ -506,16 +506,16 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) //--------------------------------------------------------------------+ void dcd_int_handler(uint8_t rhport) { - uint32_t is = CI_REG->INT_STAT; - uint32_t msk = CI_REG->INT_EN; + uint8_t is = CI_REG->INT_STAT; + uint8_t msk = CI_REG->INT_EN; // clear non-enabled interrupts - CI_REG->INT_STAT = is & ~msk; + CI_REG->INT_STAT = (uint8_t)(is & ~msk); is &= msk; if (is & USB_ISTAT_ERROR_MASK) { /* TODO: */ - uint32_t es = CI_REG->ERR_STAT; + uint8_t es = CI_REG->ERR_STAT; CI_REG->ERR_STAT = es; CI_REG->INT_STAT = is; /* discard any pending events */ } diff --git a/src/portable/dialog/da146xx/dcd_da146xx.c b/src/portable/dialog/da146xx/dcd_da146xx.c index a283c8362..7d90b1f94 100644 --- a/src/portable/dialog/da146xx/dcd_da146xx.c +++ b/src/portable/dialog/da146xx/dcd_da146xx.c @@ -148,8 +148,8 @@ typedef struct #ifndef TU_DA146XX_DMA_RX_CHANNEL #define TU_DA146XX_DMA_RX_CHANNEL 6 #endif -#define DA146XX_DMA_USB_MUX (0x6 << (TU_DA146XX_DMA_RX_CHANNEL * 2)) -#define DA146XX_DMA_USB_MUX_MASK (0xF << (TU_DA146XX_DMA_RX_CHANNEL * 2)) +#define DA146XX_DMA_USB_MUX (0x6u << (TU_DA146XX_DMA_RX_CHANNEL * 2)) +#define DA146XX_DMA_USB_MUX_MASK (0xFu << (TU_DA146XX_DMA_RX_CHANNEL * 2)) typedef struct { @@ -311,12 +311,12 @@ static void fill_tx_fifo(xfer_ctl_t * xfer) // Max packet size is set to value greater then FIFO. Enable fifo level warning // to handle larger packets. regs->txc |= (3 << USB_USB_TXC1_REG_USB_TFWL_Pos); - USB->USB_FWMSK_REG |= 1 << (epnum - 1 + USB_USB_FWMSK_REG_USB_M_TXWARN31_Pos); + USB->USB_FWMSK_REG |= 1u << (epnum - 1 + USB_USB_FWMSK_REG_USB_M_TXWARN31_Pos); } else { regs->txc &= ~USB_USB_TXC1_REG_USB_TFWL_Msk; - USB->USB_FWMSK_REG &= ~(1 << (epnum - 1 + USB_USB_FWMSK_REG_USB_M_TXWARN31_Pos)); + USB->USB_FWMSK_REG &= ~(1u << (epnum - 1 + USB_USB_FWMSK_REG_USB_M_TXWARN31_Pos)); // Whole packet already in fifo, no need to refill it later. Mark last. regs->txc |= USB_USB_TXC1_REG_USB_LAST_Msk; } @@ -371,14 +371,14 @@ static void start_rx_packet(xfer_ctl_t *xfer) // For endpoint size greater than FIFO size enable FIFO level warning interrupt // when FIFO has less than 17 bytes free. regs->rxc |= USB_USB_RXC1_REG_USB_RFWL_Msk; - USB->USB_FWMSK_REG |= 1 << (epnum - 1 + USB_USB_FWMSK_REG_USB_M_RXWARN31_Pos); + USB->USB_FWMSK_REG |= 1u << (epnum - 1 + USB_USB_FWMSK_REG_USB_M_RXWARN31_Pos); } } else if (epnum != 0) { // If max_packet_size would fit in FIFO no need for FIFO level warning interrupt. regs->rxc &= ~USB_USB_RXC1_REG_USB_RFWL_Msk; - USB->USB_FWMSK_REG &= ~(1 << (epnum - 1 + USB_USB_FWMSK_REG_USB_M_RXWARN31_Pos)); + USB->USB_FWMSK_REG &= ~(1u << (epnum - 1 + USB_USB_FWMSK_REG_USB_M_RXWARN31_Pos)); } regs->rxc |= USB_USB_RXC1_REG_USB_RX_EN_Msk; } @@ -388,7 +388,7 @@ static void start_tx_dma(void *src, volatile void *dst, uint16_t size) // Setup SRC and DST registers TX_DMA_REGS->DMAx_A_START_REG = (uint32_t)src; TX_DMA_REGS->DMAx_B_START_REG = (uint32_t)dst; - // Interrupt not needed + // Interrupt is not needed TX_DMA_REGS->DMAx_INT_REG = size; TX_DMA_REGS->DMAx_LEN_REG = size - 1; TX_DMA_REGS->DMAx_CTRL_REG = TX_DMA_START; @@ -430,7 +430,9 @@ static uint16_t read_rx_fifo(xfer_ctl_t *xfer, uint16_t bytes_in_fifo) uint8_t *buf = xfer->buffer + xfer->transferred + xfer->last_packet_size; - for (int i = 0; i < receive_this_time; ++i) buf[i] = regs->rxd; + for (int i = 0; i < receive_this_time; ++i) { + buf[i] = (uint8_t)regs->rxd; + } xfer->last_packet_size += receive_this_time; @@ -449,7 +451,9 @@ static void handle_ep0_rx(void) { xfer_ctl_t *xfer_in = XFER_CTL_BASE(0, TUSB_DIR_IN); // Setup packet is in - for (int i = 0; i < fifo_bytes; ++i) _setup_packet[i] = USB->USB_RXD0_REG; + for (int i = 0; i < fifo_bytes; ++i) { + _setup_packet[i] = (uint8_t)USB->USB_RXD0_REG; + } xfer->stall = 0; xfer->data1 = 1; @@ -469,7 +473,7 @@ static void handle_ep0_rx(void) } else { - read_rx_fifo(xfer, fifo_bytes); + read_rx_fifo(xfer, (uint16_t)fifo_bytes); if (rxs0 & USB_USB_RXS0_REG_USB_RX_LAST_Msk) { xfer->transferred += xfer->last_packet_size; @@ -553,7 +557,7 @@ static void handle_epx_rx_ev(uint8_t ep) { // Disable DMA and update last_packet_size with what DMA reported. RX_DMA_REGS->DMAx_CTRL_REG &= ~DMA_DMA0_CTRL_REG_DMA_ON_Msk; - xfer->last_packet_size = RX_DMA_REGS->DMAx_IDX_REG; + xfer->last_packet_size = (uint16_t)RX_DMA_REGS->DMAx_IDX_REG; // When DMA did not finished (packet was smaller then MPS), DMAx_IDX_REG holds exact number of bytes transmitted. // When DMA finished value in DMAx_IDX_REG is one less then actual number of transmitted bytes. if (xfer->last_packet_size == RX_DMA_REGS->DMAx_LEN_REG) xfer->last_packet_size++; @@ -564,7 +568,7 @@ static void handle_epx_rx_ev(uint8_t ep) // FIFO maybe empty if DMA read it before or it's final iteration and function already read all that was to read. if (fifo_bytes > 0) { - fifo_bytes = read_rx_fifo(xfer, fifo_bytes); + fifo_bytes = read_rx_fifo(xfer, (uint16_t)fifo_bytes); } if (GET_BIT(rxs, USB_USB_RXS1_REG_USB_RX_LAST)) { @@ -624,7 +628,7 @@ static void handle_epx_tx_ev(xfer_ctl_t *xfer) { // Disable DMA and update last_packet_size with what DMA reported. TX_DMA_REGS->DMAx_CTRL_REG &= ~DMA_DMA1_CTRL_REG_DMA_ON_Msk; - xfer->last_packet_size = TX_DMA_REGS->DMAx_IDX_REG + 1; + xfer->last_packet_size = (uint16_t)(TX_DMA_REGS->DMAx_IDX_REG + 1); // Release DMA to used by other endpoints. _dcd.dma_ep[TUSB_DIR_IN] = 0; } @@ -954,13 +958,13 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt) if (dir == TUSB_DIR_OUT) { regs->epc_out = epnum | USB_USB_EPC1_REG_USB_EP_EN_Msk | iso_mask; - USB->USB_RXMSK_REG |= 0x11 << (epnum - 1); + USB->USB_RXMSK_REG |= 0x11u << (epnum - 1); REG_SET_BIT(USB_MAMSK_REG, USB_M_RX_EV); } else { regs->epc_in = epnum | USB_USB_EPC1_REG_USB_EP_EN_Msk | iso_mask; - USB->USB_TXMSK_REG |= 0x11 << (epnum - 1); + USB->USB_TXMSK_REG |= 0x11u << (epnum - 1); REG_SET_BIT(USB_MAMSK_REG, USB_M_TX_EV); } } @@ -974,8 +978,8 @@ void dcd_edpt_close_all (uint8_t rhport) for (int epnum = 1; epnum < EP_MAX; ++epnum) { - dcd_edpt_close(0, epnum | TUSB_DIR_OUT); - dcd_edpt_close(0, epnum | TUSB_DIR_IN); + dcd_edpt_close(0, (uint8_t)(epnum | TUSB_DIR_OUT)); + dcd_edpt_close(0, (uint8_t)(epnum | TUSB_DIR_IN)); } } @@ -1001,7 +1005,7 @@ void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) { regs->rxc = USB_USB_RXC1_REG_USB_FLUSH_Msk; regs->epc_out = 0; - USB->USB_RXMSK_REG &= ~(0x11 << (epnum - 1)); + USB->USB_RXMSK_REG &= ~(0x11u << (epnum - 1)); // Release DMA if needed if (_dcd.dma_ep[TUSB_DIR_OUT] == epnum) { @@ -1013,7 +1017,7 @@ void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) { regs->txc = USB_USB_TXC1_REG_USB_FLUSH_Msk; regs->epc_in = 0; - USB->USB_TXMSK_REG &= ~(0x11 << (epnum - 1)); + USB->USB_TXMSK_REG &= ~(0x11u << (epnum - 1)); // Release DMA if needed if (_dcd.dma_ep[TUSB_DIR_IN] == epnum) { diff --git a/src/portable/mentor/musb/dcd_musb.c b/src/portable/mentor/musb/dcd_musb.c index 339048473..3111f6185 100644 --- a/src/portable/mentor/musb/dcd_musb.c +++ b/src/portable/mentor/musb/dcd_musb.c @@ -111,8 +111,8 @@ TU_ATTR_ALWAYS_INLINE static inline void hwfifo_reset(musb_regs_t* musb, unsigne TU_ATTR_ALWAYS_INLINE static inline bool hwfifo_config(musb_regs_t* musb, unsigned epnum, unsigned is_rx, unsigned mps, bool double_packet) { (void) epnum; - uint8_t ffsize = hwfifo_byte2size(mps); - mps = 8 << ffsize; // round up to the next power of 2 + uint8_t ffsize = hwfifo_byte2size((uint16_t)mps); + mps = 8u << ffsize; // round up to the next power of 2 if (double_packet) { ffsize |= MUSB_FIFOSZ_DOUBLE_PACKET; @@ -120,7 +120,7 @@ TU_ATTR_ALWAYS_INLINE static inline bool hwfifo_config(musb_regs_t* musb, unsign } TU_ASSERT(alloced_fifo_bytes + mps <= MUSB_CFG_DYNAMIC_FIFO_SIZE); - musb->fifo_addr[is_rx] = alloced_fifo_bytes / 8; + musb->fifo_addr[is_rx] = (uint16_t)(alloced_fifo_bytes / 8); musb->fifo_size[is_rx] = ffsize; alloced_fifo_bytes += mps; @@ -157,12 +157,12 @@ TU_ATTR_ALWAYS_INLINE static inline bool hwfifo_config(musb_regs_t* musb, unsign // Flush FIFO and clear data toggle TU_ATTR_ALWAYS_INLINE static inline void hwfifo_flush(musb_regs_t* musb, unsigned epnum, unsigned is_rx, bool clear_dtog) { (void) epnum; - const uint8_t csrl_dtog = clear_dtog ? MUSB_CSRL_CLEAR_DATA_TOGGLE(is_rx) : 0; + const uint8_t csrl_dtog = clear_dtog ? (uint8_t)MUSB_CSRL_CLEAR_DATA_TOGGLE(is_rx) : 0; musb_ep_maxp_csr_t* maxp_csr = &musb->indexed_csr.maxp_csr[is_rx]; // may need to flush twice for double packet for (unsigned i=0; i<2; i++) { if (maxp_csr->csrl & MUSB_CSRL_PACKET_READY(is_rx)) { - maxp_csr->csrl = MUSB_CSRL_FLUSH_FIFO(is_rx) | csrl_dtog; + maxp_csr->csrl = (uint8_t)(MUSB_CSRL_FLUSH_FIFO(is_rx) | csrl_dtog); } } } @@ -180,7 +180,7 @@ static void process_setup_packet(uint8_t rhport) { dcd_event_setup_received(rhport, (const uint8_t*)(uintptr_t)&_dcd.setup_packet, true); const unsigned len = _dcd.setup_packet.wLength; - _dcd.remaining_ctrl = len; + _dcd.remaining_ctrl = (uint16_t)len; const unsigned dir_in = tu_edpt_dir(_dcd.setup_packet.bmRequestType); /* Clear RX FIFO and reverse the transaction direction */ if (len && dir_in) { @@ -441,14 +441,14 @@ static void process_edpt_n(uint8_t rhport, uint_fast8_t ep_addr) if (dir_in) { // TU_LOG1(" TX CSRL%d = %x\r\n", epn, ep_csr->tx_csrl); if (ep_csr->tx_csrl & MUSB_TXCSRL1_STALLED) { - ep_csr->tx_csrl &= ~(MUSB_TXCSRL1_STALLED | MUSB_TXCSRL1_UNDRN); + ep_csr->tx_csrl = (uint8_t)(ep_csr->tx_csrl & ~(MUSB_TXCSRL1_STALLED | MUSB_TXCSRL1_UNDRN)); return; } completed = handle_xfer_in(rhport, ep_addr); } else { // TU_LOG1(" RX CSRL%d = %x\r\n", epn, ep_csr->rx_csrl); if (ep_csr->rx_csrl & MUSB_RXCSRL1_STALLED) { - ep_csr->rx_csrl &= ~(MUSB_RXCSRL1_STALLED | MUSB_RXCSRL1_OVER); + ep_csr->rx_csrl = (uint8_t)(ep_csr->rx_csrl & ~(MUSB_RXCSRL1_STALLED | MUSB_RXCSRL1_OVER)); return; } completed = handle_xfer_out(rhport, ep_addr); @@ -778,7 +778,7 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) musb_ep_csr_t* ep_csr = get_ep_csr(musb_regs, epn); const uint8_t is_rx = 1 - tu_edpt_dir(ep_addr); - ep_csr->maxp_csr[is_rx].csrl = MUSB_CSRL_CLEAR_DATA_TOGGLE(is_rx); + ep_csr->maxp_csr[is_rx].csrl = (uint8_t)MUSB_CSRL_CLEAR_DATA_TOGGLE(is_rx); if (ie) musb_dcd_int_enable(rhport); } @@ -794,8 +794,8 @@ void dcd_int_handler(uint8_t rhport) { musb_dcd_int_handler_enter(rhport); uint_fast8_t intr_usb = musb_regs->intr_usb; // a read will clear this interrupt status - uint_fast8_t intr_tx = musb_regs->intr_tx; // a read will clear this interrupt status - uint_fast8_t intr_rx = musb_regs->intr_rx; // a read will clear this interrupt status + uint_fast16_t intr_tx = musb_regs->intr_tx; // a read will clear this interrupt status + uint_fast16_t intr_rx = musb_regs->intr_rx; // a read will clear this interrupt status // TU_LOG1("D%2x T%2x R%2x\r\n", is, txis, rxis); intr_usb &= musb_regs->intr_usben; /* Clear disabled interrupts */ diff --git a/src/portable/mentor/musb/musb_type.h b/src/portable/mentor/musb/musb_type.h index b2f6492fa..176504a2f 100644 --- a/src/portable/mentor/musb/musb_type.h +++ b/src/portable/mentor/musb/musb_type.h @@ -300,7 +300,7 @@ TU_VERIFY_STATIC(sizeof(musb_regs_t) == 0x350, "size is not correct"); // Helper //--------------------------------------------------------------------+ TU_ATTR_ALWAYS_INLINE static inline musb_ep_csr_t* get_ep_csr(musb_regs_t* musb_regs, unsigned epnum) { - musb_regs->index = epnum; + musb_regs->index = (uint8_t)epnum; return &musb_regs->indexed_csr; } diff --git a/src/portable/microchip/samg/dcd_samg.c b/src/portable/microchip/samg/dcd_samg.c index 4115eecc5..f8980b775 100644 --- a/src/portable/microchip/samg/dcd_samg.c +++ b/src/portable/microchip/samg/dcd_samg.c @@ -352,8 +352,8 @@ void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr) csr_clear(epnum, UDP_CSR_FORCESTALL_Msk); // must also reset EP to clear data toggle - UDP->UDP_RST_EP |= (1 << epnum); - UDP->UDP_RST_EP &= ~(1 << epnum); + UDP->UDP_RST_EP |= (1u << epnum); + UDP->UDP_RST_EP &= ~(1u << epnum); } //--------------------------------------------------------------------+ diff --git a/src/portable/nordic/nrf5x/dcd_nrf5x.c b/src/portable/nordic/nrf5x/dcd_nrf5x.c index 8a41c4790..6ed5fde8e 100644 --- a/src/portable/nordic/nrf5x/dcd_nrf5x.c +++ b/src/portable/nordic/nrf5x/dcd_nrf5x.c @@ -36,6 +36,8 @@ #pragma GCC diagnostic ignored "-Wcast-qual" #pragma GCC diagnostic ignored "-Wcast-align" #pragma GCC diagnostic ignored "-Wunused-parameter" +#pragma GCC diagnostic ignored "-Wconversion" +#pragma GCC diagnostic ignored "-Wsign-conversion" #endif #include "nrf.h" @@ -461,7 +463,7 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t to xfer->actual_len = 0; // Control endpoint with zero-length packet and opposite direction to 1st request byte --> status stage - bool const control_status = (epnum == 0 && total_bytes == 0 && dir != tu_edpt_dir(NRF_USBD->BMREQUESTTYPE)); + bool const control_status = (epnum == 0 && total_bytes == 0 && dir != tu_edpt_dir((uint8_t)NRF_USBD->BMREQUESTTYPE)); if (control_status) { // The nRF doesn't interrupt on status transmit so we queue up a success response. diff --git a/src/portable/nuvoton/nuc120/dcd_nuc120.c b/src/portable/nuvoton/nuc120/dcd_nuc120.c index d9a0e3fa8..2edb1bc7a 100644 --- a/src/portable/nuvoton/nuc120/dcd_nuc120.c +++ b/src/portable/nuvoton/nuc120/dcd_nuc120.c @@ -253,13 +253,13 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) /* mine the data for the information we need */ int const dir = tu_edpt_dir(p_endpoint_desc->bEndpointAddress); - int const size = tu_edpt_packet_size(p_endpoint_desc); + uint16_t const size = tu_edpt_packet_size(p_endpoint_desc); tusb_xfer_type_t const type = (tusb_xfer_type_t) p_endpoint_desc->bmAttributes.xfer; struct xfer_ctl_t *xfer = &xfer_table[ep - USBD->EP]; /* allocate buffer from USB RAM */ ep->BUFSEG = bufseg_addr; - bufseg_addr += size; + bufseg_addr += (uint32_t)size; TU_ASSERT(bufseg_addr <= USBD_BUF_SIZE); /* construct USB Configuration Register value and then write it */ @@ -435,7 +435,7 @@ void dcd_int_handler(uint8_t rhport) /* given ACK from host has happened, we can now set the address (if not already done) */ if((USBD->FADDR != assigned_address) && (USBD->FADDR == 0)) USBD->FADDR = assigned_address; - uint16_t const available_bytes = USBD->EP[PERIPH_EP0].MXPLD; + uint16_t const available_bytes = (uint16_t)USBD->EP[PERIPH_EP0].MXPLD; active_ep0_xfer = (available_bytes == xfer_table[PERIPH_EP0].max_packet_size); @@ -453,7 +453,7 @@ void dcd_int_handler(uint8_t rhport) { USBD->INTSTS = mask; - uint16_t const available_bytes = ep->MXPLD; + uint16_t const available_bytes = (uint16_t)ep->MXPLD; uint8_t const ep_addr = decode_ep_addr(ep); bool const out_ep = !(ep_addr & TUSB_DIR_IN_MASK); diff --git a/src/portable/nuvoton/nuc121/dcd_nuc121.c b/src/portable/nuvoton/nuc121/dcd_nuc121.c index 42fb58a0a..008c9df6b 100644 --- a/src/portable/nuvoton/nuc121/dcd_nuc121.c +++ b/src/portable/nuvoton/nuc121/dcd_nuc121.c @@ -42,6 +42,8 @@ #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wredundant-decls" +#pragma GCC diagnostic ignored "-Wconversion" +#pragma GCC diagnostic ignored "-Wsign-conversion" #endif #include "NuMicro.h" @@ -291,7 +293,7 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) /* mine the data for the information we need */ int const dir = tu_edpt_dir(p_endpoint_desc->bEndpointAddress); - int const size = tu_edpt_packet_size(p_endpoint_desc); + uint16_t const size = tu_edpt_packet_size(p_endpoint_desc); tusb_xfer_type_t const type = (tusb_xfer_type_t) p_endpoint_desc->bmAttributes.xfer; struct xfer_ctl_t *xfer = &xfer_table[ep - USBD->EP]; @@ -478,7 +480,7 @@ void dcd_int_handler(uint8_t rhport) { if (status & USBD_INTSTS_EPEVT0_Msk) /* PERIPH_EP0 (EP0_IN) event: this is treated separately from the rest */ { - uint16_t const available_bytes = USBD->EP[PERIPH_EP0].MXPLD; + uint16_t const available_bytes = (uint16_t)USBD->EP[PERIPH_EP0].MXPLD; active_ep0_xfer = (available_bytes == xfer_table[PERIPH_EP0].max_packet_size); @@ -496,7 +498,7 @@ void dcd_int_handler(uint8_t rhport) { USBD->INTSTS = mask; - uint16_t const available_bytes = ep->MXPLD; + uint16_t const available_bytes = (uint16_t)ep->MXPLD; uint8_t const ep_addr = decode_ep_addr(ep); bool const out_ep = !(ep_addr & TUSB_DIR_IN_MASK); diff --git a/src/portable/nuvoton/nuc505/dcd_nuc505.c b/src/portable/nuvoton/nuc505/dcd_nuc505.c index ca17d6251..a0f3d4c3f 100644 --- a/src/portable/nuvoton/nuc505/dcd_nuc505.c +++ b/src/portable/nuvoton/nuc505/dcd_nuc505.c @@ -42,6 +42,8 @@ #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wredundant-decls" +#pragma GCC diagnostic ignored "-Wconversion" +#pragma GCC diagnostic ignored "-Wsign-conversion" #endif #include "NUC505Series.h" @@ -338,13 +340,13 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc) /* mine the data for the information we need */ int const dir = tu_edpt_dir(p_endpoint_desc->bEndpointAddress); - int const size = tu_edpt_packet_size(p_endpoint_desc); + uint16_t const size = tu_edpt_packet_size(p_endpoint_desc); tusb_xfer_type_t const type = p_endpoint_desc->bmAttributes.xfer; struct xfer_ctl_t *xfer = &xfer_table[ep - USBD->EP]; /* allocate buffer from USB RAM */ ep->EPBUFSTART = bufseg_addr; - bufseg_addr += size; + bufseg_addr += (uint32_t)size; ep->EPBUFEND = bufseg_addr - 1; TU_ASSERT(bufseg_addr <= USBD_BUF_SIZE); diff --git a/src/portable/nxp/lpc17_40/dcd_lpc17_40.c b/src/portable/nxp/lpc17_40/dcd_lpc17_40.c index 349229c8d..2840c6d5e 100644 --- a/src/portable/nxp/lpc17_40/dcd_lpc17_40.c +++ b/src/portable/nxp/lpc17_40/dcd_lpc17_40.c @@ -131,7 +131,7 @@ static uint8_t sie_read (uint8_t cmd_code) //--------------------------------------------------------------------+ static inline uint8_t ep_addr2idx(uint8_t ep_addr) { - return 2*(ep_addr & 0x0F) + ((ep_addr & TUSB_DIR_IN_MASK) ? 1 : 0); + return (uint8_t)(2*(ep_addr & 0x0F) + ((ep_addr & TUSB_DIR_IN_MASK) ? 1 : 0)); } static void set_ep_size(uint8_t ep_id, uint16_t max_packet_size) @@ -243,7 +243,7 @@ void dcd_sof_enable(uint8_t rhport, bool en) //--------------------------------------------------------------------+ static inline uint8_t byte2dword(uint8_t bytes) { - return (bytes + 3) / 4; // length in dwords + return (uint8_t)((bytes + 3) / 4); // length in dwords } static void control_ep_write(void const * buffer, uint8_t len) diff --git a/src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c b/src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c index 5f4a441dc..8adf0f840 100644 --- a/src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c +++ b/src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c @@ -243,7 +243,7 @@ TU_ATTR_ALWAYS_INLINE static inline uint16_t get_buf_offset(void const * buffer) } TU_ATTR_ALWAYS_INLINE static inline uint8_t ep_addr2id(uint8_t ep_addr) { - return 2*(ep_addr & 0x0F) + ((ep_addr & TUSB_DIR_IN_MASK) ? 1 : 0); + return (uint8_t)(2*(ep_addr & 0x0F) + ((ep_addr & TUSB_DIR_IN_MASK) ? 1 : 0)); } TU_ATTR_ALWAYS_INLINE static inline bool ep_is_iso(ep_cmd_sts_t* ep_cs, bool is_highspeed) { @@ -539,8 +539,8 @@ static void process_xfer_isr(uint8_t rhport, uint32_t int_status) { uint16_t buf_nbytes; if ( rhport_is_highspeed(rhport) ) { - buf_offset = ep_cs->buffer_hs.offset; - buf_nbytes = ep_cs->buffer_hs.nbytes; + buf_offset = (uint16_t)ep_cs->buffer_hs.offset; + buf_nbytes = (uint16_t)ep_cs->buffer_hs.nbytes; #if TU_CHECK_MCU(OPT_MCU_LPC54) // LPC54 Errata USB.2: In USB high-speed device mode, the NBytes field is not correct after BULK IN transfer @@ -550,8 +550,8 @@ static void process_xfer_isr(uint8_t rhport, uint32_t int_status) { } #endif } else { - buf_offset = ep_cs->buffer_fs.offset; - buf_nbytes = ep_cs->buffer_fs.nbytes; + buf_offset = (uint16_t)ep_cs->buffer_fs.offset; + buf_nbytes = (uint16_t)ep_cs->buffer_fs.nbytes; } xfer_dma->xferred_bytes += xfer_dma->nbytes - buf_nbytes; diff --git a/src/portable/renesas/rusb2/dcd_rusb2.c b/src/portable/renesas/rusb2/dcd_rusb2.c index e2a51a5ca..adbb53787 100644 --- a/src/portable/renesas/rusb2/dcd_rusb2.c +++ b/src/portable/renesas/rusb2/dcd_rusb2.c @@ -93,7 +93,9 @@ static unsigned find_pipe(unsigned xfer_type) { const uint8_t idx_last = pipe_idx_arr[xfer_type][1]; for (int i = idx_last; i >= idx_first; i--) { - if (0 == _dcd.pipe[i].ep) return i; + if (0 == _dcd.pipe[i].ep) { + return (unsigned)i; + } } return 0; @@ -117,10 +119,10 @@ static volatile reg_pipetre_t* get_pipetre(rusb2_reg_t *rusb, unsigned num) { static volatile uint16_t* ep_addr_to_pipectr(uint8_t rhport, unsigned ep_addr) { rusb2_reg_t *rusb = RUSB2_REG(rhport); - const unsigned epn = tu_edpt_number(ep_addr); + const unsigned epn = tu_edpt_number((uint8_t)ep_addr); if (epn) { - const unsigned dir = tu_edpt_dir(ep_addr); + const unsigned dir = tu_edpt_dir((uint8_t)ep_addr); const unsigned num = _dcd.ep[dir][epn]; return get_pipectr(rusb, num); } else { @@ -129,11 +131,11 @@ static volatile uint16_t* ep_addr_to_pipectr(uint8_t rhport, unsigned ep_addr) { } static uint16_t edpt0_max_packet_size(rusb2_reg_t* rusb) { - return rusb->DCPMAXP_b.MXPS; + return (uint16_t)rusb->DCPMAXP_b.MXPS; } static uint16_t edpt_max_packet_size(rusb2_reg_t *rusb, unsigned num) { - rusb->PIPESEL = num; + rusb->PIPESEL = (uint16_t)num; return rusb->PIPEMAXP; } @@ -285,7 +287,7 @@ static bool pipe_xfer_out(rusb2_reg_t* rusb, unsigned num) const uint16_t mps = edpt_max_packet_size(rusb, num); pipe_wait_for_ready(rusb, num); - const uint16_t vld = rusb->D0FIFOCTR_b.DTLN; + const uint16_t vld = (uint16_t)rusb->D0FIFOCTR_b.DTLN; const uint16_t len = tu_min16(tu_min16(rem, mps), vld); void *buf = pipe->buf; @@ -498,7 +500,7 @@ static void process_bus_reset(uint8_t rhport) volatile uint16_t *ctr = (volatile uint16_t*)((uintptr_t) (&rusb->PIPE_CTR[0])); volatile uint16_t *tre = (volatile uint16_t*)((uintptr_t) (&rusb->PIPE_TR[0].E)); - for (int i = 1; i <= 5; ++i) { + for (uint16_t i = 1; i <= 5; ++i) { rusb->PIPESEL = i; rusb->PIPECFG = 0; *ctr = RUSB2_PIPE_CTR_ACLRM_Msk; @@ -508,7 +510,7 @@ static void process_bus_reset(uint8_t rhport) tre += 2; } - for (int i = 6; i <= 9; ++i) { + for (uint16_t i = 6; i <= 9; ++i) { rusb->PIPESEL = i; rusb->PIPECFG = 0; *ctr = RUSB2_PIPE_CTR_ACLRM_Msk; @@ -542,7 +544,7 @@ static void process_bus_reset(uint8_t rhport) static void process_set_address(uint8_t rhport) { rusb2_reg_t* rusb = RUSB2_REG(rhport); - const uint16_t addr = rusb->USBADDR_b.USBADDR; + const uint16_t addr = (uint16_t)rusb->USBADDR_b.USBADDR; if (!addr) { return; } @@ -706,7 +708,7 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * ep_desc) (void)rhport; rusb2_reg_t * rusb = RUSB2_REG(rhport); - const unsigned ep_addr = ep_desc->bEndpointAddress; + const uint8_t ep_addr = ep_desc->bEndpointAddress; const unsigned epn = tu_edpt_number(ep_addr); const unsigned dir = tu_edpt_dir(ep_addr); const unsigned xfer = ep_desc->bmAttributes.xfer; @@ -770,8 +772,10 @@ void dcd_edpt_close_all(uint8_t rhport) dcd_int_disable(rhport); while (--i) { /* Close all pipes except 0 */ const unsigned ep_addr = _dcd.pipe[i].ep; - if (!ep_addr) continue; - dcd_edpt_close(rhport, ep_addr); + if (!ep_addr) { + continue; + } + dcd_edpt_close(rhport, (uint8_t)ep_addr); } dcd_int_enable(rhport); } @@ -783,10 +787,10 @@ void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) const unsigned dir = tu_edpt_dir(ep_addr); const unsigned num = _dcd.ep[dir][epn]; - rusb->BRDYENB &= ~TU_BIT(num); + rusb->BRDYENB &= (uint16_t)~TU_BIT(num); volatile uint16_t *ctr = get_pipectr(rusb, num); *ctr = 0; - rusb->PIPESEL = num; + rusb->PIPESEL = (uint16_t)num; rusb->PIPECFG = 0; _dcd.pipe[num].ep = 0; _dcd.ep[dir][epn] = 0; @@ -860,7 +864,7 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) *ctr = RUSB2_PIPE_CTR_PID_BUF; } else { const unsigned num = _dcd.ep[0][tu_edpt_number(ep_addr)]; - rusb->PIPESEL = num; + rusb->PIPESEL = (uint16_t)num; if (rusb->PIPECFG_b.TYPE != 1) { *ctr = RUSB2_PIPE_CTR_PID_BUF; } diff --git a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c index 8b4719b21..135b4e8f6 100644 --- a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c +++ b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c @@ -259,7 +259,7 @@ static void handle_ctr_tx(uint32_t ep_id) { } 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); } @@ -267,7 +267,7 @@ 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); + uint16_t rx_addr = (uint16_t)btable_get_addr(ep_id, BTABLE_BUF_RX); uint8_t setup_packet[8] TU_ATTR_ALIGNED(4); tu_hwfifo_read(PMA_BUF_AT(rx_addr), setup_packet, rx_count, NULL); @@ -531,8 +531,8 @@ void edpt0_open(uint8_t rhport) { xfer_status[0][1].max_packet_size = CFG_TUD_ENDPOINT0_SIZE; 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); @@ -574,7 +574,7 @@ bool dcd_edpt_open(uint8_t rhport, const tusb_desc_endpoint_t *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); @@ -624,17 +624,17 @@ bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet #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; + 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 = pma_addr; + uint16_t pma_addr2 = (uint16_t)pma_addr; #endif #if FSDEV_USE_SBUF_ISO == 0 - btable_set_addr(ep_idx, 0, pma_addr); + 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); + btable_set_addr(ep_idx, dir == TUSB_DIR_IN ? BTABLE_BUF_TX : BTABLE_BUF_RX, (uint16_t)pma_addr); (void)pma_addr2; #endif diff --git a/src/portable/st/stm32_fsdev/fsdev_common.c b/src/portable/st/stm32_fsdev/fsdev_common.c index 003bcd069..7c4572a1e 100644 --- a/src/portable/st/stm32_fsdev/fsdev_common.c +++ b/src/portable/st/stm32_fsdev/fsdev_common.c @@ -81,11 +81,11 @@ uint16_t pma_align_buffer_size(uint16_t size, uint8_t* blsize, uint8_t* num_bloc if (size > 62) { block_in_bytes = 32; *blsize = 1; - *num_block = tu_div_ceil(size, 32); + *num_block = (uint8_t)tu_div_ceil(size, 32); } else { block_in_bytes = 2; *blsize = 0; - *num_block = tu_div_ceil(size, 2); + *num_block = (uint8_t)tu_div_ceil(size, 2); } return (*num_block) * block_in_bytes; diff --git a/src/portable/st/stm32_fsdev/fsdev_common.h b/src/portable/st/stm32_fsdev/fsdev_common.h index 140ff1d61..af84b8b97 100644 --- a/src/portable/st/stm32_fsdev/fsdev_common.h +++ b/src/portable/st/stm32_fsdev/fsdev_common.h @@ -345,7 +345,7 @@ TU_ATTR_ALWAYS_INLINE static inline void ep_write_clear_ctr(uint32_t ep_id, tusb uint32_t reg = FSDEV_REG->ep[ep_id].reg; reg |= U_EP_CTR_TX | U_EP_CTR_RX; reg &= U_EPREG_MASK; - reg &= ~(1 << (U_EP_CTR_TX_Pos + (dir == TUSB_DIR_IN ? 0 : 8))); + reg &= ~(1u << (U_EP_CTR_TX_Pos + (dir == TUSB_DIR_IN ? 0u : 8u))); ep_write(ep_id, reg, false); } @@ -378,7 +378,7 @@ TU_ATTR_ALWAYS_INLINE static inline void ch_write_clear_ctr(uint32_t ch_id, tusb uint32_t reg = FSDEV_REG->ep[ch_id].reg; reg |= U_EP_CTR_TX | U_EP_CTR_RX; reg &= U_EPREG_MASK; - reg &= ~(1 << (U_EP_CTR_TX_Pos + (dir == TUSB_DIR_IN ? 8 : 0))); + reg &= ~(1u << (U_EP_CTR_TX_Pos + (dir == TUSB_DIR_IN ? 8u : 0u))); ep_write(ch_id, reg, false); } diff --git a/src/portable/synopsys/dwc2/dcd_dwc2.c b/src/portable/synopsys/dwc2/dcd_dwc2.c index 9a9c734a0..2e5a56d08 100644 --- a/src/portable/synopsys/dwc2/dcd_dwc2.c +++ b/src/portable/synopsys/dwc2/dcd_dwc2.c @@ -191,7 +191,7 @@ static void dma_setup_prepare(uint8_t rhport) { */ TU_ATTR_ALWAYS_INLINE static inline uint16_t calc_device_grxfsiz(uint16_t largest_ep_size, uint8_t ep_count) { - return 13 + 1 + 2 * ((largest_ep_size / 4) + 1) + 2 * ep_count; + return (uint16_t)(13 + 1 + 2 * ((largest_ep_size / 4) + 1) + 2 * ep_count); } static bool dfifo_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t packet_size, bool is_bulk) { @@ -203,7 +203,7 @@ static bool dfifo_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t packet_size, b TU_ASSERT(epnum < ep_count); - uint16_t fifo_size = tu_div_ceil(packet_size, 4); + uint16_t fifo_size = (uint16_t)tu_div_ceil(packet_size, 4); if (dir == TUSB_DIR_OUT) { // Calculate required size of RX FIFO const uint16_t new_sz = calc_device_grxfsiz(4 * fifo_size, ep_count); @@ -371,7 +371,7 @@ static void edpt_schedule_packets(uint8_t rhport, const uint8_t epnum, const uin num_packets = 1; } else { total_bytes = xfer->total_len; - num_packets = tu_div_ceil(total_bytes, xfer->max_size); + num_packets = (uint16_t)tu_div_ceil(total_bytes, xfer->max_size); if (num_packets == 0) { num_packets = 1; // zero length packet still count as 1 } -- cgit v1.3.1 From 269589df064eb79998c15148d60e987302cfe96a Mon Sep 17 00:00:00 2001 From: Andrew Leech Date: Fri, 17 Apr 2026 20:01:53 +1000 Subject: tinyusb.mk: Remove duplicate usbc.c entry. Signed-off-by: Andrew Leech --- src/tinyusb.mk | 1 - 1 file changed, 1 deletion(-) (limited to 'src') diff --git a/src/tinyusb.mk b/src/tinyusb.mk index e7d1c0b5b..169098016 100644 --- a/src/tinyusb.mk +++ b/src/tinyusb.mk @@ -26,4 +26,3 @@ TINYUSB_SRC_C += \ src/class/midi/midi_host.c \ src/class/msc/msc_host.c \ src/class/vendor/vendor_host.c \ - src/typec/usbc.c \ -- cgit v1.3.1 From 5939831f17272571911d089508b458f496a4cc62 Mon Sep 17 00:00:00 2001 From: Fan DANG Date: Fri, 17 Apr 2026 18:39:56 +0800 Subject: remove duplicated tu_le16toh since we have converted the endian when setup. --- examples/device/audio_test_multi_rate/src/main.c | 2 +- examples/device/cdc_uac2/src/uac2_app.c | 8 ++++---- examples/device/uac2_headset/src/main.c | 8 ++++---- examples/device/uac2_speaker_fb/src/main.c | 8 ++++---- src/class/mtp/mtp_device.c | 2 +- src/common/tusb_compiler.h | 2 ++ src/device/usbd.c | 2 +- 7 files changed, 17 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/examples/device/audio_test_multi_rate/src/main.c b/examples/device/audio_test_multi_rate/src/main.c index 952176997..a86beb415 100644 --- a/examples/device/audio_test_multi_rate/src/main.c +++ b/examples/device/audio_test_multi_rate/src/main.c @@ -532,7 +532,7 @@ static bool audio20_get_req_entity(uint8_t rhport, tusb_control_request_t const bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request) { (void) rhport; //uint8_t const itf = tu_u16_low(tu_le16toh(p_request->wIndex)); - uint8_t const alt = tu_u16_low(tu_le16toh(p_request->wValue)); + uint8_t const alt = tu_u16_low(p_request->wValue); // Clear buffer when streaming format is changed if (alt != 0) { diff --git a/examples/device/cdc_uac2/src/uac2_app.c b/examples/device/cdc_uac2/src/uac2_app.c index 7760c402b..6e9d1d9e3 100644 --- a/examples/device/cdc_uac2/src/uac2_app.c +++ b/examples/device/cdc_uac2/src/uac2_app.c @@ -263,8 +263,8 @@ bool tud_audio_set_itf_close_ep_cb(uint8_t rhport, tusb_control_request_t const { (void)rhport; - uint8_t const itf = tu_u16_low(tu_le16toh(p_request->wIndex)); - uint8_t const alt = tu_u16_low(tu_le16toh(p_request->wValue)); + uint8_t const itf = tu_u16_low(p_request->wIndex); + uint8_t const alt = tu_u16_low(p_request->wValue); if (ITF_NUM_AUDIO_STREAMING_SPK == itf && alt == 0) { // Audio streaming stop @@ -277,8 +277,8 @@ bool tud_audio_set_itf_close_ep_cb(uint8_t rhport, tusb_control_request_t const bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request) { (void)rhport; - uint8_t const itf = tu_u16_low(tu_le16toh(p_request->wIndex)); - uint8_t const alt = tu_u16_low(tu_le16toh(p_request->wValue)); + uint8_t const itf = tu_u16_low(p_request->wIndex); + uint8_t const alt = tu_u16_low(p_request->wValue); TU_LOG2("Set interface %d alt %d\r\n", itf, alt); if (ITF_NUM_AUDIO_STREAMING_SPK == itf && alt != 0) { diff --git a/examples/device/uac2_headset/src/main.c b/examples/device/uac2_headset/src/main.c index 0ea63d8f7..779e927bc 100644 --- a/examples/device/uac2_headset/src/main.c +++ b/examples/device/uac2_headset/src/main.c @@ -522,8 +522,8 @@ bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p bool tud_audio_set_itf_close_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request) { (void) rhport; - uint8_t const itf = tu_u16_low(tu_le16toh(p_request->wIndex)); - uint8_t const alt = tu_u16_low(tu_le16toh(p_request->wValue)); + uint8_t const itf = tu_u16_low(p_request->wIndex); + uint8_t const alt = tu_u16_low(p_request->wValue); if (ITF_NUM_AUDIO_STREAMING_SPK == itf && alt == 0) { blink_interval_ms = BLINK_MOUNTED; @@ -534,8 +534,8 @@ bool tud_audio_set_itf_close_ep_cb(uint8_t rhport, tusb_control_request_t const bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request) { (void) rhport; - uint8_t const itf = tu_u16_low(tu_le16toh(p_request->wIndex)); - uint8_t const alt = tu_u16_low(tu_le16toh(p_request->wValue)); + uint8_t const itf = tu_u16_low(p_request->wIndex); + uint8_t const alt = tu_u16_low(p_request->wValue); TU_LOG2("Set interface %d alt %d\r\n", itf, alt); if (ITF_NUM_AUDIO_STREAMING_SPK == itf && alt != 0) { diff --git a/examples/device/uac2_speaker_fb/src/main.c b/examples/device/uac2_speaker_fb/src/main.c index c3e97bb28..402642162 100644 --- a/examples/device/uac2_speaker_fb/src/main.c +++ b/examples/device/uac2_speaker_fb/src/main.c @@ -457,8 +457,8 @@ static bool audio20_set_req_entity(tusb_control_request_t const *p_request, uint bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request) { (void) rhport; - uint8_t const itf = tu_u16_low(tu_le16toh(p_request->wIndex)); - uint8_t const alt = tu_u16_low(tu_le16toh(p_request->wValue)); + uint8_t const itf = tu_u16_low(p_request->wIndex); + uint8_t const alt = tu_u16_low(p_request->wValue); TU_LOG2("Set interface %d alt %d\r\n", itf, alt); if (ITF_NUM_AUDIO_STREAMING == itf && alt != 0) @@ -531,8 +531,8 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p bool tud_audio_set_itf_close_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request) { (void) rhport; - uint8_t const itf = tu_u16_low(tu_le16toh(p_request->wIndex)); - uint8_t const alt = tu_u16_low(tu_le16toh(p_request->wValue)); + uint8_t const itf = tu_u16_low(p_request->wIndex); + uint8_t const alt = tu_u16_low(p_request->wValue); if (ITF_NUM_AUDIO_STREAMING == itf && alt == 0) { blink_interval_ms = BLINK_MOUNTED; diff --git a/src/class/mtp/mtp_device.c b/src/class/mtp/mtp_device.c index 59096e476..0da984f4a 100644 --- a/src/class/mtp/mtp_device.c +++ b/src/class/mtp/mtp_device.c @@ -321,7 +321,7 @@ bool mtpd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t .session_id = p_mtp->session_id, .request = request, .buf = p_mtp->control_buf, - .bufsize = tu_le16toh(request->wLength), + .bufsize = request->wLength, }; switch (request->bRequest) { diff --git a/src/common/tusb_compiler.h b/src/common/tusb_compiler.h index 4ed14dcfb..a8971c3df 100644 --- a/src/common/tusb_compiler.h +++ b/src/common/tusb_compiler.h @@ -246,8 +246,10 @@ // Endian conversion use well-known host to network (big endian) naming #if defined(__LIT) #define TU_BYTE_ORDER TU_LITTLE_ENDIAN + #define TU_BITFIELD_ORDER TU_BITFIELD_LE #else #define TU_BYTE_ORDER TU_BIG_ENDIAN + #define TU_BITFIELD_ORDER TU_BITFIELD_BE #endif #define TU_BSWAP16(u16) ((unsigned short)_builtin_revw((unsigned long)u16)) diff --git a/src/device/usbd.c b/src/device/usbd.c index 3c14175f6..da0ffb4c6 100644 --- a/src/device/usbd.c +++ b/src/device/usbd.c @@ -1212,7 +1212,7 @@ static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const TU_LOG_USBD(" String[%u]\r\n", desc_index); // String Descriptor always uses the desc set from user - uint8_t const* desc_str = (uint8_t const*) tud_descriptor_string_cb(desc_index, tu_le16toh(p_request->wIndex)); + uint8_t const* desc_str = (uint8_t const*) tud_descriptor_string_cb(desc_index, p_request->wIndex); TU_VERIFY(desc_str); // first byte of descriptor is its size -- cgit v1.3.1 From 5540b2a83fb83dd7f1a477e99084b1818abc2e46 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 17 Apr 2026 19:06:56 +0700 Subject: use single volatile counter --- src/portable/st/stm32_fsdev/fsdev_stm32.h | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/portable/st/stm32_fsdev/fsdev_stm32.h b/src/portable/st/stm32_fsdev/fsdev_stm32.h index 79052b489..070aa00ec 100644 --- a/src/portable/st/stm32_fsdev/fsdev_stm32.h +++ b/src/portable/st/stm32_fsdev/fsdev_stm32.h @@ -256,10 +256,14 @@ TU_ATTR_ALWAYS_INLINE static inline void fsdev_int_disable(uint8_t rhport) { //--------------------------------------------------------------------+ #ifdef CFG_TUSB_FSDEV_32BIT -// ES0561 (STM32H503), ES0587 (STM32U535/U545) -// CTR may trigger before final PMA SRAM accesses complete on OUT transfers. -// Insert delay before reading PMA count/data. -// Max CPU frequency in MHz, used to derive conservative FSDEV PMA delay defaults. +/* 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 MHz, used to derive conservative FSDEV PMA delay defaults. + */ #if CFG_TUSB_MCU == OPT_MCU_STM32H5 #define FSDEV_STM32_CPU_MHZ 250U #elif CFG_TUSB_MCU == OPT_MCU_STM32U5 @@ -283,10 +287,9 @@ TU_ATTR_ALWAYS_INLINE static inline void fsdev_int_disable(uint8_t rhport) { #endif TU_ATTR_ALWAYS_INLINE static inline void fsdev_btable_workaround_delay(bool low_speed) { - uint32_t cycle_count = low_speed ? CFG_TUSB_FSDEV_BTABLE_LS_DELAY_COUNT : CFG_TUSB_FSDEV_BTABLE_FS_DELAY_COUNT; - volatile uint32_t delay_count = cycle_count; - while (delay_count > 0U) { - delay_count--; + 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 -- cgit v1.3.1 From ce81b01eda0f9e833bbb717e6f2cebad0030afa1 Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 20 Apr 2026 16:44:25 +0700 Subject: improving transfer tracking and adding support for un-armed Rx data handling --- src/portable/mentor/musb/dcd_musb.c | 88 ++++++++++++++++++++++++------------- test/hil/tinyusb.json | 3 ++ 2 files changed, 61 insertions(+), 30 deletions(-) (limited to 'src') diff --git a/src/portable/mentor/musb/dcd_musb.c b/src/portable/mentor/musb/dcd_musb.c index 64f9ebacf..ad3838a09 100644 --- a/src/portable/mentor/musb/dcd_musb.c +++ b/src/portable/mentor/musb/dcd_musb.c @@ -58,15 +58,18 @@ typedef union { volatile uint32_t u32; } hw_fifo_t; -typedef struct TU_ATTR_PACKED -{ - void *buf; /* the start address of a transfer data buffer */ +typedef struct { + union { + uint8_t *buf; /* the start address of a transfer data buffer */ + tu_fifo_t *fifo; + }; uint16_t length; /* the number of bytes in the buffer */ uint16_t remaining; /* the number of bytes remaining in the buffer */ + bool armed; /* true while a transfer is posted */ + bool use_fifo; /* true: buf is tu_fifo_t*; false: buf is plain byte pointer. */ } pipe_state_t; -typedef struct -{ +typedef struct { union { tusb_control_request_t setup_packet; uint32_t setup_buffer[2]; @@ -75,7 +78,6 @@ typedef struct int8_t status_out; pipe_state_t pipe0; pipe_state_t pipe[2][TUP_DCD_ENDPOINT_MAX-1]; /* pipe[direction][endpoint number - 1] */ - uint16_t pipe_buf_is_fifo[2]; /* Bitmap. Each bit means whether 1:TU_FIFO or 0:POD. */ } dcd_data_t; static dcd_data_t _dcd; @@ -197,6 +199,7 @@ static bool handle_xfer_in(uint8_t rhport, uint_fast8_t ep_addr) { if (rem == 0 && pipe->length > 0) { pipe->buf = NULL; + pipe->armed = false; return true; } @@ -204,15 +207,13 @@ static bool handle_xfer_in(uint8_t rhport, uint_fast8_t ep_addr) { musb_ep_csr_t* ep_csr = get_ep_csr(musb_regs, epnum); const unsigned mps = ep_csr->tx_maxp; const unsigned len = TU_MIN(mps, rem); - void *buf = pipe->buf; volatile void *fifo_ptr = &musb_regs->fifo[epnum]; - // TU_LOG1(" %p mps %d len %d rem %d\r\n", buf, mps, len, rem); if (len) { - if (_dcd.pipe_buf_is_fifo[TUSB_DIR_IN] & TU_BIT(epnum_minus1)) { - tu_hwfifo_write_from_fifo(fifo_ptr, (tu_fifo_t *)buf, len, NULL); + if (pipe->use_fifo) { + tu_hwfifo_write_from_fifo(fifo_ptr, pipe->fifo, len, NULL); } else { - tu_hwfifo_write(fifo_ptr, buf, len, NULL); - pipe->buf = (uint8_t*)buf + len; + tu_hwfifo_write(fifo_ptr, pipe->buf, len, NULL); + pipe->buf += len; } pipe->remaining = rem - len; } @@ -231,11 +232,17 @@ static bool handle_xfer_out(uint8_t rhport, uint_fast8_t ep_addr) // TU_LOG1(" RXCSRL%d = %x\r\n", epnum_minus1 + 1, ep_csr->rx_csrl); //Fail gracefully. Spurious interrupt. - if (!(ep_csr->rx_csrl & MUSB_RXCSRL1_RXRDY)) return false; + if (!(ep_csr->rx_csrl & MUSB_RXCSRL1_RXRDY)) { + return false; + } - void *buf = pipe->buf; - if (buf == NULL) { - ep_csr->rx_csrl = MUSB_RXCSRL1_FLUSH; + if (!pipe->armed) { + // Packet is already ACK'd by hardware and sitting in the Rx FIFO, but no transfer is + // posted. Do NOT flush (per MUSB spec §3.3.11 FlushFIFO) - that would silently drop + // acknowledged data. Mask this endpoint's Rx interrupt so the ISR stops re-firing; + // the FIFO stays occupied so hardware NAKs further OUT tokens (natural backpressure). + // The next dcd_edpt_xfer() on this endpoint will drain the staged packet. + musb_regs->intr_rxen &= (uint16_t) ~TU_BIT(epnum); return false; } @@ -245,11 +252,11 @@ static bool handle_xfer_out(uint8_t rhport, uint_fast8_t ep_addr) const unsigned len = TU_MIN(TU_MIN(rem, mps), vld); volatile void *fifo_ptr = &musb_regs->fifo[epnum]; if (len) { - if (_dcd.pipe_buf_is_fifo[TUSB_DIR_OUT] & TU_BIT(epnum_minus1)) { - tu_hwfifo_read_to_fifo(fifo_ptr, (tu_fifo_t *)buf, len, NULL); + if (pipe->use_fifo) { + tu_hwfifo_read_to_fifo(fifo_ptr, pipe->fifo, len, NULL); } else { - tu_hwfifo_read(fifo_ptr, buf, len, NULL); - pipe->buf = (uint8_t*)buf + len; + tu_hwfifo_read(fifo_ptr, pipe->buf, len, NULL); + pipe->buf += len; } pipe->remaining = rem - len; } @@ -257,28 +264,46 @@ static bool handle_xfer_out(uint8_t rhport, uint_fast8_t ep_addr) ep_csr->rx_csrl = 0; /* Always Clear RXRDY bit */ if ((len < mps) || (rem == len)) { pipe->buf = NULL; - return NULL != buf; + pipe->armed = false; + return true; } return false; } -static bool edpt_n_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes) +static bool edpt_n_xfer(uint8_t rhport, uint8_t ep_addr, void *buffer, uint16_t total_bytes, bool use_fifo) { unsigned epnum = tu_edpt_number(ep_addr); unsigned epnum_minus1 = epnum - 1; unsigned dir_in = tu_edpt_dir(ep_addr); pipe_state_t *pipe = &_dcd.pipe[dir_in][epnum_minus1]; - pipe->buf = buffer; + if (use_fifo) { + pipe->fifo = (tu_fifo_t *) buffer; + } else { + pipe->buf = (uint8_t *) buffer; + } pipe->length = total_bytes; pipe->remaining = total_bytes; + pipe->use_fifo = use_fifo; + pipe->armed = true; if (dir_in) { handle_xfer_in(rhport, ep_addr); } else { musb_regs_t* musb_regs = MUSB_REGS(rhport); musb_ep_csr_t* ep_csr = get_ep_csr(musb_regs, epnum); - if (ep_csr->rx_csrl & MUSB_RXCSRL1_RXRDY) ep_csr->rx_csrl = 0; + + // Re-enable Rx interrupt (may have been masked by the no-buffer path in handle_xfer_out) + musb_regs->intr_rxen |= (uint16_t) TU_BIT(epnum); + + // Drain any packet staged in the Rx FIFO from a prior no-buffer interrupt + if (ep_csr->rx_csrl & MUSB_RXCSRL1_RXRDY) { + if (handle_xfer_out(rhport, ep_addr)) { + dcd_event_xfer_complete(rhport, ep_addr, + pipe->length - pipe->remaining, + XFER_RESULT_SUCCESS, false); + } + } } return true; } @@ -411,7 +436,7 @@ static void process_ep0(uint8_t rhport) return; } - /* When CSRL0 is zero, it means that completion of sending a any length packet + /* When CSRL0 is zero, it means that completion of sending any length packet * or receiving a zero length packet. */ if (req != REQUEST_TYPE_INVALID && !tu_edpt_dir(req)) { /* STATUS IN */ @@ -611,6 +636,7 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * ep_desc) { pipe->buf = NULL; pipe->length = 0; pipe->remaining = 0; + pipe->armed = false; musb_regs_t* musb = MUSB_REGS(rhport); musb_ep_csr_t* ep_csr = get_ep_csr(musb, epn); @@ -656,6 +682,7 @@ bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const *ep_desc ) pipe->buf = NULL; pipe->length = 0; pipe->remaining = 0; + pipe->armed = false; musb_regs_t* musb = MUSB_REGS(rhport); musb_ep_csr_t* ep_csr = get_ep_csr(musb, epn); @@ -722,13 +749,14 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t t musb_dcd_int_disable(rhport); if (epnum) { - _dcd.pipe_buf_is_fifo[tu_edpt_dir(ep_addr)] &= ~TU_BIT(epnum - 1); - ret = edpt_n_xfer(rhport, ep_addr, buffer, total_bytes); + ret = edpt_n_xfer(rhport, ep_addr, buffer, total_bytes, false); } else { ret = edpt0_xfer(rhport, ep_addr, buffer, total_bytes); } - if (ie) musb_dcd_int_enable(rhport); + if (ie) { + musb_dcd_int_enable(rhport); + } return ret; } @@ -744,8 +772,7 @@ bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_ TU_ASSERT(epnum); unsigned const ie = musb_dcd_get_int_enable(rhport); musb_dcd_int_disable(rhport); - _dcd.pipe_buf_is_fifo[tu_edpt_dir(ep_addr)] |= TU_BIT(epnum - 1); - ret = edpt_n_xfer(rhport, ep_addr, (uint8_t*)ff, total_bytes); + ret = edpt_n_xfer(rhport, ep_addr, ff, total_bytes, true); if (ie) musb_dcd_int_enable(rhport); return ret; } @@ -768,6 +795,7 @@ void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) { } else { const uint8_t is_rx = 1 - tu_edpt_dir(ep_addr); ep_csr->maxp_csr[is_rx].csrl = MUSB_CSRL_SEND_STALL(is_rx); + _dcd.pipe[tu_edpt_dir(ep_addr)][epn - 1].armed = false; } if (ie) musb_dcd_int_enable(rhport); diff --git a/test/hil/tinyusb.json b/test/hil/tinyusb.json index 92b7b21b0..5466cd534 100644 --- a/test/hil/tinyusb.json +++ b/test/hil/tinyusb.json @@ -226,6 +226,9 @@ { "name": "stm32f072disco", "uid": "3A001A001357364230353532", + "tests": { + "device": true, "host": false, "dual": false + }, "flasher": { "name": "jlink", "uid": "779541626", -- cgit v1.3.1 From da2368bc141c7e76e818df2336ffe672b7927748 Mon Sep 17 00:00:00 2001 From: hathach Date: Mon, 20 Apr 2026 22:11:17 +0700 Subject: fix usbnet hardcode speed. disable hil test for now --- lib/networking/rndis_reports.c | 8 ++++++-- src/class/net/ecm_rndis_device.c | 5 +++-- test/hil/hil_test.py | 2 +- 3 files changed, 10 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/lib/networking/rndis_reports.c b/lib/networking/rndis_reports.c index 5e824d5a5..f06bc5507 100644 --- a/lib/networking/rndis_reports.c +++ b/lib/networking/rndis_reports.c @@ -36,9 +36,13 @@ #include "rndis_protocol.h" #include "netif/ethernet.h" -#define RNDIS_LINK_SPEED 12000000 /* Link baudrate (12Mbit/s for USB-FS) */ #define RNDIS_VENDOR "TinyUSB" /* NIC vendor name */ +// USB link speed in bits/sec, reflected to host via OID_GEN_LINK_SPEED. +static inline uint32_t rndis_link_speed_bps(void) { + return (tud_speed_get() == TUSB_SPEED_HIGH) ? 480000000U : 12000000U; +} + static const uint8_t *const station_hwaddr = tud_network_mac_address; static const uint8_t *const permanent_hwaddr = tud_network_mac_address; @@ -127,7 +131,7 @@ static void rndis_query(void) case OID_GEN_MEDIA_IN_USE: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, NDIS_MEDIUM_802_3); return; case OID_GEN_PHYSICAL_MEDIUM: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, NDIS_MEDIUM_802_3); return; case OID_GEN_HARDWARE_STATUS: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, 0); return; - case OID_GEN_LINK_SPEED: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, RNDIS_LINK_SPEED / 100); return; + case OID_GEN_LINK_SPEED: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, rndis_link_speed_bps() / 100U); return; case OID_GEN_VENDOR_ID: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, 0x00FFFFFF); return; case OID_GEN_VENDOR_DESCRIPTION: rndis_query_cmplt(RNDIS_STATUS_SUCCESS, rndis_vendor, strlen(rndis_vendor) + 1); return; case OID_GEN_CURRENT_PACKET_FILTER: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, oid_packet_filter); return; diff --git a/src/class/net/ecm_rndis_device.c b/src/class/net/ecm_rndis_device.c index eaa82c187..9282e0605 100644 --- a/src/class/net/ecm_rndis_device.c +++ b/src/class/net/ecm_rndis_device.c @@ -206,14 +206,15 @@ static void ecm_report(bool nc) { }, }; + const uint32_t link_bps = (tud_speed_get() == TUSB_SPEED_HIGH) ? 480000000U : 12000000U; const ecm_notify_t ecm_notify_csc = { .header = { .bmRequestType = 0xA1, .bRequest = 0x2A, /* CONNECTION_SPEED_CHANGE aka ConnectionSpeedChange */ .wLength = 8, }, - .downlink = 9728000, - .uplink = 9728000, + .downlink = link_bps, + .uplink = link_bps, }; ecm_notify_t notify = (nc) ? ecm_notify_nc : ecm_notify_csc; diff --git a/test/hil/hil_test.py b/test/hil/hil_test.py index 1d88b4c5f..6f9b70e95 100755 --- a/test/hil/hil_test.py +++ b/test/hil/hil_test.py @@ -1208,7 +1208,7 @@ device_tests = [ 'device/printer_to_cdc', 'device/midi_test', 'device/mtp', - 'device/net_lwip_webserver' + # 'device/net_lwip_webserver' ] dual_tests = [ -- cgit v1.3.1 From f9e79844edd9757243c786c9e901cfba3f281b6b Mon Sep 17 00:00:00 2001 From: hathach Date: Tue, 21 Apr 2026 11:26:51 +0700 Subject: replace `TUD_ENDPOINT_ONE_DIRECTION_ONLY` with `CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY` for improved configuration consistency across examples and core sources --- .../device/cdc_dual_ports/src/usb_descriptors.c | 2 +- examples/device/cdc_msc/src/usb_descriptors.c | 2 +- examples/device/cdc_msc_freertos/src/main.c | 4 ++-- .../device/cdc_msc_freertos/src/usb_descriptors.c | 2 +- examples/device/cdc_uac2/src/usb_descriptors.c | 2 +- .../dynamic_configuration/src/usb_descriptors.c | 2 +- .../device/hid_generic_inout/src/usb_descriptors.c | 2 +- examples/device/midi_test/src/usb_descriptors.c | 2 +- .../midi_test_freertos/src/usb_descriptors.c | 2 +- examples/device/msc_dual_lun/src/usb_descriptors.c | 2 +- examples/device/mtp/src/usb_descriptors.c | 2 +- .../net_lwip_webserver/src/usb_descriptors.c | 2 +- .../device/printer_to_cdc/src/usb_descriptors.c | 2 +- examples/device/uac2_headset/src/usb_descriptors.c | 2 +- .../device/uac2_speaker_fb/src/usb_descriptors.c | 2 +- .../device/webusb_serial/src/usb_descriptors.c | 2 +- examples/dual/dynamic_switch/src/usb_descriptors.c | 2 +- src/common/tusb_mcu.h | 27 +++++++++++++++------- 18 files changed, 37 insertions(+), 26 deletions(-) (limited to 'src') diff --git a/examples/device/cdc_dual_ports/src/usb_descriptors.c b/examples/device/cdc_dual_ports/src/usb_descriptors.c index e6011c35a..2d899a7c6 100644 --- a/examples/device/cdc_dual_ports/src/usb_descriptors.c +++ b/examples/device/cdc_dual_ports/src/usb_descriptors.c @@ -106,7 +106,7 @@ enum { #define EPNUM_CDC_1_OUT 0x05 #define EPNUM_CDC_1_IN 0x84 -#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY) +#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY // MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h // e.g EP1 OUT & EP1 IN cannot exist together #define EPNUM_CDC_0_NOTIF 0x81 diff --git a/examples/device/cdc_msc/src/usb_descriptors.c b/examples/device/cdc_msc/src/usb_descriptors.c index c668ea3a7..b738e7d12 100644 --- a/examples/device/cdc_msc/src/usb_descriptors.c +++ b/examples/device/cdc_msc/src/usb_descriptors.c @@ -102,7 +102,7 @@ enum { #define EPNUM_MSC_OUT 0x05 #define EPNUM_MSC_IN 0x84 -#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY) +#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY // MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h // e.g EP1 OUT & EP1 IN cannot exist together #define EPNUM_CDC_NOTIF 0x81 diff --git a/examples/device/cdc_msc_freertos/src/main.c b/examples/device/cdc_msc_freertos/src/main.c index 4fb209fd0..f2f71d089 100644 --- a/examples/device/cdc_msc_freertos/src/main.c +++ b/examples/device/cdc_msc_freertos/src/main.c @@ -34,10 +34,10 @@ #define USBD_STACK_SIZE 4096 #else // Increase stack size when debug log is enabled - #define USBD_STACK_SIZE (3*configMINIMAL_STACK_SIZE/2) * (CFG_TUSB_DEBUG ? 2 : 1) + #define USBD_STACK_SIZE (configMINIMAL_STACK_SIZE * (CFG_TUSB_DEBUG ? 4 : 2)) #endif -#define CDC_STACK_SIZE (configMINIMAL_STACK_SIZE * (CFG_TUSB_DEBUG ? 2 : 1)) +#define CDC_STACK_SIZE (configMINIMAL_STACK_SIZE * (CFG_TUSB_DEBUG ? 3 : 2)) #define BLINKY_STACK_SIZE configMINIMAL_STACK_SIZE //--------------------------------------------------------------------+ diff --git a/examples/device/cdc_msc_freertos/src/usb_descriptors.c b/examples/device/cdc_msc_freertos/src/usb_descriptors.c index 4950f02e0..26bc0de00 100644 --- a/examples/device/cdc_msc_freertos/src/usb_descriptors.c +++ b/examples/device/cdc_msc_freertos/src/usb_descriptors.c @@ -102,7 +102,7 @@ enum { #define EPNUM_MSC_OUT 0x05 #define EPNUM_MSC_IN 0x84 -#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY) +#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY // MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h // e.g EP1 OUT & EP1 IN cannot exist together #define EPNUM_CDC_NOTIF 0x81 diff --git a/examples/device/cdc_uac2/src/usb_descriptors.c b/examples/device/cdc_uac2/src/usb_descriptors.c index e6caaa971..7ef738de9 100644 --- a/examples/device/cdc_uac2/src/usb_descriptors.c +++ b/examples/device/cdc_uac2/src/usb_descriptors.c @@ -97,7 +97,7 @@ uint8_t const * tud_descriptor_device_cb(void) #define EPNUM_CDC_OUT 0x02 #define EPNUM_CDC_IN 0x82 -#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY) +#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY // MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h // e.g EP1 OUT & EP1 IN cannot exist together #define EPNUM_AUDIO_IN 0x01 diff --git a/examples/device/dynamic_configuration/src/usb_descriptors.c b/examples/device/dynamic_configuration/src/usb_descriptors.c index 458b7c2a5..c4049414f 100644 --- a/examples/device/dynamic_configuration/src/usb_descriptors.c +++ b/examples/device/dynamic_configuration/src/usb_descriptors.c @@ -132,7 +132,7 @@ enum #define EPNUM_1_MSC_OUT 0x02 #define EPNUM_1_MSC_IN 0x82 -#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY) +#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY // MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h // e.g EP1 OUT & EP1 IN cannot exist together #define EPNUM_0_CDC_NOTIF 0x81 diff --git a/examples/device/hid_generic_inout/src/usb_descriptors.c b/examples/device/hid_generic_inout/src/usb_descriptors.c index 929b2fd3a..93e718461 100644 --- a/examples/device/hid_generic_inout/src/usb_descriptors.c +++ b/examples/device/hid_generic_inout/src/usb_descriptors.c @@ -97,7 +97,7 @@ enum #define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_HID_INOUT_DESC_LEN) -#if defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY) +#if CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY // MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h // e.g EP1 OUT & EP1 IN cannot exist together #define EPNUM_HID_OUT 0x01 diff --git a/examples/device/midi_test/src/usb_descriptors.c b/examples/device/midi_test/src/usb_descriptors.c index e969f33a3..99c798ce1 100644 --- a/examples/device/midi_test/src/usb_descriptors.c +++ b/examples/device/midi_test/src/usb_descriptors.c @@ -87,7 +87,7 @@ enum { #define EPNUM_MIDI_OUT 0x02 #define EPNUM_MIDI_IN 0x81 -#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY) +#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY // MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h // e.g EP1 OUT & EP1 IN cannot exist together #define EPNUM_MIDI_OUT 0x01 diff --git a/examples/device/midi_test_freertos/src/usb_descriptors.c b/examples/device/midi_test_freertos/src/usb_descriptors.c index e969f33a3..99c798ce1 100644 --- a/examples/device/midi_test_freertos/src/usb_descriptors.c +++ b/examples/device/midi_test_freertos/src/usb_descriptors.c @@ -87,7 +87,7 @@ enum { #define EPNUM_MIDI_OUT 0x02 #define EPNUM_MIDI_IN 0x81 -#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY) +#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY // MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h // e.g EP1 OUT & EP1 IN cannot exist together #define EPNUM_MIDI_OUT 0x01 diff --git a/examples/device/msc_dual_lun/src/usb_descriptors.c b/examples/device/msc_dual_lun/src/usb_descriptors.c index f73935ee0..c2eb22a4c 100644 --- a/examples/device/msc_dual_lun/src/usb_descriptors.c +++ b/examples/device/msc_dual_lun/src/usb_descriptors.c @@ -91,7 +91,7 @@ enum #define EPNUM_MSC_OUT 0x02 #define EPNUM_MSC_IN 0x81 -#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY) +#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY // MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h // e.g EP1 OUT & EP1 IN cannot exist together #define EPNUM_MSC_OUT 0x01 diff --git a/examples/device/mtp/src/usb_descriptors.c b/examples/device/mtp/src/usb_descriptors.c index f0aa3de6b..4c840560e 100644 --- a/examples/device/mtp/src/usb_descriptors.c +++ b/examples/device/mtp/src/usb_descriptors.c @@ -94,7 +94,7 @@ enum #define EPNUM_MTP_OUT 0x02 #define EPNUM_MTP_IN 0x81 -#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY) +#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY // MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h // e.g EP1 OUT & EP1 IN cannot exist together #define EPNUM_MTP_EVT 0x81 diff --git a/examples/device/net_lwip_webserver/src/usb_descriptors.c b/examples/device/net_lwip_webserver/src/usb_descriptors.c index c976cb62b..8cfef41a6 100644 --- a/examples/device/net_lwip_webserver/src/usb_descriptors.c +++ b/examples/device/net_lwip_webserver/src/usb_descriptors.c @@ -121,7 +121,7 @@ const uint8_t *tud_descriptor_device_cb(void) { #define EPNUM_NET_OUT 0x02 #define EPNUM_NET_IN 0x81 -#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY) +#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY // MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h // e.g EP1 OUT & EP1 IN cannot exist together #define EPNUM_NET_NOTIF 0x81 diff --git a/examples/device/printer_to_cdc/src/usb_descriptors.c b/examples/device/printer_to_cdc/src/usb_descriptors.c index 30d309ed4..2e6b3f6c3 100644 --- a/examples/device/printer_to_cdc/src/usb_descriptors.c +++ b/examples/device/printer_to_cdc/src/usb_descriptors.c @@ -67,7 +67,7 @@ uint8_t const *tud_descriptor_device_cb(void) { //--------------------------------------------------------------------+ // Endpoint numbers -#if defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY) +#if CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY #define EPNUM_CDC_NOTIF 0x81 #define EPNUM_CDC_OUT 0x02 #define EPNUM_CDC_IN 0x83 diff --git a/examples/device/uac2_headset/src/usb_descriptors.c b/examples/device/uac2_headset/src/usb_descriptors.c index e4fbbf8a5..e9ac8b817 100644 --- a/examples/device/uac2_headset/src/usb_descriptors.c +++ b/examples/device/uac2_headset/src/usb_descriptors.c @@ -97,7 +97,7 @@ uint8_t const * tud_descriptor_device_cb(void) #define EPNUM_AUDIO_OUT 0x08 #define EPNUM_AUDIO_INT 0x01 -#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY) +#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY // MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h // e.g EP1 OUT & EP1 IN cannot exist together #define EPNUM_AUDIO_IN 0x01 diff --git a/examples/device/uac2_speaker_fb/src/usb_descriptors.c b/examples/device/uac2_speaker_fb/src/usb_descriptors.c index c5a161a1e..2e21e54e3 100644 --- a/examples/device/uac2_speaker_fb/src/usb_descriptors.c +++ b/examples/device/uac2_speaker_fb/src/usb_descriptors.c @@ -115,7 +115,7 @@ uint8_t const * tud_hid_descriptor_report_cb(uint8_t itf) { #define EPNUM_AUDIO_FB 0x08 #define EPNUM_DEBUG 0x01 -#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY) +#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY // MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h // e.g EP1 OUT & EP1 IN cannot exist together #define EPNUM_AUDIO 0x02 diff --git a/examples/device/webusb_serial/src/usb_descriptors.c b/examples/device/webusb_serial/src/usb_descriptors.c index 0ef41a68e..415d2b66a 100644 --- a/examples/device/webusb_serial/src/usb_descriptors.c +++ b/examples/device/webusb_serial/src/usb_descriptors.c @@ -104,7 +104,7 @@ enum #define EPNUM_VENDOR_OUT 0x05 #define EPNUM_VENDOR_IN 0x84 -#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY) +#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY // MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h // e.g EP1 OUT & EP1 IN cannot exist together #define EPNUM_CDC_NOTIF 0x81 diff --git a/examples/dual/dynamic_switch/src/usb_descriptors.c b/examples/dual/dynamic_switch/src/usb_descriptors.c index 54ffc2c18..ef6d795b7 100644 --- a/examples/dual/dynamic_switch/src/usb_descriptors.c +++ b/examples/dual/dynamic_switch/src/usb_descriptors.c @@ -86,7 +86,7 @@ enum { #define EPNUM_CDC_OUT 0x02 #define EPNUM_CDC_IN 0x82 -#elif defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY) +#elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY // MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h // e.g EP1 OUT & EP1 IN cannot exist together #define EPNUM_CDC_NOTIF 0x81 diff --git a/src/common/tusb_mcu.h b/src/common/tusb_mcu.h index 77a0bbf1d..651bb149d 100644 --- a/src/common/tusb_mcu.h +++ b/src/common/tusb_mcu.h @@ -177,12 +177,12 @@ #elif TU_CHECK_MCU(OPT_MCU_SAMG) #define TUP_DCD_ENDPOINT_MAX 6 - #define TUD_ENDPOINT_ONE_DIRECTION_ONLY + #define CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY 1 #elif TU_CHECK_MCU(OPT_MCU_SAMX7X) #define TUP_DCD_ENDPOINT_MAX 10 #define TUP_RHPORT_HIGHSPEED 1 - #define TUD_ENDPOINT_ONE_DIRECTION_ONLY + #define CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY 1 // Enable dcache if DMA is enabled #define CFG_TUD_MEM_DCACHE_ENABLE_DEFAULT CFG_TUD_SAMX7X_DMA_ENABLE @@ -190,11 +190,11 @@ #elif TU_CHECK_MCU(OPT_MCU_PIC32MZ) #define TUP_DCD_ENDPOINT_MAX 8 - #define TUD_ENDPOINT_ONE_DIRECTION_ONLY + #define CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY 1 #elif TU_CHECK_MCU(OPT_MCU_PIC32MX, OPT_MCU_PIC32MM, OPT_MCU_PIC32MK) || TU_CHECK_MCU(OPT_MCU_PIC24, OPT_MCU_DSPIC33) #define TUP_DCD_ENDPOINT_MAX 16 - #define TUD_ENDPOINT_ONE_DIRECTION_ONLY + #define CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY 1 #define TUP_DCD_EDPT_CLOSE_API //--------------------------------------------------------------------+ @@ -411,7 +411,7 @@ #elif TU_CHECK_MCU(OPT_MCU_CXD56) #define TUP_DCD_ENDPOINT_MAX 7 #define TUP_RHPORT_HIGHSPEED 1 - #define TUD_ENDPOINT_ONE_DIRECTION_ONLY + #define CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY 1 //--------------------------------------------------------------------+ // TI @@ -547,12 +547,12 @@ #elif TU_CHECK_MCU(OPT_MCU_FT90X) #define TUP_DCD_ENDPOINT_MAX 8 #define TUP_RHPORT_HIGHSPEED 1 - #define TUD_ENDPOINT_ONE_DIRECTION_ONLY + #define CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY 1 #elif TU_CHECK_MCU(OPT_MCU_FT93X) #define TUP_DCD_ENDPOINT_MAX 16 #define TUP_RHPORT_HIGHSPEED 1 - #define TUD_ENDPOINT_ONE_DIRECTION_ONLY + #define CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY 1 //--------------------------------------------------------------------+ // Allwinner @@ -643,7 +643,7 @@ #define TUP_USBIP_MUSB_ADI #define TUP_DCD_ENDPOINT_MAX 12 #define TUP_RHPORT_HIGHSPEED 1 - #define TUD_ENDPOINT_ONE_DIRECTION_ONLY + #define CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY 1 //--------------------------------------------------------------------+ // ArteryTek @@ -727,3 +727,14 @@ #ifndef TUP_DCD_EDPT_CLOSE_API #define TUP_DCD_EDPT_ISO_ALLOC #endif + +// Some USBIPs (SAMG, SAMX7X, PIC32, MAX3266x/MAX78002) cannot assign the same endpoint +// number to both IN and OUT. Default to 0 (same endpoint number may be used for IN and OUT). +#ifndef CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY + #define CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY 0 +#endif + +// Backward-compatible alias: legacy code only tests defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY) +#if CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY && !defined(TUD_ENDPOINT_ONE_DIRECTION_ONLY) + #define TUD_ENDPOINT_ONE_DIRECTION_ONLY +#endif -- cgit v1.3.1 From 85b967c9b0d26c8dbd16fedfc07d166b8f9b77ae Mon Sep 17 00:00:00 2001 From: hathach Date: Tue, 21 Apr 2026 11:28:29 +0700 Subject: refactor interrupt handling and add `pipe_write` to fix IN ZLP issue --- src/portable/mentor/musb/dcd_musb.c | 35 +++++++++++++++++++---------------- test/hil/hil_test.py | 31 ++++++++++++++++++++----------- 2 files changed, 39 insertions(+), 27 deletions(-) (limited to 'src') diff --git a/src/portable/mentor/musb/dcd_musb.c b/src/portable/mentor/musb/dcd_musb.c index ad3838a09..acd86b9e7 100644 --- a/src/portable/mentor/musb/dcd_musb.c +++ b/src/portable/mentor/musb/dcd_musb.c @@ -191,21 +191,12 @@ static void process_setup_packet(uint8_t rhport) { } } -static bool handle_xfer_in(uint8_t rhport, uint_fast8_t ep_addr) { - unsigned epnum = tu_edpt_number(ep_addr); - unsigned epnum_minus1 = epnum - 1; - pipe_state_t *pipe = &_dcd.pipe[tu_edpt_dir(ep_addr)][epnum_minus1]; - const unsigned rem = pipe->remaining; - - if (rem == 0 && pipe->length > 0) { - pipe->buf = NULL; - pipe->armed = false; - return true; - } - - musb_regs_t* musb_regs = MUSB_REGS(rhport); +// write to txfifo using pipe_state_t info +static void pipe_write(musb_regs_t* musb_regs, uint8_t epnum) { + pipe_state_t* pipe = &_dcd.pipe[TUSB_DIR_IN][epnum - 1]; musb_ep_csr_t* ep_csr = get_ep_csr(musb_regs, epnum); const unsigned mps = ep_csr->tx_maxp; + const unsigned rem = pipe->remaining; const unsigned len = TU_MIN(mps, rem); volatile void *fifo_ptr = &musb_regs->fifo[epnum]; if (len) { @@ -218,7 +209,19 @@ static bool handle_xfer_in(uint8_t rhport, uint_fast8_t ep_addr) { pipe->remaining = rem - len; } ep_csr->tx_csrl = MUSB_TXCSRL1_TXRDY; - // TU_LOG1(" TXCSRL%d = %x %d\r\n", epnum, ep_csr->tx_csrl, rem - len); +} + +// Called from the TX interrupt. If the last queued packet finished the transfer, +// signal completion; otherwise queue the next packet. +static bool handle_xfer_in(musb_regs_t* musb_regs, uint8_t epnum) { + pipe_state_t* pipe = &_dcd.pipe[TUSB_DIR_IN][epnum - 1]; + + if (pipe->remaining == 0) { + pipe->buf = NULL; + pipe->armed = false; + return true; + } + pipe_write(musb_regs, epnum); return false; } @@ -288,7 +291,7 @@ static bool edpt_n_xfer(uint8_t rhport, uint8_t ep_addr, void *buffer, uint16_t pipe->armed = true; if (dir_in) { - handle_xfer_in(rhport, ep_addr); + pipe_write(MUSB_REGS(rhport), (uint8_t) epnum); } else { musb_regs_t* musb_regs = MUSB_REGS(rhport); musb_ep_csr_t* ep_csr = get_ep_csr(musb_regs, epnum); @@ -476,7 +479,7 @@ static void process_edpt_n(uint8_t rhport, uint_fast8_t ep_addr) ep_csr->tx_csrl &= ~(MUSB_TXCSRL1_STALLED | MUSB_TXCSRL1_UNDRN); return; } - completed = handle_xfer_in(rhport, ep_addr); + completed = handle_xfer_in(musb_regs, (uint8_t) epn); } else { // TU_LOG1(" RX CSRL%d = %x\r\n", epn, ep_csr->rx_csrl); if (ep_csr->rx_csrl & MUSB_RXCSRL1_STALLED) { diff --git a/test/hil/hil_test.py b/test/hil/hil_test.py index 6f9b70e95..dfe09bf23 100755 --- a/test/hil/hil_test.py +++ b/test/hil/hil_test.py @@ -59,6 +59,7 @@ STATUS_SKIPPED = "\033[33mSkipped\033[0m" verbose = False test_only = [] build_dir = 'cmake-build' +skip_flash = False WCH_RISCV_CONTENT = """ adapter driver wlinke @@ -1248,11 +1249,15 @@ def test_example(board, f1, example): if verbose: print(f'Flashing {fw_name}.elf') - # flash firmware. It may fail randomly, retry a few times + # flash firmware (unless --skip-flash), then run the test. Both may fail randomly, + # retry a few times. start_s = time.time() + flash_ok = True for i in range(max_retry): - ret = globals()[f'flash_{board["flasher"]["name"].lower()}'](board, fw_name) - if ret.returncode == 0: + if not skip_flash: + ret = globals()[f'flash_{board["flasher"]["name"].lower()}'](board, fw_name) + flash_ok = (ret.returncode == 0) + if flash_ok: try: tret = globals()[f'test_{example.replace("/", "_")}'](board) if tret == 'skipped': @@ -1271,7 +1276,7 @@ def test_example(board, f1, example): print(f'\n Flash failed, retry {i+2}/{max_retry}', end='') time.sleep(0.5) - if ret.returncode != 0: + if not flash_ok: err_count += 1 print(f' Flash {STATUS_FAILED}', end='') @@ -1315,8 +1320,9 @@ def test_board(board): for test in test_list: err_count += test_example(board, f1, test) - # flash board_test last to disable board's usb - test_example(board, flags_on_list[0], 'device/board_test') + # flash board_test last to disable board's usb (skipped when --skip-flash is set) + if not skip_flash: + test_example(board, flags_on_list[0], 'device/board_test') return name, err_count @@ -1329,26 +1335,29 @@ def main(): global test_only global build_dir global max_retry + global skip_flash duration = time.time() parser = argparse.ArgumentParser() parser.add_argument('config_file', help='Configuration JSON file') parser.add_argument('-b', '--board', action='append', default=[], help='Boards to test, all if not specified') - parser.add_argument('-s', '--skip', action='append', default=[], help='Skip boards from test') + parser.add_argument('-s', '--skip-board', action='append', default=[], help='Skip boards from test') + parser.add_argument('-sf', '--skip-flash', action='store_true', help='Run tests without flashing firmware (use whatever is already on the board)') parser.add_argument('-t', '--test-only', action='append', default=[], help='Tests to run, all if not specified') - parser.add_argument('-B', '--build', default='cmake-build', help='Build folder name (default: cmake-build)') + parser.add_argument('-B', '--build-dir', default='cmake-build', help='Build folder name (default: cmake-build)') parser.add_argument('-r', '--retry', type=int, default=3, help='Retry count for failed tests (default: 3)') parser.add_argument('-v', '--verbose', action='store_true', help='Verbose output') args = parser.parse_args() config_file = args.config_file boards = args.board - skip_boards = args.skip + skip_boards = args.skip_board verbose = args.verbose test_only = args.test_only - build_dir = args.build + build_dir = args.build_dir max_retry = args.retry + skip_flash = args.skip_flash # if config file is not found, try to find it in the same directory as this script if not os.path.exists(config_file): @@ -1370,7 +1379,7 @@ def main(): if err_count > 0: skip_boards += [name for name, err in mret if err == 0] with open(skip_fname, 'w') as f: - f.write(' '.join(f'-s {i}' for i in skip_boards)) + f.write(' '.join(f'--skip-board {i}' for i in skip_boards)) elif os.path.exists(skip_fname): os.remove(skip_fname) -- cgit v1.3.1 From c13864dbe49b2009f9ad3be2f912ddd80a9ecd40 Mon Sep 17 00:00:00 2001 From: hathach Date: Tue, 21 Apr 2026 15:28:44 +0700 Subject: optimize pipe_state_t sram for port with CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY. separate process_edpt_n() to process_epin() and process_epout() --- src/common/tusb_types.h | 10 +- src/portable/mentor/musb/dcd_musb.c | 209 ++++++++++++++++++------------------ 2 files changed, 111 insertions(+), 108 deletions(-) (limited to 'src') diff --git a/src/common/tusb_types.h b/src/common/tusb_types.h index a18f9feb7..806997866 100644 --- a/src/common/tusb_types.h +++ b/src/common/tusb_types.h @@ -100,12 +100,14 @@ typedef enum { } tusb_xfer_type_t; typedef enum { - TUSB_DIR_OUT = 0, - TUSB_DIR_IN = 1, + TUSB_DIR_OUT = 0u, + TUSB_DIR_IN = 1u, +} tusb_dir_t; - TUSB_EPNUM_MASK = 0x0F, +enum { + TUSB_EPNUM_MASK = 0x0F, TUSB_DIR_IN_MASK = 0x80 -} tusb_dir_t; +}; enum { TUSB_EPSIZE_BULK_FS = 64, diff --git a/src/portable/mentor/musb/dcd_musb.c b/src/portable/mentor/musb/dcd_musb.c index acd86b9e7..bf60adbe7 100644 --- a/src/portable/mentor/musb/dcd_musb.c +++ b/src/portable/mentor/musb/dcd_musb.c @@ -69,6 +69,19 @@ typedef struct { bool use_fifo; /* true: buf is tu_fifo_t*; false: buf is plain byte pointer. */ } pipe_state_t; +// Pipe array layout (N = TUP_DCD_ENDPOINT_MAX): +// [0] : EP0 (shared between IN/OUT control stages) +// One-direction-only IPs (CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY=1): +// [1 .. n-1] : EP1..n-1 (single slot per endpoint) +// Bidirectional-capable IPs: +// [1 .. N-1 ] : EP OUT +// [N .. 2*N-2] : EP IN +#if CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY + #define MUSB_PIPE_COUNT TUP_DCD_ENDPOINT_MAX +#else + #define MUSB_PIPE_COUNT (2u * TUP_DCD_ENDPOINT_MAX - 1u) +#endif + typedef struct { union { tusb_control_request_t setup_packet; @@ -76,12 +89,27 @@ typedef struct { }; uint16_t remaining_ctrl; /* The number of bytes remaining in data stage of control transfer. */ int8_t status_out; - pipe_state_t pipe0; - pipe_state_t pipe[2][TUP_DCD_ENDPOINT_MAX-1]; /* pipe[direction][endpoint number - 1] */ + pipe_state_t pipe[MUSB_PIPE_COUNT]; } dcd_data_t; static dcd_data_t _dcd; +TU_ATTR_ALWAYS_INLINE static inline pipe_state_t* pipe_get(uint8_t epnum, tusb_dir_t epdir) { +#if CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY + (void) epdir; + return &_dcd.pipe[epnum]; +#else + if (epnum == 0) { + return &_dcd.pipe[0]; + } + size_t idx = epnum; + if (epdir == TUSB_DIR_IN) { + idx += TUP_DCD_ENDPOINT_MAX - 1u; + } + return &_dcd.pipe[idx]; +#endif +} + //-------------------------------------------------------------------- // HW FIFO Helper // Note: Index register is already set by caller @@ -176,9 +204,10 @@ static void process_setup_packet(uint8_t rhport) { _dcd.setup_buffer[0] = musb_regs->fifo[0]; _dcd.setup_buffer[1] = musb_regs->fifo[0]; - _dcd.pipe0.buf = NULL; - _dcd.pipe0.length = 0; - _dcd.pipe0.remaining = 0; + pipe_state_t* pipe0 = pipe_get(0, TUSB_DIR_OUT); + pipe0->buf = NULL; + pipe0->length = 0; + pipe0->remaining = 0; dcd_event_setup_received(rhport, (const uint8_t*)(uintptr_t)&_dcd.setup_packet, true); const unsigned len = _dcd.setup_packet.wLength; @@ -193,7 +222,7 @@ static void process_setup_packet(uint8_t rhport) { // write to txfifo using pipe_state_t info static void pipe_write(musb_regs_t* musb_regs, uint8_t epnum) { - pipe_state_t* pipe = &_dcd.pipe[TUSB_DIR_IN][epnum - 1]; + pipe_state_t* pipe = pipe_get(epnum, TUSB_DIR_IN); musb_ep_csr_t* ep_csr = get_ep_csr(musb_regs, epnum); const unsigned mps = ep_csr->tx_maxp; const unsigned rem = pipe->remaining; @@ -213,32 +242,38 @@ static void pipe_write(musb_regs_t* musb_regs, uint8_t epnum) { // Called from the TX interrupt. If the last queued packet finished the transfer, // signal completion; otherwise queue the next packet. -static bool handle_xfer_in(musb_regs_t* musb_regs, uint8_t epnum) { - pipe_state_t* pipe = &_dcd.pipe[TUSB_DIR_IN][epnum - 1]; +static void process_epin(uint8_t rhport, musb_regs_t *musb_regs, uint8_t epnum) { + musb_ep_csr_t* ep_csr = get_ep_csr(musb_regs, epnum); + if (ep_csr->tx_csrl & MUSB_TXCSRL1_STALLED) { + ep_csr->tx_csrl &= ~(MUSB_TXCSRL1_STALLED | MUSB_TXCSRL1_UNDRN); + return; // sent STALL, do nothing + } + pipe_state_t* pipe = pipe_get(epnum, TUSB_DIR_IN); if (pipe->remaining == 0) { + const uint16_t xferred_len = pipe->length; pipe->buf = NULL; pipe->armed = false; - return true; + dcd_event_xfer_complete(rhport, tu_edpt_addr(epnum, TUSB_DIR_IN), xferred_len, XFER_RESULT_SUCCESS, true); + return; } pipe_write(musb_regs, epnum); - return false; } -static bool handle_xfer_out(uint8_t rhport, uint_fast8_t ep_addr) -{ - unsigned epnum = tu_edpt_number(ep_addr); - unsigned epnum_minus1 = epnum - 1; - pipe_state_t *pipe = &_dcd.pipe[tu_edpt_dir(ep_addr)][epnum_minus1]; - musb_regs_t* musb_regs = MUSB_REGS(rhport); +static void process_epout(uint8_t rhport, musb_regs_t *musb_regs, uint8_t epnum) { musb_ep_csr_t* ep_csr = get_ep_csr(musb_regs, epnum); - // TU_LOG1(" RXCSRL%d = %x\r\n", epnum_minus1 + 1, ep_csr->rx_csrl); + if (ep_csr->rx_csrl & MUSB_RXCSRL1_STALLED) { + ep_csr->rx_csrl &= ~(MUSB_RXCSRL1_STALLED | MUSB_RXCSRL1_OVER); + return; // sent STALL, do nothing + } //Fail gracefully. Spurious interrupt. if (!(ep_csr->rx_csrl & MUSB_RXCSRL1_RXRDY)) { - return false; + return; } + pipe_state_t *pipe = pipe_get(epnum, TUSB_DIR_OUT); + if (!pipe->armed) { // Packet is already ACK'd by hardware and sitting in the Rx FIFO, but no transfer is // posted. Do NOT flush (per MUSB spec §3.3.11 FlushFIFO) - that would silently drop @@ -246,7 +281,7 @@ static bool handle_xfer_out(uint8_t rhport, uint_fast8_t ep_addr) // the FIFO stays occupied so hardware NAKs further OUT tokens (natural backpressure). // The next dcd_edpt_xfer() on this endpoint will drain the staged packet. musb_regs->intr_rxen &= (uint16_t) ~TU_BIT(epnum); - return false; + return; } const unsigned mps = ep_csr->rx_maxp; @@ -266,20 +301,20 @@ static bool handle_xfer_out(uint8_t rhport, uint_fast8_t ep_addr) ep_csr->rx_csrl = 0; /* Always Clear RXRDY bit */ if ((len < mps) || (rem == len)) { + const uint16_t xferred_len = pipe->length - pipe->remaining; pipe->buf = NULL; pipe->armed = false; - return true; + + dcd_event_xfer_complete(rhport, tu_edpt_addr(epnum, TUSB_DIR_OUT), xferred_len, XFER_RESULT_SUCCESS, true); } - return false; } static bool edpt_n_xfer(uint8_t rhport, uint8_t ep_addr, void *buffer, uint16_t total_bytes, bool use_fifo) { unsigned epnum = tu_edpt_number(ep_addr); - unsigned epnum_minus1 = epnum - 1; unsigned dir_in = tu_edpt_dir(ep_addr); - pipe_state_t *pipe = &_dcd.pipe[dir_in][epnum_minus1]; + pipe_state_t *pipe = pipe_get(epnum, dir_in); if (use_fifo) { pipe->fifo = (tu_fifo_t *) buffer; } else { @@ -296,16 +331,13 @@ static bool edpt_n_xfer(uint8_t rhport, uint8_t ep_addr, void *buffer, uint16_t musb_regs_t* musb_regs = MUSB_REGS(rhport); musb_ep_csr_t* ep_csr = get_ep_csr(musb_regs, epnum); - // Re-enable Rx interrupt (may have been masked by the no-buffer path in handle_xfer_out) + // Re-enable Rx interrupt (may have been masked by the no-buffer path in process_epout) musb_regs->intr_rxen |= (uint16_t) TU_BIT(epnum); - // Drain any packet staged in the Rx FIFO from a prior no-buffer interrupt + // Drain any packet staged in the Rx FIFO from a prior no-buffer interrupt. + // process_epout() fires dcd_event_xfer_complete() itself if the drain completes. if (ep_csr->rx_csrl & MUSB_RXCSRL1_RXRDY) { - if (handle_xfer_out(rhport, ep_addr)) { - dcd_event_xfer_complete(rhport, ep_addr, - pipe->length - pipe->remaining, - XFER_RESULT_SUCCESS, false); - } + process_epout(rhport, musb_regs, (uint8_t) epnum); } } return true; @@ -317,6 +349,7 @@ static bool edpt0_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_ TU_ASSERT(total_bytes <= 64); /* Current implementation supports for only up to 64 bytes. */ musb_regs_t* musb_regs = MUSB_REGS(rhport); musb_ep_csr_t* ep_csr = get_ep_csr(musb_regs, 0); + pipe_state_t* pipe0 = pipe_get(0, TUSB_DIR_OUT); const unsigned req = _dcd.setup_packet.bmRequestType; TU_ASSERT(req != REQUEST_TYPE_INVALID || total_bytes == 0); @@ -347,9 +380,9 @@ static bool edpt0_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_ if (dir_in) { tu_hwfifo_write(fifo_ptr, buffer, len, NULL); - _dcd.pipe0.buf = buffer + len; - _dcd.pipe0.length = len; - _dcd.pipe0.remaining = 0; + pipe0->buf = buffer + len; + pipe0->length = len; + pipe0->remaining = 0; _dcd.remaining_ctrl = rem - len; if ((len < 64) || (rem == len)) { @@ -360,19 +393,16 @@ static bool edpt0_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_ } else { ep_csr->csr0l = MUSB_CSRL0_TXRDY; /* Flush TX FIFO to return ACK. */ } - // TU_LOG1(" IN ep_csr->csr0l = %x\r\n", ep_csr->csr0l); } else { - // TU_LOG1(" OUT ep_csr->csr0l = %x\r\n", ep_csr->csr0l); - _dcd.pipe0.buf = buffer; - _dcd.pipe0.length = len; - _dcd.pipe0.remaining = len; + pipe0->buf = buffer; + pipe0->length = len; + pipe0->remaining = len; ep_csr->csr0l = MUSB_CSRL0_RXRDYC; /* Clear RX FIFO to return ACK. */ } } else if (dir_in) { - // TU_LOG1(" STATUS IN ep_csr->csr0l = %x\r\n", ep_csr->csr0l); - _dcd.pipe0.buf = NULL; - _dcd.pipe0.length = 0; - _dcd.pipe0.remaining = 0; + pipe0->buf = NULL; + pipe0->length = 0; + pipe0->remaining = 0; /* Clear RX FIFO and reverse the transaction direction */ ep_csr->csr0l = MUSB_CSRL0_RXRDYC | MUSB_CSRL0_DATAEND; } @@ -383,9 +413,9 @@ static void process_ep0(uint8_t rhport) { musb_regs_t* musb_regs = MUSB_REGS(rhport); musb_ep_csr_t* ep_csr = get_ep_csr(musb_regs, 0); + pipe_state_t* pipe0 = pipe_get(0, TUSB_DIR_OUT); uint_fast8_t csrl = ep_csr->csr0l; - // TU_LOG1(" EP0 ep_csr->csr0l = %x\r\n", csrl); // 21.1.5: endpoint 0 service routine as peripheral if (csrl & MUSB_CSRL0_STALLED) { @@ -398,13 +428,13 @@ static void process_ep0(uint8_t rhport) if (csrl & MUSB_CSRL0_SETEND) { TU_LOG1(" ABORT by the next packets\r\n"); ep_csr->csr0l = MUSB_CSRL0_SETENDC; - if (req != REQUEST_TYPE_INVALID && _dcd.pipe0.buf) { + if (req != REQUEST_TYPE_INVALID && pipe0->buf) { /* DATA stage was aborted by receiving STATUS or SETUP packet. */ - _dcd.pipe0.buf = NULL; + pipe0->buf = NULL; _dcd.setup_packet.bmRequestType = REQUEST_TYPE_INVALID; dcd_event_xfer_complete(rhport, req & TUSB_DIR_IN_MASK, - _dcd.pipe0.length - _dcd.pipe0.remaining, + pipe0->length - pipe0->remaining, XFER_RESULT_SUCCESS, true); } req = REQUEST_TYPE_INVALID; @@ -419,21 +449,21 @@ static void process_ep0(uint8_t rhport) process_setup_packet(rhport); return; } - if (_dcd.pipe0.buf) { + if (pipe0->buf) { /* DATA OUT */ const unsigned vld = ep_csr->count0; - const unsigned rem = _dcd.pipe0.remaining; + const unsigned rem = pipe0->remaining; const unsigned len = TU_MIN(TU_MIN(rem, 64), vld); volatile void *fifo_ptr = &musb_regs->fifo[0]; - tu_hwfifo_read(fifo_ptr, _dcd.pipe0.buf, len, NULL); + tu_hwfifo_read(fifo_ptr, pipe0->buf, len, NULL); - _dcd.pipe0.remaining = rem - len; + pipe0->remaining = rem - len; _dcd.remaining_ctrl -= len; - _dcd.pipe0.buf = NULL; + pipe0->buf = NULL; dcd_event_xfer_complete(rhport, tu_edpt_addr(0, TUSB_DIR_OUT), - _dcd.pipe0.length - _dcd.pipe0.remaining, + pipe0->length - pipe0->remaining, XFER_RESULT_SUCCESS, true); } return; @@ -450,49 +480,16 @@ static void process_ep0(uint8_t rhport) _dcd.setup_packet.bmRequestType = REQUEST_TYPE_INVALID; dcd_event_xfer_complete(rhport, tu_edpt_addr(0, TUSB_DIR_IN), - _dcd.pipe0.length - _dcd.pipe0.remaining, + pipe0->length - pipe0->remaining, XFER_RESULT_SUCCESS, true); return; } - if (_dcd.pipe0.buf) { + if (pipe0->buf) { /* DATA IN */ - _dcd.pipe0.buf = NULL; + pipe0->buf = NULL; dcd_event_xfer_complete(rhport, tu_edpt_addr(0, TUSB_DIR_IN), - _dcd.pipe0.length - _dcd.pipe0.remaining, - XFER_RESULT_SUCCESS, true); - } -} - -static void process_edpt_n(uint8_t rhport, uint_fast8_t ep_addr) -{ - bool completed; - const unsigned dir_in = tu_edpt_dir(ep_addr); - const unsigned epn = tu_edpt_number(ep_addr); - const unsigned epn_minus1 = epn - 1; - - musb_regs_t* musb_regs = MUSB_REGS(rhport); - musb_ep_csr_t* ep_csr = get_ep_csr(musb_regs, epn); - if (dir_in) { - // TU_LOG1(" TX CSRL%d = %x\r\n", epn, ep_csr->tx_csrl); - if (ep_csr->tx_csrl & MUSB_TXCSRL1_STALLED) { - ep_csr->tx_csrl &= ~(MUSB_TXCSRL1_STALLED | MUSB_TXCSRL1_UNDRN); - return; - } - completed = handle_xfer_in(musb_regs, (uint8_t) epn); - } else { - // TU_LOG1(" RX CSRL%d = %x\r\n", epn, ep_csr->rx_csrl); - if (ep_csr->rx_csrl & MUSB_RXCSRL1_STALLED) { - ep_csr->rx_csrl &= ~(MUSB_RXCSRL1_STALLED | MUSB_RXCSRL1_OVER); - return; - } - completed = handle_xfer_out(rhport, ep_addr); - } - - if (completed) { - pipe_state_t *pipe = &_dcd.pipe[dir_in][epn_minus1]; - dcd_event_xfer_complete(rhport, ep_addr, - pipe->length - pipe->remaining, + pipe0->length - pipe0->remaining, XFER_RESULT_SUCCESS, true); } } @@ -509,8 +506,9 @@ static void process_bus_reset(uint8_t rhport) { /* When bmRequestType is REQUEST_TYPE_INVALID(0xFF), a control transfer state is SETUP or STATUS stage. */ _dcd.setup_packet.bmRequestType = REQUEST_TYPE_INVALID; _dcd.status_out = 0; - /* When pipe0.buf has not NULL, DATA stage works in progress. */ - _dcd.pipe0.buf = NULL; + /* When EP0 pipe buf has not NULL, DATA stage works in progress. */ + pipe_state_t* pipe0 = pipe_get(0, TUSB_DIR_OUT); + pipe0->buf = NULL; musb->intr_txen = 1; /* Enable only EP0 */ musb->intr_rxen = 0; @@ -578,10 +576,11 @@ void dcd_set_address(uint8_t rhport, uint8_t dev_addr) (void)dev_addr; musb_regs_t* musb_regs = MUSB_REGS(rhport); musb_ep_csr_t* ep_csr = get_ep_csr(musb_regs, 0); + pipe_state_t* pipe0 = pipe_get(0, TUSB_DIR_OUT); - _dcd.pipe0.buf = NULL; - _dcd.pipe0.length = 0; - _dcd.pipe0.remaining = 0; + pipe0->buf = NULL; + pipe0->length = 0; + pipe0->remaining = 0; /* Clear RX FIFO to return ACK. */ ep_csr->csr0l = MUSB_CSRL0_RXRDYC | MUSB_CSRL0_DATAEND; } @@ -635,7 +634,7 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * ep_desc) { const unsigned dir_in = tu_edpt_dir(ep_addr); const unsigned mps = tu_edpt_packet_size(ep_desc); - pipe_state_t *pipe = &_dcd.pipe[dir_in][epn - 1]; + pipe_state_t *pipe = pipe_get(epn, dir_in); pipe->buf = NULL; pipe->length = 0; pipe->remaining = 0; @@ -681,7 +680,7 @@ bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const *ep_desc ) unsigned const ie = musb_dcd_get_int_enable(rhport); musb_dcd_int_disable(rhport); - pipe_state_t *pipe = &_dcd.pipe[dir_in][epn - 1]; + pipe_state_t *pipe = pipe_get(epn, dir_in); pipe->buf = NULL; pipe->length = 0; pipe->remaining = 0; @@ -792,13 +791,15 @@ void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) { if (0 == epn) { if (!ep_addr) { /* Ignore EP80 */ _dcd.setup_packet.bmRequestType = REQUEST_TYPE_INVALID; - _dcd.pipe0.buf = NULL; + pipe_state_t* pipe0 = pipe_get(0, TUSB_DIR_OUT); + pipe0->buf = NULL; ep_csr->csr0l = MUSB_CSRL0_STALL; } } else { const uint8_t is_rx = 1 - tu_edpt_dir(ep_addr); ep_csr->maxp_csr[is_rx].csrl = MUSB_CSRL_SEND_STALL(is_rx); - _dcd.pipe[tu_edpt_dir(ep_addr)][epn - 1].armed = false; + pipe_state_t* pipe = pipe_get(epn, tu_edpt_dir(ep_addr)); + pipe->armed = false; } if (ie) musb_dcd_int_enable(rhport); @@ -858,16 +859,16 @@ void dcd_int_handler(uint8_t rhport) { intr_tx &= ~TU_BIT(0); } while (intr_tx) { - unsigned const num = __builtin_ctz(intr_tx); - process_edpt_n(rhport, tu_edpt_addr(num, TUSB_DIR_IN)); - intr_tx &= ~TU_BIT(num); + const unsigned epnum = __builtin_ctz(intr_tx); + process_epin(rhport, musb_regs, epnum); + intr_tx &= ~TU_BIT(epnum); } intr_rx &= musb_regs->intr_rxen; /* Clear disabled interrupts */ while (intr_rx) { - unsigned const num = __builtin_ctz(intr_rx); - process_edpt_n(rhport, tu_edpt_addr(num, TUSB_DIR_OUT)); - intr_rx &= ~TU_BIT(num); + unsigned const epnum = __builtin_ctz(intr_rx); + process_epout(rhport, musb_regs, epnum); + intr_rx &= ~TU_BIT(epnum); } musb_regs->index = saved_index; // restore endpoint index -- cgit v1.3.1 From 100cfd6360ddd0c94a0aaeaa2da05449c0648dc1 Mon Sep 17 00:00:00 2001 From: hathach Date: Tue, 21 Apr 2026 16:36:31 +0700 Subject: minor clean up --- src/portable/mentor/musb/dcd_musb.c | 48 ++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 27 deletions(-) (limited to 'src') diff --git a/src/portable/mentor/musb/dcd_musb.c b/src/portable/mentor/musb/dcd_musb.c index bf60adbe7..283d8b257 100644 --- a/src/portable/mentor/musb/dcd_musb.c +++ b/src/portable/mentor/musb/dcd_musb.c @@ -221,8 +221,7 @@ static void process_setup_packet(uint8_t rhport) { } // write to txfifo using pipe_state_t info -static void pipe_write(musb_regs_t* musb_regs, uint8_t epnum) { - pipe_state_t* pipe = pipe_get(epnum, TUSB_DIR_IN); +static void pipe_write(musb_regs_t* musb_regs, pipe_state_t* pipe, uint8_t epnum) { musb_ep_csr_t* ep_csr = get_ep_csr(musb_regs, epnum); const unsigned mps = ep_csr->tx_maxp; const unsigned rem = pipe->remaining; @@ -257,7 +256,7 @@ static void process_epin(uint8_t rhport, musb_regs_t *musb_regs, uint8_t epnum) dcd_event_xfer_complete(rhport, tu_edpt_addr(epnum, TUSB_DIR_IN), xferred_len, XFER_RESULT_SUCCESS, true); return; } - pipe_write(musb_regs, epnum); + pipe_write(musb_regs, pipe, epnum); } static void process_epout(uint8_t rhport, musb_regs_t *musb_regs, uint8_t epnum) { @@ -309,35 +308,34 @@ static void process_epout(uint8_t rhport, musb_regs_t *musb_regs, uint8_t epnum) } } -static bool edpt_n_xfer(uint8_t rhport, uint8_t ep_addr, void *buffer, uint16_t total_bytes, bool use_fifo) -{ - unsigned epnum = tu_edpt_number(ep_addr); - unsigned dir_in = tu_edpt_dir(ep_addr); +static bool edpt_n_xfer(uint8_t rhport, uint8_t ep_addr, void *buffer, uint16_t total_bytes, bool use_fifo) { + const uint8_t epnum = tu_edpt_number(ep_addr); + const unsigned dir_in = tu_edpt_dir(ep_addr); pipe_state_t *pipe = pipe_get(epnum, dir_in); if (use_fifo) { - pipe->fifo = (tu_fifo_t *) buffer; + pipe->fifo = (tu_fifo_t *)buffer; } else { - pipe->buf = (uint8_t *) buffer; + pipe->buf = (uint8_t *)buffer; } - pipe->length = total_bytes; - pipe->remaining = total_bytes; - pipe->use_fifo = use_fifo; - pipe->armed = true; + pipe->length = total_bytes; + pipe->remaining = total_bytes; + pipe->use_fifo = use_fifo; + pipe->armed = true; + + musb_regs_t *musb_regs = MUSB_REGS(rhport); + musb_ep_csr_t *ep_csr = get_ep_csr(musb_regs, epnum); if (dir_in) { - pipe_write(MUSB_REGS(rhport), (uint8_t) epnum); + pipe_write(musb_regs, pipe, epnum); } else { - musb_regs_t* musb_regs = MUSB_REGS(rhport); - musb_ep_csr_t* ep_csr = get_ep_csr(musb_regs, epnum); - // Re-enable Rx interrupt (may have been masked by the no-buffer path in process_epout) - musb_regs->intr_rxen |= (uint16_t) TU_BIT(epnum); + musb_regs->intr_rxen |= (uint16_t)TU_BIT(epnum); // Drain any packet staged in the Rx FIFO from a prior no-buffer interrupt. // process_epout() fires dcd_event_xfer_complete() itself if the drain completes. if (ep_csr->rx_csrl & MUSB_RXCSRL1_RXRDY) { - process_epout(rhport, musb_regs, (uint8_t) epnum); + process_epout(rhport, musb_regs, epnum); } } return true; @@ -622,19 +620,15 @@ void dcd_sof_enable(uint8_t rhport, bool en) //--------------------------------------------------------------------+ // Endpoint API //--------------------------------------------------------------------+ -// static void edpt_setup(musb_regs_t* musb, uint8_t ep_addr, uint8_t ep_type, uint16_t ep_size){ -// const unsigned epn = tu_edpt_number(ep_addr); -// const unsigned dir_in = tu_edpt_dir(ep_addr); -// } // Configure endpoint's registers according to descriptor bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * ep_desc) { const unsigned ep_addr = ep_desc->bEndpointAddress; const unsigned epn = tu_edpt_number(ep_addr); - const unsigned dir_in = tu_edpt_dir(ep_addr); + const unsigned epdir = tu_edpt_dir(ep_addr); const unsigned mps = tu_edpt_packet_size(ep_desc); - pipe_state_t *pipe = pipe_get(epn, dir_in); + pipe_state_t *pipe = pipe_get(epn, epdir); pipe->buf = NULL; pipe->length = 0; pipe->remaining = 0; @@ -642,13 +636,13 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * ep_desc) { musb_regs_t* musb = MUSB_REGS(rhport); musb_ep_csr_t* ep_csr = get_ep_csr(musb, epn); - const uint8_t is_rx = 1 - dir_in; + const uint8_t is_rx = (1 - epdir); musb_ep_maxp_csr_t* maxp_csr = &ep_csr->maxp_csr[is_rx]; maxp_csr->maxp = mps; maxp_csr->csrh = 0; #if MUSB_CFG_SHARED_FIFO - if (dir_in) { + if (epdir) { maxp_csr->csrh |= MUSB_CSRH_TX_MODE; } #endif -- cgit v1.3.1 From d0c550cadceff3fdf30060f4ac0ebf919e5934a1 Mon Sep 17 00:00:00 2001 From: hathach Date: Tue, 21 Apr 2026 19:44:10 +0700 Subject: enable double buffer for tm4c --- hw/bsp/tm4c/family.c | 8 +++++++ src/portable/mentor/musb/dcd_musb.c | 43 ++++++++++++++++++++++++++---------- src/portable/mentor/musb/musb_type.h | 2 +- test/hil/hil_test.py | 2 +- 4 files changed, 41 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/hw/bsp/tm4c/family.c b/hw/bsp/tm4c/family.c index 6988a264e..c5e4bd64e 100644 --- a/hw/bsp/tm4c/family.c +++ b/hw/bsp/tm4c/family.c @@ -58,6 +58,14 @@ static void board_button_init(GPIOA_Type* port, uint8_t PinMsk) { /* Set direction */ port->DIR &= ~PinMsk; + + /* Enable internal pull so the idle state is deterministic. LaunchPad buttons + * connect the pin to GND when pressed (active-low) and require a pull-up. */ +#if BUTTON_STATE_ACTIVE == 0 + port->PUR |= PinMsk; +#else + port->PDR |= PinMsk; +#endif } static void board_led_init(GPIOA_Type* port, uint8_t PinMsk, uint8_t dirmsk) { diff --git a/src/portable/mentor/musb/dcd_musb.c b/src/portable/mentor/musb/dcd_musb.c index 283d8b257..02d9c2f66 100644 --- a/src/portable/mentor/musb/dcd_musb.c +++ b/src/portable/mentor/musb/dcd_musb.c @@ -140,7 +140,6 @@ TU_ATTR_ALWAYS_INLINE static inline void hwfifo_reset(musb_regs_t* musb, unsigne TU_ATTR_ALWAYS_INLINE static inline bool hwfifo_config(musb_regs_t* musb, unsigned epnum, unsigned is_rx, unsigned mps, bool double_packet) { - (void) epnum; uint8_t ffsize = hwfifo_byte2size(mps); mps = 8 << ffsize; // round up to the next power of 2 @@ -153,6 +152,13 @@ TU_ATTR_ALWAYS_INLINE static inline bool hwfifo_config(musb_regs_t* musb, unsign musb->fifo_addr[is_rx] = alloced_fifo_bytes / 8; musb->fifo_size[is_rx] = ffsize; + volatile uint16_t* dp_disable = is_rx ? &musb->rx_doulbe_packet_disable : &musb->tx_double_packet_disable; + if (double_packet) { + *dp_disable &= ~(1u << epnum); + } else { + *dp_disable |= (1u << epnum); + } + alloced_fifo_bytes += mps; return true; } @@ -167,17 +173,22 @@ TU_ATTR_ALWAYS_INLINE static inline void hwfifo_reset(musb_regs_t* musb, unsigne TU_ATTR_ALWAYS_INLINE static inline bool hwfifo_config(musb_regs_t* musb, unsigned epnum, unsigned is_rx, unsigned mps, bool double_packet) { (void) epnum; (void) mps; - if (!double_packet) { - #if defined(TUP_USBIP_MUSB_ADI) - musb->indexed_csr.maxp_csr[is_rx].csrh |= MUSB_CSRH_DISABLE_DOUBLE_PACKET(is_rx); - #else - if (is_rx) { - musb->rx_doulbe_packet_disable |= 1u << epnum; - } else { - musb->tx_double_packet_disable |= 1u << epnum; - } - #endif + + #if defined(TUP_USBIP_MUSB_ADI) + volatile uint8_t* csrh = &musb->indexed_csr.maxp_csr[is_rx].csrh; + if (double_packet) { + *csrh &= ~MUSB_CSRH_DISABLE_DOUBLE_PACKET; + } else { + *csrh |= MUSB_CSRH_DISABLE_DOUBLE_PACKET; } + #else + volatile uint16_t* dp_disable = is_rx ? &musb->rx_doulbe_packet_disable : &musb->tx_double_packet_disable; + if (double_packet) { + *dp_disable &= ~(1u << epnum); + } else { + *dp_disable |= (1u << epnum); + } + #endif return true; } @@ -250,6 +261,14 @@ static void process_epin(uint8_t rhport, musb_regs_t *musb_regs, uint8_t epnum) pipe_state_t* pipe = pipe_get(epnum, TUSB_DIR_IN); if (pipe->remaining == 0) { + // All bytes have been loaded into the FIFO. With double-packet buffering a + // second packet may still be waiting in the FIFO when this IRQ fires (the + // hardware signals TXRDY clear as soon as a slot frees, not when the wire + // transfer finishes). Defer completion until FIFONE == 0 so we don't emit + // a duplicate xfer_complete before the final packet has been sent. + if (ep_csr->tx_csrl & MUSB_TXCSRL1_FIFONE) { + return; + } const uint16_t xferred_len = pipe->length; pipe->buf = NULL; pipe->armed = false; @@ -649,7 +668,7 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * ep_desc) { hwfifo_flush(musb, epn, is_rx, true); - TU_ASSERT(hwfifo_config(musb, epn, is_rx, mps, false)); + TU_ASSERT(hwfifo_config(musb, epn, is_rx, mps, ep_desc->bmAttributes.xfer == TUSB_XFER_BULK)); musb->intren_ep[is_rx] |= TU_BIT(epn); return true; diff --git a/src/portable/mentor/musb/musb_type.h b/src/portable/mentor/musb/musb_type.h index b2f6492fa..6a85d2ca8 100644 --- a/src/portable/mentor/musb/musb_type.h +++ b/src/portable/mentor/musb/musb_type.h @@ -336,7 +336,7 @@ TU_ATTR_ALWAYS_INLINE static inline musb_ep_csr_t* get_ep_csr(musb_regs_t* musb_ #define MUSB_CSRL_CLEAR_DATA_TOGGLE(_rx) (1u << ((_rx) ? 7 : 6)) // 0x13, 0x17: TX/RX CSRH -#define MUSB_CSRH_DISABLE_DOUBLE_PACKET(_rx) (1u << 1) +#define MUSB_CSRH_DISABLE_DOUBLE_PACKET (1u << 1) #define MUSB_CSRH_TX_MODE (1u << 5) // 1 = TX, 0 = RX. only relevant for SHARED FIFO #define MUSB_CSRH_ISO (1u << 6) diff --git a/test/hil/hil_test.py b/test/hil/hil_test.py index dfe09bf23..58116fb67 100755 --- a/test/hil/hil_test.py +++ b/test/hil/hil_test.py @@ -1209,7 +1209,7 @@ device_tests = [ 'device/printer_to_cdc', 'device/midi_test', 'device/mtp', - # 'device/net_lwip_webserver' + 'device/net_lwip_webserver' ] dual_tests = [ -- cgit v1.3.1 From 8513c50231e1935657737b42a739e96c6d0dd154 Mon Sep 17 00:00:00 2001 From: hathach Date: Wed, 22 Apr 2026 11:53:02 +0700 Subject: musb implement double buffer for tx --- examples/device/cdc_msc/src/usb_descriptors.c | 22 +++++++--- examples/device/dfu/skip.txt | 1 - .../net_lwip_webserver/src/usb_descriptors.c | 10 ++++- src/portable/mentor/musb/dcd_musb.c | 49 +++++++++++++--------- src/portable/mentor/musb/musb_type.h | 10 +++++ 5 files changed, 64 insertions(+), 28 deletions(-) (limited to 'src') diff --git a/examples/device/cdc_msc/src/usb_descriptors.c b/examples/device/cdc_msc/src/usb_descriptors.c index b738e7d12..5dc80dee3 100644 --- a/examples/device/cdc_msc/src/usb_descriptors.c +++ b/examples/device/cdc_msc/src/usb_descriptors.c @@ -105,12 +105,22 @@ enum { #elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY // MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h // e.g EP1 OUT & EP1 IN cannot exist together - #define EPNUM_CDC_NOTIF 0x81 - #define EPNUM_CDC_OUT 0x02 - #define EPNUM_CDC_IN 0x83 - - #define EPNUM_MSC_OUT 0x04 - #define EPNUM_MSC_IN 0x85 + #if TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002) + // Put bulk on EP>=8 so the 2048/4096-byte FIFOs can back double packet buffering + #define EPNUM_CDC_NOTIF 0x81 + #define EPNUM_CDC_OUT 0x08 + #define EPNUM_CDC_IN 0x89 + + #define EPNUM_MSC_OUT 0x0A + #define EPNUM_MSC_IN 0x8B + #else + #define EPNUM_CDC_NOTIF 0x81 + #define EPNUM_CDC_OUT 0x02 + #define EPNUM_CDC_IN 0x83 + + #define EPNUM_MSC_OUT 0x04 + #define EPNUM_MSC_IN 0x85 + #endif #else #define EPNUM_CDC_NOTIF 0x81 diff --git a/examples/device/dfu/skip.txt b/examples/device/dfu/skip.txt index 79d3da9d2..ccff857ac 100644 --- a/examples/device/dfu/skip.txt +++ b/examples/device/dfu/skip.txt @@ -1,3 +1,2 @@ -mcu:TM4C mcu:BCM2835 family:espressif diff --git a/examples/device/net_lwip_webserver/src/usb_descriptors.c b/examples/device/net_lwip_webserver/src/usb_descriptors.c index 8cfef41a6..e97b103f9 100644 --- a/examples/device/net_lwip_webserver/src/usb_descriptors.c +++ b/examples/device/net_lwip_webserver/src/usb_descriptors.c @@ -122,11 +122,19 @@ const uint8_t *tud_descriptor_device_cb(void) { #define EPNUM_NET_IN 0x81 #elif CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY -// MCUs that don't support a same endpoint number with different direction IN and OUT defined in tusb_mcu.h +// MCUs that don't support the same endpoint number with different direction IN and OUT defined in tusb_mcu.h // e.g EP1 OUT & EP1 IN cannot exist together + +#if TU_CHECK_MCU(OPT_MCU_MAX32650, OPT_MCU_MAX32666, OPT_MCU_MAX32690, OPT_MCU_MAX78002) +// endpoint 8,9 has FIFO of 2048 bytes +#define EPNUM_NET_NOTIF 0x81 +#define EPNUM_NET_OUT 0x08 +#define EPNUM_NET_IN 0x89 +#else #define EPNUM_NET_NOTIF 0x81 #define EPNUM_NET_OUT 0x02 #define EPNUM_NET_IN 0x83 +#endif #else #define EPNUM_NET_NOTIF 0x81 diff --git a/src/portable/mentor/musb/dcd_musb.c b/src/portable/mentor/musb/dcd_musb.c index 02d9c2f66..667102bc5 100644 --- a/src/portable/mentor/musb/dcd_musb.c +++ b/src/portable/mentor/musb/dcd_musb.c @@ -172,9 +172,15 @@ TU_ATTR_ALWAYS_INLINE static inline void hwfifo_reset(musb_regs_t* musb, unsigne TU_ATTR_ALWAYS_INLINE static inline bool hwfifo_config(musb_regs_t* musb, unsigned epnum, unsigned is_rx, unsigned mps, bool double_packet) { - (void) epnum; (void) mps; + (void) mps; #if defined(TUP_USBIP_MUSB_ADI) + // AnalogDevice FIFO sizes: EP1..7 = 512 B, EP8..9 = 2048 B, EP10..11 = 4096 B. + // DPB requires FIFO >= 2 * MPS. For HS bulk (MPS=512) only EP >= 8 qualifies. + // Force single-buffered on EP < 8 even if the caller requested DPB. + if (epnum < 8 && (musb->power & MUSB_POWER_HSMODE)) { + double_packet = false; + } volatile uint8_t* csrh = &musb->indexed_csr.maxp_csr[is_rx].csrh; if (double_packet) { *csrh &= ~MUSB_CSRH_DISABLE_DOUBLE_PACKET; @@ -234,7 +240,7 @@ static void process_setup_packet(uint8_t rhport) { // write to txfifo using pipe_state_t info static void pipe_write(musb_regs_t* musb_regs, pipe_state_t* pipe, uint8_t epnum) { musb_ep_csr_t* ep_csr = get_ep_csr(musb_regs, epnum); - const unsigned mps = ep_csr->tx_maxp; + const unsigned mps = ep_csr->tx_maxp & MUSB_TXMAXP_PACKET_SIZE_MASK; const unsigned rem = pipe->remaining; const unsigned len = TU_MIN(mps, rem); volatile void *fifo_ptr = &musb_regs->fifo[epnum]; @@ -260,7 +266,9 @@ static void process_epin(uint8_t rhport, musb_regs_t *musb_regs, uint8_t epnum) } pipe_state_t* pipe = pipe_get(epnum, TUSB_DIR_IN); - if (pipe->remaining == 0) { + if (pipe->remaining > 0) { + pipe_write(musb_regs, pipe, epnum); + } else { // All bytes have been loaded into the FIFO. With double-packet buffering a // second packet may still be waiting in the FIFO when this IRQ fires (the // hardware signals TXRDY clear as soon as a slot frees, not when the wire @@ -273,12 +281,10 @@ static void process_epin(uint8_t rhport, musb_regs_t *musb_regs, uint8_t epnum) pipe->buf = NULL; pipe->armed = false; dcd_event_xfer_complete(rhport, tu_edpt_addr(epnum, TUSB_DIR_IN), xferred_len, XFER_RESULT_SUCCESS, true); - return; } - pipe_write(musb_regs, pipe, epnum); } -static void process_epout(uint8_t rhport, musb_regs_t *musb_regs, uint8_t epnum) { +static void process_epout(uint8_t rhport, musb_regs_t *musb_regs, uint8_t epnum, bool is_isr) { musb_ep_csr_t* ep_csr = get_ep_csr(musb_regs, epnum); if (ep_csr->rx_csrl & MUSB_RXCSRL1_STALLED) { ep_csr->rx_csrl &= ~(MUSB_RXCSRL1_STALLED | MUSB_RXCSRL1_OVER); @@ -302,7 +308,7 @@ static void process_epout(uint8_t rhport, musb_regs_t *musb_regs, uint8_t epnum) return; } - const unsigned mps = ep_csr->rx_maxp; + const unsigned mps = ep_csr->rx_maxp & MUSB_RXMAXP_PACKET_SIZE_MASK; const unsigned rem = pipe->remaining; const unsigned vld = ep_csr->rx_count; const unsigned len = TU_MIN(TU_MIN(rem, mps), vld); @@ -323,11 +329,11 @@ static void process_epout(uint8_t rhport, musb_regs_t *musb_regs, uint8_t epnum) pipe->buf = NULL; pipe->armed = false; - dcd_event_xfer_complete(rhport, tu_edpt_addr(epnum, TUSB_DIR_OUT), xferred_len, XFER_RESULT_SUCCESS, true); + dcd_event_xfer_complete(rhport, tu_edpt_addr(epnum, TUSB_DIR_OUT), xferred_len, XFER_RESULT_SUCCESS, is_isr); } } -static bool edpt_n_xfer(uint8_t rhport, uint8_t ep_addr, void *buffer, uint16_t total_bytes, bool use_fifo) { +static bool edpt_n_xfer(uint8_t rhport, uint8_t ep_addr, void *buffer, uint16_t total_bytes, bool use_fifo, bool is_isr) { const uint8_t epnum = tu_edpt_number(ep_addr); const unsigned dir_in = tu_edpt_dir(ep_addr); @@ -354,13 +360,13 @@ static bool edpt_n_xfer(uint8_t rhport, uint8_t ep_addr, void *buffer, uint16_t // Drain any packet staged in the Rx FIFO from a prior no-buffer interrupt. // process_epout() fires dcd_event_xfer_complete() itself if the drain completes. if (ep_csr->rx_csrl & MUSB_RXCSRL1_RXRDY) { - process_epout(rhport, musb_regs, epnum); + process_epout(rhport, musb_regs, epnum, is_isr); } } return true; } -static bool edpt0_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes) +static bool edpt0_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes, bool is_isr) { (void)rhport; TU_ASSERT(total_bytes <= 64); /* Current implementation supports for only up to 64 bytes. */ @@ -380,7 +386,7 @@ static bool edpt0_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_ // TU_LOG1(" STATUS OUT ep_csr->csr0l = %x\r\n", ep_csr->csr0l); _dcd.status_out = 0; if (req == REQUEST_TYPE_INVALID) { - dcd_event_xfer_complete(rhport, ep_addr, total_bytes, XFER_RESULT_SUCCESS, false); + dcd_event_xfer_complete(rhport, ep_addr, total_bytes, XFER_RESULT_SUCCESS, is_isr); } else { /* The next setup packet has already been received, it aborts * invoking callback function to avoid confusing TUSB stack. */ @@ -755,18 +761,16 @@ void dcd_edpt_close_all(uint8_t rhport) // Submit a transfer, When complete dcd_event_xfer_complete() is invoked to notify the stack bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes, bool is_isr) { - (void) is_isr; (void)rhport; bool ret; - // TU_LOG1("X %x %d\r\n", ep_addr, total_bytes); unsigned const epnum = tu_edpt_number(ep_addr); unsigned const ie = musb_dcd_get_int_enable(rhport); musb_dcd_int_disable(rhport); if (epnum) { - ret = edpt_n_xfer(rhport, ep_addr, buffer, total_bytes, false); + ret = edpt_n_xfer(rhport, ep_addr, buffer, total_bytes, false, is_isr); } else { - ret = edpt0_xfer(rhport, ep_addr, buffer, total_bytes); + ret = edpt0_xfer(rhport, ep_addr, buffer, total_bytes, is_isr); } if (ie) { @@ -779,15 +783,13 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t t // - optional, however, must be listed in usbd.c 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; (void)rhport; bool ret; - // TU_LOG1("X %x %d\r\n", ep_addr, total_bytes); unsigned const epnum = tu_edpt_number(ep_addr); TU_ASSERT(epnum); unsigned const ie = musb_dcd_get_int_enable(rhport); musb_dcd_int_disable(rhport); - ret = edpt_n_xfer(rhport, ep_addr, ff, total_bytes, true); + ret = edpt_n_xfer(rhport, ep_addr, ff, total_bytes, true, is_isr); if (ie) musb_dcd_int_enable(rhport); return ret; } @@ -871,16 +873,23 @@ void dcd_int_handler(uint8_t rhport) { process_ep0(rhport); intr_tx &= ~TU_BIT(0); } + while (intr_tx) { const unsigned epnum = __builtin_ctz(intr_tx); process_epin(rhport, musb_regs, epnum); intr_tx &= ~TU_BIT(epnum); + + // for Double-buffered endpoint: TxPktRdy is cleared and interrupt is generated when we write the first packet + uint_fast8_t new_intr_tx = musb_regs->intr_tx; + new_intr_tx &= musb_regs->intr_txen; + + intr_tx |= new_intr_tx; } intr_rx &= musb_regs->intr_rxen; /* Clear disabled interrupts */ while (intr_rx) { unsigned const epnum = __builtin_ctz(intr_rx); - process_epout(rhport, musb_regs, epnum); + process_epout(rhport, musb_regs, epnum, true); intr_rx &= ~TU_BIT(epnum); } diff --git a/src/portable/mentor/musb/musb_type.h b/src/portable/mentor/musb/musb_type.h index 6a85d2ca8..dd1cd6ded 100644 --- a/src/portable/mentor/musb/musb_type.h +++ b/src/portable/mentor/musb/musb_type.h @@ -566,6 +566,16 @@ TU_ATTR_ALWAYS_INLINE static inline musb_ep_csr_t* get_ep_csr(musb_regs_t* musb_ #define MUSB_NAKLMT_NAKLMT_M 0x001F // EP0 NAK Limit #define MUSB_NAKLMT_NAKLMT_S 0 +//***************************************************************************** +// +// The following are defines for the bit fields in the MUSB_O_TXMAXP / MUSB_O_RXMAXP +// registers. Bits [10:0] carry the maximum packet size; bits [15:11] carry +// numpackminus1 (HB-iso / HS-bulk multiplier - 1). +// +//***************************************************************************** +#define MUSB_TXMAXP_PACKET_SIZE_MASK 0x07FFu +#define MUSB_RXMAXP_PACKET_SIZE_MASK 0x07FFu + //***************************************************************************** // // The following are defines for the bit fields in the MUSB_O_TXCSRL1 register. -- cgit v1.3.1 From 776f613dbf9ebb14e7dab6529db96a273157a943 Mon Sep 17 00:00:00 2001 From: "igor.masar" Date: Wed, 22 Apr 2026 14:22:23 +0200 Subject: Add ESP32-S31 as a supported MCU in TinyUSB --- README.rst | 2 ++ hw/bsp/espressif/boards/family.c | 8 ++------ hw/bsp/espressif/family.cmake | 17 ++++++++++++++--- src/common/tusb_mcu.h | 18 +++++++++++++++++- src/portable/synopsys/dwc2/dcd_dwc2.c | 10 ++++++---- src/portable/synopsys/dwc2/dwc2_esp32.h | 12 ++++++++++++ src/tusb_option.h | 1 + 7 files changed, 54 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/README.rst b/README.rst index 6be9d4873..04998abaa 100644 --- a/README.rst +++ b/README.rst @@ -157,6 +157,8 @@ Supported CPUs | Espressif | S2, S3, H4 | ✔ | ✔ | ✖ | dwc2 | | | ESP32 +-----------------------------+--------+------+-----------+------------------------+--------------------+ | | P4 | ✔ | ✔ | ✔ | dwc2 | | +| +-----------------------------+--------+------+-----------+------------------------+--------------------+ +| | S31 | ✔ | ✔ | ✔ | dwc2 | | +--------------+-----------------------------+--------+------+-----------+------------------------+--------------------+ | GigaDevice | GD32VF103 | ✔ | | ✖ | dwc2 | | +--------------+-----------------------------+--------+------+-----------+------------------------+--------------------+ diff --git a/hw/bsp/espressif/boards/family.c b/hw/bsp/espressif/boards/family.c index 48b1253b6..4b28cecf2 100644 --- a/hw/bsp/espressif/boards/family.c +++ b/hw/bsp/espressif/boards/family.c @@ -49,7 +49,7 @@ static led_strip_handle_t led_strip; static void max3421_init(void); #endif -#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3, OPT_MCU_ESP32H4, OPT_MCU_ESP32P4) +#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3, OPT_MCU_ESP32H4, OPT_MCU_ESP32P4, OPT_MCU_ESP32S31) static bool usb_init(uint8_t rhport, bool is_host); #endif @@ -111,10 +111,6 @@ void board_init(void) { #endif } -#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3, OPT_MCU_ESP32H4) - -#endif - //--------------------------------------------------------------------+ // Board porting API //--------------------------------------------------------------------+ @@ -178,7 +174,7 @@ void board_reset_to_bootloader(void) { // PHY Init //-------------------------------------------------------------------- -#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3, OPT_MCU_ESP32H4, OPT_MCU_ESP32P4) +#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3, OPT_MCU_ESP32H4, OPT_MCU_ESP32P4, OPT_MCU_ESP32S31) #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) #include "esp_private/usb_phy.h" diff --git a/hw/bsp/espressif/family.cmake b/hw/bsp/espressif/family.cmake index ca9eadaf6..30d5a6ac9 100644 --- a/hw/bsp/espressif/family.cmake +++ b/hw/bsp/espressif/family.cmake @@ -2,7 +2,10 @@ include("${CMAKE_CURRENT_LIST_DIR}/boards/${BOARD}/board.cmake") string(TOUPPER ${IDF_TARGET} FAMILY_MCUS) -# Device port default to Port1 for P4 (highspeed), Port0 for others (fullspeed) +# Device/host port defaults: +# - ESP32-P4 uses Port1 (highspeed) +# - ESP32-S31 uses Port0 (highspeed) +# - Other targets use Port0 and derive the default speed from RHPORT_SPEED set(RHPORT_SPEED OPT_MODE_FULL_SPEED OPT_MODE_HIGH_SPEED) if (NOT DEFINED RHPORT_DEVICE) @@ -22,10 +25,18 @@ if (NOT DEFINED RHPORT_HOST) endif() if (NOT DEFINED RHPORT_DEVICE_SPEED) - list(GET RHPORT_SPEED ${RHPORT_DEVICE} RHPORT_DEVICE_SPEED) + if (IDF_TARGET STREQUAL "esp32s31") + set(RHPORT_DEVICE_SPEED OPT_MODE_HIGH_SPEED) + else () + list(GET RHPORT_SPEED ${RHPORT_DEVICE} RHPORT_DEVICE_SPEED) + endif () endif () if (NOT DEFINED RHPORT_HOST_SPEED) - list(GET RHPORT_SPEED ${RHPORT_HOST} RHPORT_HOST_SPEED) + if (IDF_TARGET STREQUAL "esp32s31") + set(RHPORT_HOST_SPEED OPT_MODE_HIGH_SPEED) + else () + list(GET RHPORT_SPEED ${RHPORT_HOST} RHPORT_HOST_SPEED) + endif () endif () # Add example src and bsp directories diff --git a/src/common/tusb_mcu.h b/src/common/tusb_mcu.h index 77a0bbf1d..a17c76a3b 100644 --- a/src/common/tusb_mcu.h +++ b/src/common/tusb_mcu.h @@ -455,7 +455,7 @@ #define CFG_TUSB_OS_INC_PATH_DEFAULT freertos/ // clang-format on - #if CFG_TUSB_MCU == OPT_MCU_ESP32S3 + #if CFG_TUSB_MCU == OPT_MCU_ESP32S3 || CFG_TUSB_MCU == OPT_MCU_ESP32H4 #define TUP_MCU_MULTIPLE_CORE 1 #endif @@ -476,6 +476,22 @@ #define CFG_TUH_MEM_DCACHE_ENABLE_DEFAULT CFG_TUH_DWC2_DMA_ENABLE #define CFG_TUSB_MEM_DCACHE_LINE_SIZE_DEFAULT 64 +#elif TU_CHECK_MCU(OPT_MCU_ESP32S31) + #define TUP_USBIP_DWC2 + #define TUP_USBIP_DWC2_ESP32 + #define TUP_RHPORT_HIGHSPEED 1 + #define TUP_DCD_ENDPOINT_MAX 16 + + // clang-format off + #define CFG_TUSB_OS_INC_PATH_DEFAULT freertos/ + // clang-format on + + #define TUP_MCU_MULTIPLE_CORE 1 + + // Disable slave if DMA is enabled + #define CFG_TUD_DWC2_SLAVE_ENABLE_DEFAULT !CFG_TUD_DWC2_DMA_ENABLE + #define CFG_TUH_DWC2_SLAVE_ENABLE_DEFAULT !CFG_TUH_DWC2_DMA_ENABLE + #elif TU_CHECK_MCU(OPT_MCU_ESP32, OPT_MCU_ESP32C2, OPT_MCU_ESP32C3, OPT_MCU_ESP32C5, OPT_MCU_ESP32C6, \ OPT_MCU_ESP32C61, OPT_MCU_ESP32H2) #if (CFG_TUD_ENABLED || !(defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421)) diff --git a/src/portable/synopsys/dwc2/dcd_dwc2.c b/src/portable/synopsys/dwc2/dcd_dwc2.c index 9a9c734a0..c30af6196 100644 --- a/src/portable/synopsys/dwc2/dcd_dwc2.c +++ b/src/portable/synopsys/dwc2/dcd_dwc2.c @@ -541,8 +541,9 @@ void dcd_remote_wakeup(uint8_t rhport) { void dcd_connect(uint8_t rhport) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); -#ifdef TUP_USBIP_DWC2_ESP32 - // On ESP32-P4 HS PHY, do not write to USB_WRAP register which belongs to FS PHY +#if defined(TUP_USBIP_DWC2_ESP32) && !TU_CHECK_MCU(OPT_MCU_ESP32S31) + // S31 is excluded at compile time (no USB_WRAP peripheral). + // On P4, the HS PHY (port 1) must not touch USB_WRAP which belongs to the FS PHY. if (rhport == 0) { usb_wrap_otg_conf_reg_t conf = USB_WRAP.otg_conf; conf.pad_pull_override = 0; @@ -560,8 +561,9 @@ void dcd_connect(uint8_t rhport) { void dcd_disconnect(uint8_t rhport) { dwc2_regs_t* dwc2 = DWC2_REG(rhport); -#ifdef TUP_USBIP_DWC2_ESP32 - // On ESP32-P4 HS PHY, do not write to USB_WRAP register which belongs to FS PHY +#if defined(TUP_USBIP_DWC2_ESP32) && !TU_CHECK_MCU(OPT_MCU_ESP32S31) + // S31 is excluded at compile time (no USB_WRAP peripheral). + // On P4, the HS PHY (port 1) must not touch USB_WRAP which belongs to the FS PHY. if (rhport == 0) { usb_wrap_otg_conf_reg_t conf = USB_WRAP.otg_conf; conf.pad_pull_override = 1; diff --git a/src/portable/synopsys/dwc2/dwc2_esp32.h b/src/portable/synopsys/dwc2/dwc2_esp32.h index 6a10dc7f8..436f8dc30 100644 --- a/src/portable/synopsys/dwc2/dwc2_esp32.h +++ b/src/portable/synopsys/dwc2/dwc2_esp32.h @@ -37,7 +37,11 @@ #include "esp_intr_alloc.h" #include "soc/periph_defs.h" + +// ESP32-S31 does not have USB_WRAP peripheral (HS-only with UTMI PHY) +#if !TU_CHECK_MCU(OPT_MCU_ESP32S31) #include "soc/usb_wrap_struct.h" +#endif #if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3) #define DWC2_FS_REG_BASE 0x60080000UL @@ -75,6 +79,14 @@ static const dwc2_controller_t _dwc2_controller[] = { { .reg_base = DWC2_FS_REG_BASE, .irqnum = ETS_USB_OTG11_CH0_INTR_SOURCE, .ep_count = 7, .ep_in_count = 5, .otg_dfifo_depth = 256 }, { .reg_base = DWC2_HS_REG_BASE, .irqnum = ETS_USB_OTG_INTR_SOURCE, .ep_count = 16, .ep_in_count = 8, .otg_dfifo_depth = 1024 } }; + +#elif TU_CHECK_MCU(OPT_MCU_ESP32S31) +#define DWC2_HS_REG_BASE 0x20300000UL +#define DWC2_EP_MAX 16 + +static const dwc2_controller_t _dwc2_controller[] = { + { .reg_base = DWC2_HS_REG_BASE, .irqnum = ETS_USB_OTGHS_INTR_SOURCE, .ep_count = 16, .ep_in_count = 8, .otg_dfifo_depth = 1024 } +}; #endif //--------------------------------------------------------------------+ diff --git a/src/tusb_option.h b/src/tusb_option.h index dd7af76f6..154f8e2a4 100644 --- a/src/tusb_option.h +++ b/src/tusb_option.h @@ -134,6 +134,7 @@ #define OPT_MCU_ESP32C5 908 ///< Espressif ESP32-C5 #define OPT_MCU_ESP32C61 909 ///< Espressif ESP32-C61 #define OPT_MCU_ESP32H4 910 ///< Espressif ESP32-H4 +#define OPT_MCU_ESP32S31 911 ///< Espressif ESP32-S31 // Dialog #define OPT_MCU_DA1469X 1000 ///< Dialog Semiconductor DA1469x -- cgit v1.3.1 From d808111cfd1708c25cc9ec671985f742560ebf83 Mon Sep 17 00:00:00 2001 From: hathach Date: Thu, 23 Apr 2026 18:10:17 +0700 Subject: musb double packet for epout --- src/portable/mentor/musb/dcd_musb.c | 74 +++++++++++++++++++++--------------- src/portable/mentor/musb/musb_type.h | 4 +- test/hil/hil_test.py | 45 +++++++++++++++++++++- 3 files changed, 88 insertions(+), 35 deletions(-) (limited to 'src') diff --git a/src/portable/mentor/musb/dcd_musb.c b/src/portable/mentor/musb/dcd_musb.c index 667102bc5..be785324c 100644 --- a/src/portable/mentor/musb/dcd_musb.c +++ b/src/portable/mentor/musb/dcd_musb.c @@ -239,19 +239,18 @@ static void process_setup_packet(uint8_t rhport) { // write to txfifo using pipe_state_t info static void pipe_write(musb_regs_t* musb_regs, pipe_state_t* pipe, uint8_t epnum) { - musb_ep_csr_t* ep_csr = get_ep_csr(musb_regs, epnum); - const unsigned mps = ep_csr->tx_maxp & MUSB_TXMAXP_PACKET_SIZE_MASK; - const unsigned rem = pipe->remaining; - const unsigned len = TU_MIN(mps, rem); - volatile void *fifo_ptr = &musb_regs->fifo[epnum]; - if (len) { + musb_ep_csr_t* ep_csr = &musb_regs->indexed_csr; + const uint16_t mps = ep_csr->tx_maxp & MUSB_TXMAXP_PACKET_SIZE_M; + const uint16_t xact_len = tu_min16(mps, pipe->remaining); + volatile void *hwfifo = &musb_regs->fifo[epnum]; + if (xact_len) { if (pipe->use_fifo) { - tu_hwfifo_write_from_fifo(fifo_ptr, pipe->fifo, len, NULL); + tu_hwfifo_write_from_fifo(hwfifo, pipe->fifo, xact_len, NULL); } else { - tu_hwfifo_write(fifo_ptr, pipe->buf, len, NULL); - pipe->buf += len; + tu_hwfifo_write(hwfifo, pipe->buf, xact_len, NULL); + pipe->buf += xact_len; } - pipe->remaining = rem - len; + pipe->remaining -= xact_len; } ep_csr->tx_csrl = MUSB_TXCSRL1_TXRDY; } @@ -284,6 +283,28 @@ static void process_epin(uint8_t rhport, musb_regs_t *musb_regs, uint8_t epnum) } } +// Drain one packet from the Rx FIFO into pipe->buf/fifo, update pipe state, and +// release the FIFO slot by clearing RXRDY. return true if short packet +static bool pipe_read(musb_regs_t* musb_regs, pipe_state_t* pipe, uint8_t epnum) { + musb_ep_csr_t* ep_csr = &musb_regs->indexed_csr; // index already set in process_epout() + const uint16_t mps = ep_csr->rx_maxp & MUSB_RXMAXP_PACKET_SIZE_M; + const uint16_t rx_count = ep_csr->rx_count; + const uint16_t xact_len = tu_min16(tu_min16(pipe->remaining, mps), rx_count); + volatile void *hwfifo = &musb_regs->fifo[epnum]; + if (xact_len) { + if (pipe->use_fifo) { + tu_hwfifo_read_to_fifo(hwfifo, pipe->fifo, xact_len, NULL); + } else { + tu_hwfifo_read(hwfifo, pipe->buf, xact_len, NULL); + pipe->buf += xact_len; + } + pipe->remaining -= xact_len; + } + ep_csr->rx_csrl = 0; /* Clear RXRDY - release this FIFO slot */ + + return (xact_len < mps); +} + static void process_epout(uint8_t rhport, musb_regs_t *musb_regs, uint8_t epnum, bool is_isr) { musb_ep_csr_t* ep_csr = get_ep_csr(musb_regs, epnum); if (ep_csr->rx_csrl & MUSB_RXCSRL1_STALLED) { @@ -291,13 +312,12 @@ static void process_epout(uint8_t rhport, musb_regs_t *musb_regs, uint8_t epnum, return; // sent STALL, do nothing } - //Fail gracefully. Spurious interrupt. + // Fail gracefully. Spurious interrupt. if (!(ep_csr->rx_csrl & MUSB_RXCSRL1_RXRDY)) { return; } pipe_state_t *pipe = pipe_get(epnum, TUSB_DIR_OUT); - if (!pipe->armed) { // Packet is already ACK'd by hardware and sitting in the Rx FIFO, but no transfer is // posted. Do NOT flush (per MUSB spec §3.3.11 FlushFIFO) - that would silently drop @@ -308,28 +328,14 @@ static void process_epout(uint8_t rhport, musb_regs_t *musb_regs, uint8_t epnum, return; } - const unsigned mps = ep_csr->rx_maxp & MUSB_RXMAXP_PACKET_SIZE_MASK; - const unsigned rem = pipe->remaining; - const unsigned vld = ep_csr->rx_count; - const unsigned len = TU_MIN(TU_MIN(rem, mps), vld); - volatile void *fifo_ptr = &musb_regs->fifo[epnum]; - if (len) { - if (pipe->use_fifo) { - tu_hwfifo_read_to_fifo(fifo_ptr, pipe->fifo, len, NULL); - } else { - tu_hwfifo_read(fifo_ptr, pipe->buf, len, NULL); - pipe->buf += len; - } - pipe->remaining = rem - len; - } + const bool is_short = pipe_read(musb_regs, pipe, epnum); - ep_csr->rx_csrl = 0; /* Always Clear RXRDY bit */ - if ((len < mps) || (rem == len)) { + // Transfer completes on a short packet or when the rx buffer is filled. + if (is_short || pipe->remaining == 0) { const uint16_t xferred_len = pipe->length - pipe->remaining; pipe->buf = NULL; pipe->armed = false; - - dcd_event_xfer_complete(rhport, tu_edpt_addr(epnum, TUSB_DIR_OUT), xferred_len, XFER_RESULT_SUCCESS, is_isr); + dcd_event_xfer_complete(rhport, epnum, xferred_len, XFER_RESULT_SUCCESS, is_isr); } } @@ -879,7 +885,7 @@ void dcd_int_handler(uint8_t rhport) { process_epin(rhport, musb_regs, epnum); intr_tx &= ~TU_BIT(epnum); - // for Double-buffered endpoint: TxPktRdy is cleared and interrupt is generated when we write the first packet + // Double packet endpoint: TxPktRdy is clear, and interrupt is generated immediately when 1st packet is written. uint_fast8_t new_intr_tx = musb_regs->intr_tx; new_intr_tx &= musb_regs->intr_txen; @@ -891,6 +897,12 @@ void dcd_int_handler(uint8_t rhport) { unsigned const epnum = __builtin_ctz(intr_rx); process_epout(rhport, musb_regs, epnum, true); intr_rx &= ~TU_BIT(epnum); + + // Double packet endpoint: RxPktRdy is set and interrupt is generated immediately if 2nd packet is received + uint_fast8_t new_intr_rx = musb_regs->intr_rx; + new_intr_rx &= musb_regs->intr_rxen; + + intr_rx |= new_intr_rx; } musb_regs->index = saved_index; // restore endpoint index diff --git a/src/portable/mentor/musb/musb_type.h b/src/portable/mentor/musb/musb_type.h index dd1cd6ded..e51634f2a 100644 --- a/src/portable/mentor/musb/musb_type.h +++ b/src/portable/mentor/musb/musb_type.h @@ -573,8 +573,8 @@ TU_ATTR_ALWAYS_INLINE static inline musb_ep_csr_t* get_ep_csr(musb_regs_t* musb_ // numpackminus1 (HB-iso / HS-bulk multiplier - 1). // //***************************************************************************** -#define MUSB_TXMAXP_PACKET_SIZE_MASK 0x07FFu -#define MUSB_RXMAXP_PACKET_SIZE_MASK 0x07FFu +#define MUSB_TXMAXP_PACKET_SIZE_M 0x07FFu +#define MUSB_RXMAXP_PACKET_SIZE_M 0x07FFu //***************************************************************************** // diff --git a/test/hil/hil_test.py b/test/hil/hil_test.py index 58116fb67..447ae10ec 100755 --- a/test/hil/hil_test.py +++ b/test/hil/hil_test.py @@ -1285,6 +1285,32 @@ def test_example(board, f1, example): return err_count +def build_board(board): + """Build firmware for this board via tools/build.py. + Honors board config's build.flags_on variants and build.args defines. + Output goes to cmake-build/cmake-build-BOARD[-f1_...]/ (tools/build.py layout).""" + name = board['name'] + bcfg = board.get('build', {}) + flags_on_list = bcfg.get('flags_on', ['']) + extra_defs = bcfg.get('args', []) + + failed = 0 + for f1 in flags_on_list: + cmd = [sys.executable, f'{TINYUSB_ROOT}/tools/build.py', '-b', name] + for d in extra_defs: + cmd += ['-D', d] + if f1: + for flag in f1.split(): + cmd += ['-f1', flag] + if verbose: + cmd.append('-v') + print(f' + {" ".join(cmd)}') + r = subprocess.run(cmd, cwd=TINYUSB_ROOT) + if r.returncode != 0: + failed += 1 + return name, failed + + def test_board(board): name = board['name'] flasher = board['flasher'] @@ -1346,6 +1372,7 @@ def main(): parser.add_argument('-sf', '--skip-flash', action='store_true', help='Run tests without flashing firmware (use whatever is already on the board)') parser.add_argument('-t', '--test-only', action='append', default=[], help='Tests to run, all if not specified') parser.add_argument('-B', '--build-dir', default='cmake-build', help='Build folder name (default: cmake-build)') + parser.add_argument('--build', action='store_true', help='Build firmware for selected boards with cmake before running tests') parser.add_argument('-r', '--retry', type=int, default=3, help='Retry count for failed tests (default: 3)') parser.add_argument('-v', '--verbose', action='store_true', help='Verbose output') args = parser.parse_args() @@ -1370,10 +1397,24 @@ def main(): else: config_boards = [e for e in config['boards'] if e['name'] in boards] - err_count = 0 + build_err = 0 + if args.build: + if build_dir != 'cmake-build': + print(f'warning: --build writes into cmake-build/, but -B is {build_dir!r}; ' + f'tests will not find the freshly built firmware') + print('-' * 30) + print(f'Build phase: {len(config_boards)} board(s)') + print('-' * 30) + for board in config_boards: + _, nfail = build_board(board) + build_err += nfail + print('-' * 30) + print(f'Build phase done: {build_err} failed') + print('-' * 30) + with Pool(processes=os.cpu_count()) as pool: mret = pool.map(test_board, config_boards) - err_count = sum(e[1] for e in mret) + err_count = build_err + sum(e[1] for e in mret) # generate skip list for next re-run if failed skip_fname = f'{config_file}.skip' if err_count > 0: -- cgit v1.3.1 From fd4279a027a2535dbe5177a962b4cac316357af9 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 24 Apr 2026 15:50:32 +0700 Subject: refactor musb ep0 xfer --- src/portable/mentor/musb/dcd_musb.c | 191 +++++++++++++++++------------------- 1 file changed, 92 insertions(+), 99 deletions(-) (limited to 'src') diff --git a/src/portable/mentor/musb/dcd_musb.c b/src/portable/mentor/musb/dcd_musb.c index be785324c..7b46580cc 100644 --- a/src/portable/mentor/musb/dcd_musb.c +++ b/src/portable/mentor/musb/dcd_musb.c @@ -82,16 +82,28 @@ typedef struct { #define MUSB_PIPE_COUNT (2u * TUP_DCD_ENDPOINT_MAX - 1u) #endif +enum { + EP0_STATE_IDLE = 0, + EP0_STATE_TX, + EP0_STATE_RX, + EP0_STATE_STATUS +}; + typedef struct { union { tusb_control_request_t setup_packet; uint32_t setup_buffer[2]; }; - uint16_t remaining_ctrl; /* The number of bytes remaining in data stage of control transfer. */ - int8_t status_out; + uint8_t ep0_state; pipe_state_t pipe[MUSB_PIPE_COUNT]; } dcd_data_t; +// EP0 control-transfer state is held by usbd_control.c (request, total_xferred, +// data_len). dcd just keeps the last SETUP packet's bmRequestType so it knows +// the original direction when handling DATA/STATUS phase calls. After the +// transfer's STATUS stage completes (or a new SETUP/SETEND aborts it), the +// bmRequestType is reset to REQUEST_TYPE_INVALID. + static dcd_data_t _dcd; TU_ATTR_ALWAYS_INLINE static inline pipe_state_t* pipe_get(uint8_t epnum, tusb_dir_t epdir) { @@ -214,29 +226,6 @@ TU_ATTR_ALWAYS_INLINE static inline void hwfifo_flush(musb_regs_t* musb, unsigne } } -static void process_setup_packet(uint8_t rhport) { - musb_regs_t* musb_regs = MUSB_REGS(rhport); - - // Read setup packet - _dcd.setup_buffer[0] = musb_regs->fifo[0]; - _dcd.setup_buffer[1] = musb_regs->fifo[0]; - - pipe_state_t* pipe0 = pipe_get(0, TUSB_DIR_OUT); - pipe0->buf = NULL; - pipe0->length = 0; - pipe0->remaining = 0; - dcd_event_setup_received(rhport, (const uint8_t*)(uintptr_t)&_dcd.setup_packet, true); - - const unsigned len = _dcd.setup_packet.wLength; - _dcd.remaining_ctrl = len; - const unsigned dir_in = tu_edpt_dir(_dcd.setup_packet.bmRequestType); - /* Clear RX FIFO and reverse the transaction direction */ - if (len && dir_in) { - musb_ep_csr_t* ep_csr = get_ep_csr(musb_regs, 0); - ep_csr->csr0l = MUSB_CSRL0_RXRDYC; - } -} - // write to txfifo using pipe_state_t info static void pipe_write(musb_regs_t* musb_regs, pipe_state_t* pipe, uint8_t epnum) { musb_ep_csr_t* ep_csr = &musb_regs->indexed_csr; @@ -372,81 +361,79 @@ static bool edpt_n_xfer(uint8_t rhport, uint8_t ep_addr, void *buffer, uint16_t return true; } -static bool edpt0_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes, bool is_isr) -{ - (void)rhport; - TU_ASSERT(total_bytes <= 64); /* Current implementation supports for only up to 64 bytes. */ +// EP0 transfer dispatcher. usbd_control.c drives this with one of: +// - DATA IN : ep=0x80, buffer != NULL, total_bytes > 0 (write a chunk) +// - DATA OUT : ep=0x00, buffer != NULL, total_bytes > 0 (arm to receive) +// - STATUS IN : ep=0x80, total_bytes == 0 (zero-len ack of OUT request) +// - STATUS OUT: ep=0x00, total_bytes == 0 (zero-len ack of IN request, +// HW already auto-handled it +// when DATAEND was set on the +// last DATA IN packet) +static bool edpt0_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes, bool is_isr) { + TU_ASSERT(total_bytes <= CFG_TUD_ENDPOINT0_SIZE); musb_regs_t* musb_regs = MUSB_REGS(rhport); musb_ep_csr_t* ep_csr = get_ep_csr(musb_regs, 0); pipe_state_t* pipe0 = pipe_get(0, TUSB_DIR_OUT); + const unsigned dir_in = tu_edpt_dir(ep_addr); const unsigned req = _dcd.setup_packet.bmRequestType; - TU_ASSERT(req != REQUEST_TYPE_INVALID || total_bytes == 0); - - if (req == REQUEST_TYPE_INVALID || _dcd.status_out) { - /* STATUS OUT stage. - * MUSB controller automatically handles STATUS OUT packets without - * software helps. We do not have to do anything. And STATUS stage - * may have already finished and received the next setup packet - * without calling this function, so we have no choice but to - * invoke the callback function of status packet here. */ - // TU_LOG1(" STATUS OUT ep_csr->csr0l = %x\r\n", ep_csr->csr0l); - _dcd.status_out = 0; + + if (total_bytes == 0) { + // STATUS phase if (req == REQUEST_TYPE_INVALID) { - dcd_event_xfer_complete(rhport, ep_addr, total_bytes, XFER_RESULT_SUCCESS, is_isr); - } else { - /* The next setup packet has already been received, it aborts - * invoking callback function to avoid confusing TUSB stack. */ - TU_LOG1("Drop CONTROL_STAGE_ACK\r\n"); + // No active request — likely a stale STATUS call (e.g. new SETUP arrived + // after the previous DATA stage but before usbd reached this point). + // Suppress the complete event to avoid confusing the upper stack. + TU_LOG1("Drop stale CONTROL_STAGE_ACK\r\n"); + return true; } - return true; - } - const unsigned dir_in = tu_edpt_dir(ep_addr); - if (tu_edpt_dir(req) == dir_in) { /* DATA stage */ - TU_ASSERT(total_bytes <= _dcd.remaining_ctrl); - const unsigned rem = _dcd.remaining_ctrl; - const unsigned len = TU_MIN(TU_MIN(rem, 64), total_bytes); - volatile void *fifo_ptr = &musb_regs->fifo[0]; if (dir_in) { - tu_hwfifo_write(fifo_ptr, buffer, len, NULL); - - pipe0->buf = buffer + len; - pipe0->length = len; + // STATUS IN of an OUT request: send ZLP IN with DATAEND so HW completes + // the control transfer. + pipe0->buf = NULL; + pipe0->length = 0; pipe0->remaining = 0; - - _dcd.remaining_ctrl = rem - len; - if ((len < 64) || (rem == len)) { - _dcd.setup_packet.bmRequestType = REQUEST_TYPE_INVALID; /* Change to STATUS/SETUP stage */ - _dcd.status_out = 1; - /* Flush TX FIFO and reverse the transaction direction. */ - ep_csr->csr0l = MUSB_CSRL0_TXRDY | MUSB_CSRL0_DATAEND; - } else { - ep_csr->csr0l = MUSB_CSRL0_TXRDY; /* Flush TX FIFO to return ACK. */ - } + ep_csr->csr0l = MUSB_CSRL0_RXRDYC | MUSB_CSRL0_DATAEND; } else { - pipe0->buf = buffer; - pipe0->length = len; - pipe0->remaining = len; - ep_csr->csr0l = MUSB_CSRL0_RXRDYC; /* Clear RX FIFO to return ACK. */ + // STATUS OUT of an IN request: HW already auto-handled it via DATAEND on + // the last DATA IN packet. Just fire the complete event. + _dcd.setup_packet.bmRequestType = REQUEST_TYPE_INVALID; + dcd_event_xfer_complete(rhport, ep_addr, 0, XFER_RESULT_SUCCESS, is_isr); } - } else if (dir_in) { - pipe0->buf = NULL; - pipe0->length = 0; + return true; + } + + // DATA phase. Direction must match the original request. + TU_ASSERT(req != REQUEST_TYPE_INVALID && tu_edpt_dir(req) == dir_in); + volatile void *fifo_ptr = &musb_regs->fifo[0]; + if (dir_in) { + // DATA IN: load FIFO, set TXRDY. Set DATAEND when this is a short packet + // (USB short-packet rule => end of data stage). For multiple-of-EP0-size + // data, usbd will follow with another DATA chunk or a STATUS request, and + // the latter sends ZLP+DATAEND to terminate. + tu_hwfifo_write(fifo_ptr, buffer, total_bytes, NULL); + pipe0->buf = buffer + total_bytes; + pipe0->length = total_bytes; pipe0->remaining = 0; - /* Clear RX FIFO and reverse the transaction direction */ - ep_csr->csr0l = MUSB_CSRL0_RXRDYC | MUSB_CSRL0_DATAEND; + ep_csr->csr0l = (total_bytes < CFG_TUD_ENDPOINT0_SIZE) + ? (MUSB_CSRL0_TXRDY | MUSB_CSRL0_DATAEND) + : MUSB_CSRL0_TXRDY; + } else { + // DATA OUT: arm to receive into buffer; ack to release the EP0 RX FIFO. + pipe0->buf = buffer; + pipe0->length = total_bytes; + pipe0->remaining = total_bytes; + ep_csr->csr0l = MUSB_CSRL0_RXRDYC; } return true; } -static void process_ep0(uint8_t rhport) -{ +static void process_ep0(uint8_t rhport) { musb_regs_t* musb_regs = MUSB_REGS(rhport); musb_ep_csr_t* ep_csr = get_ep_csr(musb_regs, 0); pipe_state_t* pipe0 = pipe_get(0, TUSB_DIR_OUT); uint_fast8_t csrl = ep_csr->csr0l; // 21.1.5: endpoint 0 service routine as peripheral - if (csrl & MUSB_CSRL0_STALLED) { /* Returned STALL packet to HOST. */ ep_csr->csr0l = 0; /* Clear STALL */ @@ -455,7 +442,7 @@ static void process_ep0(uint8_t rhport) unsigned req = _dcd.setup_packet.bmRequestType; if (csrl & MUSB_CSRL0_SETEND) { - TU_LOG1(" ABORT by the next packets\r\n"); + // Host aborted the current control transfer (sent a new SETUP or premature STATUS in the middle of DATA stage ep_csr->csr0l = MUSB_CSRL0_SETENDC; if (req != REQUEST_TYPE_INVALID && pipe0->buf) { /* DATA stage was aborted by receiving STATUS or SETUP packet. */ @@ -475,20 +462,25 @@ static void process_ep0(uint8_t rhport) if (req == REQUEST_TYPE_INVALID) { /* SETUP */ TU_ASSERT(sizeof(tusb_control_request_t) == ep_csr->count0,); - process_setup_packet(rhport); + _dcd.setup_buffer[0] = musb_regs->fifo[0]; + _dcd.setup_buffer[1] = musb_regs->fifo[0]; + if (_dcd.setup_packet.wLength > 0 && tu_edpt_dir(_dcd.setup_packet.bmRequestType)) { + ep_csr->csr0l = MUSB_CSRL0_RXRDYC; + } + dcd_event_setup_received(rhport, (const uint8_t*)(uintptr_t)&_dcd.setup_packet, true); return; } - if (pipe0->buf) { - /* DATA OUT */ - const unsigned vld = ep_csr->count0; - const unsigned rem = pipe0->remaining; - const unsigned len = TU_MIN(TU_MIN(rem, 64), vld); - volatile void *fifo_ptr = &musb_regs->fifo[0]; - tu_hwfifo_read(fifo_ptr, pipe0->buf, len, NULL); - - pipe0->remaining = rem - len; - _dcd.remaining_ctrl -= len; + if (pipe0->buf) { + /* DATA OUT: pipe0 must be armed by the prior edpt0_xfer(OUT). The host + * cannot send DATA OUT until that call clears the SETUP-stage RXRDY, so + * armed is guaranteed true here. */ + const uint16_t count0 = ep_csr->count0; + const uint16_t len = tu_min16(tu_min16(pipe0->remaining, 64), count0); + if (len) { + tu_hwfifo_read(&musb_regs->fifo[0], pipe0->buf, len, NULL); + pipe0->remaining -= len; + } pipe0->buf = NULL; dcd_event_xfer_complete(rhport, tu_edpt_addr(0, TUSB_DIR_OUT), @@ -498,8 +490,9 @@ static void process_ep0(uint8_t rhport) return; } - /* When CSRL0 is zero, it means that completion of sending any length packet - * or receiving a zero length packet. */ + /* When CSRL0 is zero, it means that either + * - completion of sending any length packet TxPktRdy clear + * - or status stage is complete (ZLP) after DataEnd is set */ if (req != REQUEST_TYPE_INVALID && !tu_edpt_dir(req)) { /* STATUS IN */ if (*(const uint16_t*)(uintptr_t)&_dcd.setup_packet == 0x0500) { @@ -534,7 +527,6 @@ static void process_bus_reset(uint8_t rhport) { /* When bmRequestType is REQUEST_TYPE_INVALID(0xFF), a control transfer state is SETUP or STATUS stage. */ _dcd.setup_packet.bmRequestType = REQUEST_TYPE_INVALID; - _dcd.status_out = 0; /* When EP0 pipe buf has not NULL, DATA stage works in progress. */ pipe_state_t* pipe0 = pipe_get(0, TUSB_DIR_OUT); pipe0->buf = NULL; @@ -875,17 +867,18 @@ void dcd_int_handler(uint8_t rhport) { } intr_tx &= musb_regs->intr_txen; /* Clear disabled interrupts */ - if (intr_tx & TU_BIT(0)) { - process_ep0(rhport); - intr_tx &= ~TU_BIT(0); - } while (intr_tx) { const unsigned epnum = __builtin_ctz(intr_tx); - process_epin(rhport, musb_regs, epnum); + if (epnum == 0) { + process_ep0(rhport); // EP0 has its own state machine (control transfers) + } else { + process_epin(rhport, musb_regs, epnum); + } intr_tx &= ~TU_BIT(epnum); // Double packet endpoint: TxPktRdy is clear, and interrupt is generated immediately when 1st packet is written. + // Also catches EP0 SETUP arriving during bulk processing. uint_fast8_t new_intr_tx = musb_regs->intr_tx; new_intr_tx &= musb_regs->intr_txen; -- cgit v1.3.1 From f0305eac01ebe3df270e5ff77dca6f310de11a32 Mon Sep 17 00:00:00 2001 From: hathach Date: Fri, 24 Apr 2026 22:04:10 +0700 Subject: musb migrate to ep0_state, remove setup packet from dcd data --- src/portable/mentor/musb/dcd_musb.c | 167 ++++++++++++++++++++---------------- 1 file changed, 93 insertions(+), 74 deletions(-) (limited to 'src') diff --git a/src/portable/mentor/musb/dcd_musb.c b/src/portable/mentor/musb/dcd_musb.c index 7b46580cc..c646380d5 100644 --- a/src/portable/mentor/musb/dcd_musb.c +++ b/src/portable/mentor/musb/dcd_musb.c @@ -50,8 +50,6 @@ * MACRO TYPEDEF CONSTANT ENUM DECLARATION *------------------------------------------------------------------*/ -#define REQUEST_TYPE_INVALID (0xFFu) - typedef union { volatile uint8_t u8; volatile uint16_t u16; @@ -82,27 +80,25 @@ typedef struct { #define MUSB_PIPE_COUNT (2u * TUP_DCD_ENDPOINT_MAX - 1u) #endif +// EP0 control-transfer state (§21.1.4). The IRQ handler derives direction +// and phase from this state instead of the cached SETUP packet. enum { - EP0_STATE_IDLE = 0, - EP0_STATE_TX, - EP0_STATE_RX, - EP0_STATE_STATUS + EP0_STATE_IDLE = 0, // no active control transfer + EP0_STATE_SETUP_RECEIVED, // SETUP received, awaiting DATA or STATUS call from usbd + EP0_STATE_TX, // DATA IN armed (TXRDY set), awaiting send-ACK IRQ + EP0_STATE_RX, // DATA OUT armed (RXRDY cleared), awaiting host-packet IRQ + EP0_STATE_STATUS, // STATUS IN-ZLP armed (DATAEND set), awaiting confirmation IRQ }; typedef struct { - union { - tusb_control_request_t setup_packet; - uint32_t setup_buffer[2]; - }; uint8_t ep0_state; + uint8_t pending_addr; // new USB address latched by dcd_set_address, applied when STATUS IN completes pipe_state_t pipe[MUSB_PIPE_COUNT]; } dcd_data_t; // EP0 control-transfer state is held by usbd_control.c (request, total_xferred, -// data_len). dcd just keeps the last SETUP packet's bmRequestType so it knows -// the original direction when handling DATA/STATUS phase calls. After the -// transfer's STATUS stage completes (or a new SETUP/SETEND aborts it), the -// bmRequestType is reset to REQUEST_TYPE_INVALID. +// data_len). dcd tracks phase in _dcd.ep0_state. The SETUP packet is drained +// into a local in process_ep0 and dispatched upstream — never cached here. static dcd_data_t _dcd; @@ -375,140 +371,161 @@ static bool edpt0_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_ musb_ep_csr_t* ep_csr = get_ep_csr(musb_regs, 0); pipe_state_t* pipe0 = pipe_get(0, TUSB_DIR_OUT); const unsigned dir_in = tu_edpt_dir(ep_addr); - const unsigned req = _dcd.setup_packet.bmRequestType; if (total_bytes == 0) { // STATUS phase - if (req == REQUEST_TYPE_INVALID) { - // No active request — likely a stale STATUS call (e.g. new SETUP arrived - // after the previous DATA stage but before usbd reached this point). - // Suppress the complete event to avoid confusing the upper stack. + if (_dcd.ep0_state == EP0_STATE_IDLE) { + // Stale STATUS call (e.g. new SETUP arrived between DATA and STATUS). TU_LOG1("Drop stale CONTROL_STAGE_ACK\r\n"); return true; } if (dir_in) { - // STATUS IN of an OUT request: send ZLP IN with DATAEND so HW completes - // the control transfer. + // STATUS IN (Write/zero-data req): send ZLP IN with DATAEND. The + // xfer_complete event fires from process_ep0 on the confirmation IRQ. pipe0->buf = NULL; pipe0->length = 0; pipe0->remaining = 0; + _dcd.ep0_state = EP0_STATE_STATUS; ep_csr->csr0l = MUSB_CSRL0_RXRDYC | MUSB_CSRL0_DATAEND; } else { - // STATUS OUT of an IN request: HW already auto-handled it via DATAEND on - // the last DATA IN packet. Just fire the complete event. - _dcd.setup_packet.bmRequestType = REQUEST_TYPE_INVALID; + // STATUS OUT (Read req): HW already auto-handled via DATAEND on the last + // DATA IN packet. Fire complete inline; the actual OUT-ZLP IRQ that + // follows is silently absorbed in process_ep0. + _dcd.ep0_state = EP0_STATE_IDLE; dcd_event_xfer_complete(rhport, ep_addr, 0, XFER_RESULT_SUCCESS, is_isr); } return true; } - // DATA phase. Direction must match the original request. - TU_ASSERT(req != REQUEST_TYPE_INVALID && tu_edpt_dir(req) == dir_in); + // DATA phase — valid from SETUP_RECEIVED (first chunk / Write) or TX + // (subsequent Read chunk). Direction+length drives the next state. + TU_ASSERT(_dcd.ep0_state == EP0_STATE_SETUP_RECEIVED || _dcd.ep0_state == EP0_STATE_TX); volatile void *fifo_ptr = &musb_regs->fifo[0]; if (dir_in) { - // DATA IN: load FIFO, set TXRDY. Set DATAEND when this is a short packet - // (USB short-packet rule => end of data stage). For multiple-of-EP0-size - // data, usbd will follow with another DATA chunk or a STATUS request, and - // the latter sends ZLP+DATAEND to terminate. + // DATA IN: load FIFO, set TXRDY. Add DATAEND for a short packet (ends + // the data stage per USB short-packet rule). tu_hwfifo_write(fifo_ptr, buffer, total_bytes, NULL); pipe0->buf = buffer + total_bytes; pipe0->length = total_bytes; pipe0->remaining = 0; + _dcd.ep0_state = EP0_STATE_TX; ep_csr->csr0l = (total_bytes < CFG_TUD_ENDPOINT0_SIZE) ? (MUSB_CSRL0_TXRDY | MUSB_CSRL0_DATAEND) : MUSB_CSRL0_TXRDY; } else { - // DATA OUT: arm to receive into buffer; ack to release the EP0 RX FIFO. + // DATA OUT: arm, ack RXRDY so host can send DATA OUT. pipe0->buf = buffer; pipe0->length = total_bytes; pipe0->remaining = total_bytes; + _dcd.ep0_state = EP0_STATE_RX; ep_csr->csr0l = MUSB_CSRL0_RXRDYC; } return true; } +// 21.1.5: endpoint 0 service routine as peripheral. Drives the IDLE / +// SETUP_RECEIVED / TX / RX / STATUS machine; direction on each IRQ is +// implied by the state. static void process_ep0(uint8_t rhport) { musb_regs_t* musb_regs = MUSB_REGS(rhport); musb_ep_csr_t* ep_csr = get_ep_csr(musb_regs, 0); pipe_state_t* pipe0 = pipe_get(0, TUSB_DIR_OUT); uint_fast8_t csrl = ep_csr->csr0l; - // 21.1.5: endpoint 0 service routine as peripheral if (csrl & MUSB_CSRL0_STALLED) { - /* Returned STALL packet to HOST. */ - ep_csr->csr0l = 0; /* Clear STALL */ + ep_csr->csr0l = 0; + _dcd.ep0_state = EP0_STATE_IDLE; return; } - unsigned req = _dcd.setup_packet.bmRequestType; if (csrl & MUSB_CSRL0_SETEND) { - // Host aborted the current control transfer (sent a new SETUP or premature STATUS in the middle of DATA stage + // Host aborted the current control transfer (new SETUP or premature STATUS). ep_csr->csr0l = MUSB_CSRL0_SETENDC; - if (req != REQUEST_TYPE_INVALID && pipe0->buf) { - /* DATA stage was aborted by receiving STATUS or SETUP packet. */ + if (_dcd.ep0_state == EP0_STATE_TX || _dcd.ep0_state == EP0_STATE_RX) { + const uint8_t dir_ep_addr = (_dcd.ep0_state == EP0_STATE_TX) ? TUSB_DIR_IN_MASK : 0; pipe0->buf = NULL; - _dcd.setup_packet.bmRequestType = REQUEST_TYPE_INVALID; dcd_event_xfer_complete(rhport, - req & TUSB_DIR_IN_MASK, + dir_ep_addr, pipe0->length - pipe0->remaining, XFER_RESULT_SUCCESS, true); } - req = REQUEST_TYPE_INVALID; - if (!(csrl & MUSB_CSRL0_RXRDY)) return; /* Received SETUP packet */ + _dcd.ep0_state = EP0_STATE_IDLE; + if (!(csrl & MUSB_CSRL0_RXRDY)) return; /* no SETUP waiting behind it */ } if (csrl & MUSB_CSRL0_RXRDY) { - /* Received SETUP or DATA OUT packet */ - if (req == REQUEST_TYPE_INVALID) { - /* SETUP */ - TU_ASSERT(sizeof(tusb_control_request_t) == ep_csr->count0,); - _dcd.setup_buffer[0] = musb_regs->fifo[0]; - _dcd.setup_buffer[1] = musb_regs->fifo[0]; - if (_dcd.setup_packet.wLength > 0 && tu_edpt_dir(_dcd.setup_packet.bmRequestType)) { + const uint16_t count0 = ep_csr->count0; + + if (_dcd.ep0_state == EP0_STATE_IDLE) { + // SETUP token (count0 == 8). A count0 == 0 here would be a stray + // STATUS-OUT ZLP that bypassed the absorbing path below; silently ack. + if (count0 == 0) { + ep_csr->csr0l = MUSB_CSRL0_RXRDYC; + return; + } + TU_ASSERT(sizeof(tusb_control_request_t) == count0,); + union { + tusb_control_request_t req; + uint32_t u32[2]; + } setup; + setup.u32[0] = musb_regs->fifo[0]; + setup.u32[1] = musb_regs->fifo[0]; + _dcd.ep0_state = EP0_STATE_SETUP_RECEIVED; + // Ack RXRDY now for Read requests so host can start sending IN tokens. + // Write / zero-data leave it set — HW NAKs OUT tokens until edpt0_xfer + // (OUT or STATUS IN) clears it. + if (setup.req.wLength > 0 && tu_edpt_dir(setup.req.bmRequestType)) { ep_csr->csr0l = MUSB_CSRL0_RXRDYC; } - dcd_event_setup_received(rhport, (const uint8_t*)(uintptr_t)&_dcd.setup_packet, true); + dcd_event_setup_received(rhport, (const uint8_t*)&setup.req, true); return; } - if (pipe0->buf) { - /* DATA OUT: pipe0 must be armed by the prior edpt0_xfer(OUT). The host - * cannot send DATA OUT until that call clears the SETUP-stage RXRDY, so - * armed is guaranteed true here. */ - const uint16_t count0 = ep_csr->count0; + if (_dcd.ep0_state == EP0_STATE_RX) { + /* DATA OUT: drain armed buffer, complete, return to SETUP_RECEIVED for STATUS call. */ const uint16_t len = tu_min16(tu_min16(pipe0->remaining, 64), count0); if (len) { tu_hwfifo_read(&musb_regs->fifo[0], pipe0->buf, len, NULL); pipe0->remaining -= len; } pipe0->buf = NULL; + _dcd.ep0_state = EP0_STATE_SETUP_RECEIVED; + ep_csr->csr0l = MUSB_CSRL0_RXRDYC; dcd_event_xfer_complete(rhport, tu_edpt_addr(0, TUSB_DIR_OUT), pipe0->length - pipe0->remaining, XFER_RESULT_SUCCESS, true); + return; + } + + // State SETUP_RECEIVED or TX with count0 == 0: stray STATUS-OUT ZLP for + // a Read request whose inline complete already dropped state to IDLE. + if (count0 == 0) { + ep_csr->csr0l = MUSB_CSRL0_RXRDYC; } return; } - /* When CSRL0 is zero, it means that either - * - completion of sending any length packet TxPktRdy clear - * - or status stage is complete (ZLP) after DataEnd is set */ - if (req != REQUEST_TYPE_INVALID && !tu_edpt_dir(req)) { - /* STATUS IN */ - if (*(const uint16_t*)(uintptr_t)&_dcd.setup_packet == 0x0500) { - /* The address must be changed on completion of the control transfer. */ - musb_regs->faddr = (uint8_t)_dcd.setup_packet.wValue; + /* CSR0L == 0: TXRDY cleared (data sent) or STATUS confirmation. */ + if (_dcd.ep0_state == EP0_STATE_STATUS) { + // STATUS IN confirmed by host's ACK of our IN-ZLP. + if (_dcd.pending_addr) { + musb_regs->faddr = _dcd.pending_addr; + _dcd.pending_addr = 0; } - _dcd.setup_packet.bmRequestType = REQUEST_TYPE_INVALID; + _dcd.ep0_state = EP0_STATE_IDLE; dcd_event_xfer_complete(rhport, tu_edpt_addr(0, TUSB_DIR_IN), - pipe0->length - pipe0->remaining, - XFER_RESULT_SUCCESS, true); + 0, XFER_RESULT_SUCCESS, true); return; } - if (pipe0->buf) { - /* DATA IN */ + + if (_dcd.ep0_state == EP0_STATE_TX) { + /* DATA IN packet sent. For short packets DATAEND was set; the STATUS-OUT + * ZLP IRQ that follows lands in the count0==0 branch above. Return to + * SETUP_RECEIVED so usbd can post the next chunk or the STATUS call. */ pipe0->buf = NULL; + _dcd.ep0_state = EP0_STATE_SETUP_RECEIVED; dcd_event_xfer_complete(rhport, tu_edpt_addr(0, TUSB_DIR_IN), pipe0->length - pipe0->remaining, @@ -525,8 +542,7 @@ static void process_bus_reset(uint8_t rhport) { alloced_fifo_bytes = CFG_TUD_ENDPOINT0_SIZE; #endif - /* When bmRequestType is REQUEST_TYPE_INVALID(0xFF), a control transfer state is SETUP or STATUS stage. */ - _dcd.setup_packet.bmRequestType = REQUEST_TYPE_INVALID; + _dcd.ep0_state = EP0_STATE_IDLE; /* When EP0 pipe buf has not NULL, DATA stage works in progress. */ pipe_state_t* pipe0 = pipe_get(0, TUSB_DIR_OUT); pipe0->buf = NULL; @@ -591,18 +607,21 @@ void dcd_int_disable(uint8_t rhport) { musb_dcd_int_disable(rhport); } -// Receive Set Address request, mcu port must also include status IN response +// Receive Set Address request. Stash the new address here; hardware faddr is +// latched from pending_addr in process_ep0 once the STATUS IN completes (per +// USB spec, address must only take effect after the status stage). void dcd_set_address(uint8_t rhport, uint8_t dev_addr) { - (void)dev_addr; musb_regs_t* musb_regs = MUSB_REGS(rhport); musb_ep_csr_t* ep_csr = get_ep_csr(musb_regs, 0); pipe_state_t* pipe0 = pipe_get(0, TUSB_DIR_OUT); + _dcd.pending_addr = dev_addr; pipe0->buf = NULL; pipe0->length = 0; pipe0->remaining = 0; - /* Clear RX FIFO to return ACK. */ + _dcd.ep0_state = EP0_STATE_STATUS; + /* Send STATUS IN ZLP with DATAEND; host ACK fires the confirmation IRQ. */ ep_csr->csr0l = MUSB_CSRL0_RXRDYC | MUSB_CSRL0_DATAEND; } @@ -803,7 +822,7 @@ void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) { if (0 == epn) { if (!ep_addr) { /* Ignore EP80 */ - _dcd.setup_packet.bmRequestType = REQUEST_TYPE_INVALID; + _dcd.ep0_state = EP0_STATE_IDLE; pipe_state_t* pipe0 = pipe_get(0, TUSB_DIR_OUT); pipe0->buf = NULL; ep_csr->csr0l = MUSB_CSRL0_STALL; -- cgit v1.3.1 From 9fd6788add2223789e9ab08e99337bb96c77ba2a Mon Sep 17 00:00:00 2001 From: hathach Date: Sat, 25 Apr 2026 02:22:00 +0700 Subject: musb more ep0 refactor. add back remaining_ctrl for correct ep0 state transition. handle status out to make sure xfer_complete() not called before dcd_edpt_xfer() --- src/portable/mentor/musb/dcd_musb.c | 266 +++++++++++++++++++----------------- 1 file changed, 139 insertions(+), 127 deletions(-) (limited to 'src') diff --git a/src/portable/mentor/musb/dcd_musb.c b/src/portable/mentor/musb/dcd_musb.c index c646380d5..4ef10168f 100644 --- a/src/portable/mentor/musb/dcd_musb.c +++ b/src/portable/mentor/musb/dcd_musb.c @@ -80,17 +80,21 @@ typedef struct { #define MUSB_PIPE_COUNT (2u * TUP_DCD_ENDPOINT_MAX - 1u) #endif -// EP0 control-transfer state (§21.1.4). The IRQ handler derives direction -// and phase from this state instead of the cached SETUP packet. +// EP0 control-transfer phase (§21.1.4). The phase is set from the SETUP +// packet's direction/wLength when the SETUP IRQ fires, and drives what each +// subsequent IRQ or edpt0_xfer call is allowed to do. enum { EP0_STATE_IDLE = 0, // no active control transfer - EP0_STATE_SETUP_RECEIVED, // SETUP received, awaiting DATA or STATUS call from usbd - EP0_STATE_TX, // DATA IN armed (TXRDY set), awaiting send-ACK IRQ - EP0_STATE_RX, // DATA OUT armed (RXRDY cleared), awaiting host-packet IRQ - EP0_STATE_STATUS, // STATUS IN-ZLP armed (DATAEND set), awaiting confirmation IRQ + EP0_STATE_TX, // DATA IN stage (Read req data; STATUS-OUT-ZLP absorbed here too) + EP0_STATE_RX, // DATA OUT stage (Write req data) + EP0_STATE_STATUS_IN, // STATUS IN — device sends IN-ZLP to host; awaits send-ACK IRQ + EP0_STATE_STATUS_OUT, + EP0_STATE_STATUS_OUT_REQUESTED, + EP0_STATE_STATUS_OUT_SENT }; typedef struct { + uint16_t remaining_ctrl; /* The number of bytes remaining in data stage of control transfer. */ uint8_t ep0_state; uint8_t pending_addr; // new USB address latched by dcd_set_address, applied when STATUS IN completes pipe_state_t pipe[MUSB_PIPE_COUNT]; @@ -366,65 +370,65 @@ static bool edpt_n_xfer(uint8_t rhport, uint8_t ep_addr, void *buffer, uint16_t // when DATAEND was set on the // last DATA IN packet) static bool edpt0_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes, bool is_isr) { - TU_ASSERT(total_bytes <= CFG_TUD_ENDPOINT0_SIZE); + TU_ASSERT(total_bytes <= CFG_TUD_ENDPOINT0_SIZE); /* Current implementation supports for only up to 64 bytes. */ musb_regs_t* musb_regs = MUSB_REGS(rhport); musb_ep_csr_t* ep_csr = get_ep_csr(musb_regs, 0); pipe_state_t* pipe0 = pipe_get(0, TUSB_DIR_OUT); const unsigned dir_in = tu_edpt_dir(ep_addr); - if (total_bytes == 0) { - // STATUS phase - if (_dcd.ep0_state == EP0_STATE_IDLE) { - // Stale STATUS call (e.g. new SETUP arrived between DATA and STATUS). - TU_LOG1("Drop stale CONTROL_STAGE_ACK\r\n"); - return true; + switch (_dcd.ep0_state) { + case EP0_STATE_TX: + case EP0_STATE_RX: { + TU_ASSERT(dir_in ? _dcd.ep0_state == EP0_STATE_TX : _dcd.ep0_state == EP0_STATE_RX); + volatile void *fifo_ptr = &musb_regs->fifo[0]; + if (dir_in) { + // DATA IN: load FIFO, set TXRDY. Add DATAEND for a short packet (ends + // the data stage per USB short-packet rule). + tu_hwfifo_write(fifo_ptr, buffer, total_bytes, NULL); + pipe0->buf = buffer + total_bytes; + pipe0->length = total_bytes; + pipe0->remaining = 0; + + _dcd.remaining_ctrl -= total_bytes; + if (_dcd.remaining_ctrl == 0) { + ep_csr->csr0l = MUSB_CSRL0_TXRDY | MUSB_CSRL0_DATAEND; // last packet, also set DATAEND to end the data stage + } else { + ep_csr->csr0l = MUSB_CSRL0_TXRDY; + } + } else { + // DATA OUT: arm, ack RXRDY so host can send DATA OUT. + pipe0->buf = buffer; + pipe0->length = total_bytes; + pipe0->remaining = total_bytes; + ep_csr->csr0l = MUSB_CSRL0_RXRDYC; + } + break; } - if (dir_in) { - // STATUS IN (Write/zero-data req): send ZLP IN with DATAEND. The - // xfer_complete event fires from process_ep0 on the confirmation IRQ. - pipe0->buf = NULL; - pipe0->length = 0; - pipe0->remaining = 0; - _dcd.ep0_state = EP0_STATE_STATUS; + + case EP0_STATE_STATUS_IN: + TU_ASSERT(dir_in && total_bytes == 0); // only STATUS IN allowed ep_csr->csr0l = MUSB_CSRL0_RXRDYC | MUSB_CSRL0_DATAEND; - } else { - // STATUS OUT (Read req): HW already auto-handled via DATAEND on the last - // DATA IN packet. Fire complete inline; the actual OUT-ZLP IRQ that - // follows is silently absorbed in process_ep0. + break; + + case EP0_STATE_STATUS_OUT: + TU_ASSERT(!dir_in && total_bytes == 0); // only STATUS OUT allowed + _dcd.ep0_state = EP0_STATE_STATUS_OUT_REQUESTED; + break; + + case EP0_STATE_STATUS_OUT_SENT: + // status is already sent to host, complete it here _dcd.ep0_state = EP0_STATE_IDLE; dcd_event_xfer_complete(rhport, ep_addr, 0, XFER_RESULT_SUCCESS, is_isr); - } - return true; - } + break; - // DATA phase — valid from SETUP_RECEIVED (first chunk / Write) or TX - // (subsequent Read chunk). Direction+length drives the next state. - TU_ASSERT(_dcd.ep0_state == EP0_STATE_SETUP_RECEIVED || _dcd.ep0_state == EP0_STATE_TX); - volatile void *fifo_ptr = &musb_regs->fifo[0]; - if (dir_in) { - // DATA IN: load FIFO, set TXRDY. Add DATAEND for a short packet (ends - // the data stage per USB short-packet rule). - tu_hwfifo_write(fifo_ptr, buffer, total_bytes, NULL); - pipe0->buf = buffer + total_bytes; - pipe0->length = total_bytes; - pipe0->remaining = 0; - _dcd.ep0_state = EP0_STATE_TX; - ep_csr->csr0l = (total_bytes < CFG_TUD_ENDPOINT0_SIZE) - ? (MUSB_CSRL0_TXRDY | MUSB_CSRL0_DATAEND) - : MUSB_CSRL0_TXRDY; - } else { - // DATA OUT: arm, ack RXRDY so host can send DATA OUT. - pipe0->buf = buffer; - pipe0->length = total_bytes; - pipe0->remaining = total_bytes; - _dcd.ep0_state = EP0_STATE_RX; - ep_csr->csr0l = MUSB_CSRL0_RXRDYC; + default: break; } + return true; } // 21.1.5: endpoint 0 service routine as peripheral. Drives the IDLE / -// SETUP_RECEIVED / TX / RX / STATUS machine; direction on each IRQ is +// IDLE / TX / RX / STATUS machine; direction on each IRQ is // implied by the state. static void process_ep0(uint8_t rhport) { musb_regs_t* musb_regs = MUSB_REGS(rhport); @@ -440,96 +444,103 @@ static void process_ep0(uint8_t rhport) { if (csrl & MUSB_CSRL0_SETEND) { // Host aborted the current control transfer (new SETUP or premature STATUS). + // do nothing, it is probably another setup packet, usbd will reset its state. ep_csr->csr0l = MUSB_CSRL0_SETENDC; - if (_dcd.ep0_state == EP0_STATE_TX || _dcd.ep0_state == EP0_STATE_RX) { - const uint8_t dir_ep_addr = (_dcd.ep0_state == EP0_STATE_TX) ? TUSB_DIR_IN_MASK : 0; - pipe0->buf = NULL; - dcd_event_xfer_complete(rhport, - dir_ep_addr, - pipe0->length - pipe0->remaining, - XFER_RESULT_SUCCESS, true); - } _dcd.ep0_state = EP0_STATE_IDLE; - if (!(csrl & MUSB_CSRL0_RXRDY)) return; /* no SETUP waiting behind it */ + if (!(csrl & MUSB_CSRL0_RXRDY)) { + return; /* no SETUP waiting behind it */ + } } + // Receive Data (Setup or OUT) if (csrl & MUSB_CSRL0_RXRDY) { const uint16_t count0 = ep_csr->count0; - - if (_dcd.ep0_state == EP0_STATE_IDLE) { - // SETUP token (count0 == 8). A count0 == 0 here would be a stray - // STATUS-OUT ZLP that bypassed the absorbing path below; silently ack. - if (count0 == 0) { - ep_csr->csr0l = MUSB_CSRL0_RXRDYC; - return; - } - TU_ASSERT(sizeof(tusb_control_request_t) == count0,); - union { - tusb_control_request_t req; - uint32_t u32[2]; - } setup; - setup.u32[0] = musb_regs->fifo[0]; - setup.u32[1] = musb_regs->fifo[0]; - _dcd.ep0_state = EP0_STATE_SETUP_RECEIVED; - // Ack RXRDY now for Read requests so host can start sending IN tokens. - // Write / zero-data leave it set — HW NAKs OUT tokens until edpt0_xfer - // (OUT or STATUS IN) clears it. - if (setup.req.wLength > 0 && tu_edpt_dir(setup.req.bmRequestType)) { - ep_csr->csr0l = MUSB_CSRL0_RXRDYC; + switch (_dcd.ep0_state) { + case EP0_STATE_IDLE: + TU_ASSERT(sizeof(tusb_control_request_t) == count0, ); + union { + tusb_control_request_t req; + uint32_t u32[2]; + } setup_packet; + setup_packet.u32[0] = musb_regs->fifo[0]; + setup_packet.u32[1] = musb_regs->fifo[0]; + + _dcd.remaining_ctrl = setup_packet.req.wLength; + + // Pick the next phase directly from the SETUP packet: Read → TX, + // Write → RX, zero-data → STATUS_IN. For Read, also ack SETUP's RXRDY + // now so the host can start IN tokens immediately; Write/zero-data + // leave it set so HW NAKs OUT tokens until edpt0_xfer clears it. + if (setup_packet.req.wLength == 0) { + _dcd.ep0_state = EP0_STATE_STATUS_IN; + } else if (tu_edpt_dir(setup_packet.req.bmRequestType)) { + _dcd.ep0_state = EP0_STATE_TX; + ep_csr->csr0l = MUSB_CSRL0_RXRDYC; + } else { + _dcd.ep0_state = EP0_STATE_RX; + } + dcd_event_setup_received(rhport, (const uint8_t *)&setup_packet.req, true); + break; + + case EP0_STATE_RX: { + /* DATA OUT: drain armed buffer, complete. Stay in RX — usbd posts + * edpt0_xfer(STATUS IN) next which transitions us to STATUS_IN. */ + const uint16_t len = tu_min16(tu_min16(pipe0->remaining, 64), count0); + if (len) { + tu_hwfifo_read(&musb_regs->fifo[0], pipe0->buf, len, NULL); + pipe0->remaining -= len; + _dcd.remaining_ctrl -= len; + } + + if (_dcd.remaining_ctrl == 0) { + // last packet, leave it RXRDYC to edpt0_xfer() + _dcd.ep0_state = EP0_STATE_STATUS_IN; + } else { + ep_csr->csr0l = MUSB_CSRL0_RXRDYC; + } + dcd_event_xfer_complete(rhport, tu_edpt_addr(0, TUSB_DIR_OUT), pipe0->length - pipe0->remaining, + XFER_RESULT_SUCCESS, true); + break; } - dcd_event_setup_received(rhport, (const uint8_t*)&setup.req, true); - return; - } - if (_dcd.ep0_state == EP0_STATE_RX) { - /* DATA OUT: drain armed buffer, complete, return to SETUP_RECEIVED for STATUS call. */ - const uint16_t len = tu_min16(tu_min16(pipe0->remaining, 64), count0); - if (len) { - tu_hwfifo_read(&musb_regs->fifo[0], pipe0->buf, len, NULL); - pipe0->remaining -= len; - } - pipe0->buf = NULL; - _dcd.ep0_state = EP0_STATE_SETUP_RECEIVED; - ep_csr->csr0l = MUSB_CSRL0_RXRDYC; - dcd_event_xfer_complete(rhport, - tu_edpt_addr(0, TUSB_DIR_OUT), - pipe0->length - pipe0->remaining, - XFER_RESULT_SUCCESS, true); - return; + default: break; } - // State SETUP_RECEIVED or TX with count0 == 0: stray STATUS-OUT ZLP for - // a Read request whose inline complete already dropped state to IDLE. - if (count0 == 0) { - ep_csr->csr0l = MUSB_CSRL0_RXRDYC; - } return; } - /* CSR0L == 0: TXRDY cleared (data sent) or STATUS confirmation. */ - if (_dcd.ep0_state == EP0_STATE_STATUS) { - // STATUS IN confirmed by host's ACK of our IN-ZLP. - if (_dcd.pending_addr) { - musb_regs->faddr = _dcd.pending_addr; - _dcd.pending_addr = 0; - } - _dcd.ep0_state = EP0_STATE_IDLE; - dcd_event_xfer_complete(rhport, - tu_edpt_addr(0, TUSB_DIR_IN), - 0, XFER_RESULT_SUCCESS, true); - return; - } + /* When CSRL0 is zero, it means that either + * - completion of sending any length packet TxPktRdy clear + * - or status stage is complete (ZLP) after DataEnd is set */ + switch (_dcd.ep0_state) { + case EP0_STATE_TX: + if (_dcd.remaining_ctrl == 0) { + // last packet + _dcd.ep0_state = EP0_STATE_STATUS_OUT; + } + dcd_event_xfer_complete(rhport, 0x80, pipe0->length, XFER_RESULT_SUCCESS, true); + break; + + case EP0_STATE_STATUS_OUT: + // edpt0_xfer() for this is not yet requested, let it call xfer_complete() later + _dcd.ep0_state = EP0_STATE_STATUS_OUT_SENT; + break; + + case EP0_STATE_STATUS_OUT_REQUESTED: + _dcd.ep0_state = EP0_STATE_IDLE; + dcd_event_xfer_complete(rhport, 0, 0, XFER_RESULT_SUCCESS, true); + break; + + case EP0_STATE_STATUS_IN: + if (_dcd.pending_addr) { + musb_regs->faddr = _dcd.pending_addr; + _dcd.pending_addr = 0; + } + _dcd.ep0_state = EP0_STATE_IDLE; + dcd_event_xfer_complete(rhport, 0x80, 0, XFER_RESULT_SUCCESS, true); + break; - if (_dcd.ep0_state == EP0_STATE_TX) { - /* DATA IN packet sent. For short packets DATAEND was set; the STATUS-OUT - * ZLP IRQ that follows lands in the count0==0 branch above. Return to - * SETUP_RECEIVED so usbd can post the next chunk or the STATUS call. */ - pipe0->buf = NULL; - _dcd.ep0_state = EP0_STATE_SETUP_RECEIVED; - dcd_event_xfer_complete(rhport, - tu_edpt_addr(0, TUSB_DIR_IN), - pipe0->length - pipe0->remaining, - XFER_RESULT_SUCCESS, true); + default: break; } } @@ -620,7 +631,7 @@ void dcd_set_address(uint8_t rhport, uint8_t dev_addr) pipe0->buf = NULL; pipe0->length = 0; pipe0->remaining = 0; - _dcd.ep0_state = EP0_STATE_STATUS; + _dcd.ep0_state = EP0_STATE_STATUS_IN; /* Send STATUS IN ZLP with DATAEND; host ACK fires the confirmation IRQ. */ ep_csr->csr0l = MUSB_CSRL0_RXRDYC | MUSB_CSRL0_DATAEND; } @@ -787,6 +798,7 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t t if (epnum) { ret = edpt_n_xfer(rhport, ep_addr, buffer, total_bytes, false, is_isr); } else { + (void) is_isr; ret = edpt0_xfer(rhport, ep_addr, buffer, total_bytes, is_isr); } -- cgit v1.3.1 From 0e4869a729ce32b157a60ae5730abf6f5802381b Mon Sep 17 00:00:00 2001 From: hathach Date: Sat, 25 Apr 2026 14:41:08 +0700 Subject: clean up --- src/common/tusb_types.h | 6 +++ src/device/usbd_control.c | 14 ++---- src/portable/mentor/musb/dcd_musb.c | 95 +++++++++++++++---------------------- 3 files changed, 50 insertions(+), 65 deletions(-) (limited to 'src') diff --git a/src/common/tusb_types.h b/src/common/tusb_types.h index 806997866..36e72967c 100644 --- a/src/common/tusb_types.h +++ b/src/common/tusb_types.h @@ -321,6 +321,12 @@ enum { TUSB_INDEX_INVALID_8 = 0xFF }; +enum { + TU_EP0_OUT = 0x00, + TU_EP0_IN = 0x80 +}; + + //--------------------------------------------------------------------+ // //--------------------------------------------------------------------+ diff --git a/src/device/usbd_control.c b/src/device/usbd_control.c index 87593d4a7..49ecd0f16 100644 --- a/src/device/usbd_control.c +++ b/src/device/usbd_control.c @@ -44,10 +44,6 @@ TU_ATTR_WEAK void dcd_edpt0_status_complete(uint8_t rhport, const tusb_control_r // MACRO CONSTANT TYPEDEF //--------------------------------------------------------------------+ -enum { - EDPT_CTRL_OUT = 0x00, - EDPT_CTRL_IN = 0x80 -}; typedef struct { tusb_control_request_t request; @@ -74,7 +70,7 @@ uint8_t* usbd_get_ctrl_buf(void) { // Queue ZLP status transaction static inline bool status_stage_xact(uint8_t rhport, const tusb_control_request_t* request) { // Opposite to endpoint in Data Phase - const uint8_t ep_addr = request->bmRequestType_bit.direction ? EDPT_CTRL_OUT : EDPT_CTRL_IN; + const uint8_t ep_addr = request->bmRequestType_bit.direction ? TU_EP0_OUT : TU_EP0_IN; return usbd_edpt_xfer(rhport, ep_addr, NULL, 0, false); } @@ -93,10 +89,10 @@ bool tud_control_status(uint8_t rhport, const tusb_control_request_t* request) { // This function can also transfer an zero-length packet static bool data_stage_xact(uint8_t rhport) { const uint16_t xact_len = tu_min16(_ctrl_xfer.data_len - _ctrl_xfer.total_xferred, CFG_TUD_ENDPOINT0_BUFSIZE); - uint8_t ep_addr = EDPT_CTRL_OUT; + uint8_t ep_addr = TU_EP0_OUT; if (_ctrl_xfer.request.bmRequestType_bit.direction == TUSB_DIR_IN) { - ep_addr = EDPT_CTRL_IN; + ep_addr = TU_EP0_IN; if (0u != xact_len && _ctrl_xfer.buffer != _ctrl_epbuf.buf) { TU_VERIFY(0 == tu_memcpy_s(_ctrl_epbuf.buf, CFG_TUD_ENDPOINT0_BUFSIZE, _ctrl_xfer.buffer, xact_len)); } @@ -203,8 +199,8 @@ bool usbd_control_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, TU_ASSERT(status_stage_xact(rhport, &_ctrl_xfer.request)); } else { // Stall both IN and OUT control endpoint - dcd_edpt_stall(rhport, EDPT_CTRL_OUT); - dcd_edpt_stall(rhport, EDPT_CTRL_IN); + dcd_edpt_stall(rhport, TU_EP0_OUT); + dcd_edpt_stall(rhport, TU_EP0_IN); } } else { // More data to transfer diff --git a/src/portable/mentor/musb/dcd_musb.c b/src/portable/mentor/musb/dcd_musb.c index 4ef10168f..66fa86c77 100644 --- a/src/portable/mentor/musb/dcd_musb.c +++ b/src/portable/mentor/musb/dcd_musb.c @@ -70,40 +70,32 @@ typedef struct { // Pipe array layout (N = TUP_DCD_ENDPOINT_MAX): // [0] : EP0 (shared between IN/OUT control stages) // One-direction-only IPs (CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY=1): -// [1 .. n-1] : EP1..n-1 (single slot per endpoint) +// [1..N-1] : EP1..N-1 (single slot per endpoint) // Bidirectional-capable IPs: -// [1 .. N-1 ] : EP OUT -// [N .. 2*N-2] : EP IN +// [1..N-1 ] : EP OUT +// [N..2*N-2] : EP IN #if CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY #define MUSB_PIPE_COUNT TUP_DCD_ENDPOINT_MAX #else #define MUSB_PIPE_COUNT (2u * TUP_DCD_ENDPOINT_MAX - 1u) #endif -// EP0 control-transfer phase (§21.1.4). The phase is set from the SETUP -// packet's direction/wLength when the SETUP IRQ fires, and drives what each -// subsequent IRQ or edpt0_xfer call is allowed to do. enum { - EP0_STATE_IDLE = 0, // no active control transfer - EP0_STATE_TX, // DATA IN stage (Read req data; STATUS-OUT-ZLP absorbed here too) - EP0_STATE_RX, // DATA OUT stage (Write req data) - EP0_STATE_STATUS_IN, // STATUS IN — device sends IN-ZLP to host; awaits send-ACK IRQ - EP0_STATE_STATUS_OUT, - EP0_STATE_STATUS_OUT_REQUESTED, - EP0_STATE_STATUS_OUT_SENT + EP0_STATE_IDLE = 0, // no active control transfer + EP0_STATE_DATA, // DATA stage (IN or OUT — direction implied by CSR/dir) + EP0_STATE_STATUS_IN, // STATUS IN — device sends IN-ZLP; awaits send-ACK IRQ + EP0_STATE_STATUS_OUT, // post-DATAEND, neither edpt0_xfer(STATUS OUT) nor confirmation IRQ has happened yet + EP0_STATE_STATUS_OUT_REQUESTED, // edpt0_xfer(STATUS OUT) was called first; awaiting confirmation IRQ to fire complete + EP0_STATE_STATUS_OUT_SENT, // confirmation IRQ arrived first; awaiting edpt0_xfer(STATUS OUT) to fire complete }; typedef struct { - uint16_t remaining_ctrl; /* The number of bytes remaining in data stage of control transfer. */ + uint16_t ep0_remain_datalen; /* The number of bytes remaining in data stage of control transfer. */ uint8_t ep0_state; uint8_t pending_addr; // new USB address latched by dcd_set_address, applied when STATUS IN completes pipe_state_t pipe[MUSB_PIPE_COUNT]; } dcd_data_t; -// EP0 control-transfer state is held by usbd_control.c (request, total_xferred, -// data_len). dcd tracks phase in _dcd.ep0_state. The SETUP packet is drained -// into a local in process_ep0 and dispatched upstream — never cached here. - static dcd_data_t _dcd; TU_ATTR_ALWAYS_INLINE static inline pipe_state_t* pipe_get(uint8_t epnum, tusb_dir_t epdir) { @@ -377,21 +369,18 @@ static bool edpt0_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_ const unsigned dir_in = tu_edpt_dir(ep_addr); switch (_dcd.ep0_state) { - case EP0_STATE_TX: - case EP0_STATE_RX: { - TU_ASSERT(dir_in ? _dcd.ep0_state == EP0_STATE_TX : _dcd.ep0_state == EP0_STATE_RX); - volatile void *fifo_ptr = &musb_regs->fifo[0]; + case EP0_STATE_DATA: { if (dir_in) { - // DATA IN: load FIFO, set TXRDY. Add DATAEND for a short packet (ends - // the data stage per USB short-packet rule). - tu_hwfifo_write(fifo_ptr, buffer, total_bytes, NULL); + // DATA IN: load FIFO, set TXRDY. Add DATAEND on the last chunk + // (ep0_remain_datalen == 0 after this load) to end the data stage. + tu_hwfifo_write(&musb_regs->fifo[0], buffer, total_bytes, NULL); pipe0->buf = buffer + total_bytes; pipe0->length = total_bytes; pipe0->remaining = 0; - _dcd.remaining_ctrl -= total_bytes; - if (_dcd.remaining_ctrl == 0) { - ep_csr->csr0l = MUSB_CSRL0_TXRDY | MUSB_CSRL0_DATAEND; // last packet, also set DATAEND to end the data stage + _dcd.ep0_remain_datalen -= total_bytes; + if (_dcd.ep0_remain_datalen == 0) { + ep_csr->csr0l = MUSB_CSRL0_TXRDY | MUSB_CSRL0_DATAEND; } else { ep_csr->csr0l = MUSB_CSRL0_TXRDY; } @@ -427,9 +416,7 @@ static bool edpt0_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_ return true; } -// 21.1.5: endpoint 0 service routine as peripheral. Drives the IDLE / -// IDLE / TX / RX / STATUS machine; direction on each IRQ is -// implied by the state. +// 21.1.5: endpoint 0 service routine as peripheral static void process_ep0(uint8_t rhport) { musb_regs_t* musb_regs = MUSB_REGS(rhport); musb_ep_csr_t* ep_csr = get_ep_csr(musb_regs, 0); @@ -465,41 +452,35 @@ static void process_ep0(uint8_t rhport) { setup_packet.u32[0] = musb_regs->fifo[0]; setup_packet.u32[1] = musb_regs->fifo[0]; - _dcd.remaining_ctrl = setup_packet.req.wLength; + _dcd.ep0_remain_datalen = setup_packet.req.wLength; - // Pick the next phase directly from the SETUP packet: Read → TX, - // Write → RX, zero-data → STATUS_IN. For Read, also ack SETUP's RXRDY - // now so the host can start IN tokens immediately; Write/zero-data - // leave it set so HW NAKs OUT tokens until edpt0_xfer clears it. if (setup_packet.req.wLength == 0) { _dcd.ep0_state = EP0_STATE_STATUS_IN; - } else if (tu_edpt_dir(setup_packet.req.bmRequestType)) { - _dcd.ep0_state = EP0_STATE_TX; - ep_csr->csr0l = MUSB_CSRL0_RXRDYC; } else { - _dcd.ep0_state = EP0_STATE_RX; + _dcd.ep0_state = EP0_STATE_DATA; + // If OUT (rx) direction, let edpt0_xfer() clear RXRDY when it's ready to receive data. + if (setup_packet.req.bmRequestType & TUSB_DIR_IN_MASK) { + ep_csr->csr0l = MUSB_CSRL0_RXRDYC; + } } dcd_event_setup_received(rhport, (const uint8_t *)&setup_packet.req, true); break; - case EP0_STATE_RX: { - /* DATA OUT: drain armed buffer, complete. Stay in RX — usbd posts - * edpt0_xfer(STATUS IN) next which transitions us to STATUS_IN. */ - const uint16_t len = tu_min16(tu_min16(pipe0->remaining, 64), count0); + case EP0_STATE_DATA: { + const uint16_t len = tu_min16(pipe0->remaining, count0); if (len) { tu_hwfifo_read(&musb_regs->fifo[0], pipe0->buf, len, NULL); pipe0->remaining -= len; - _dcd.remaining_ctrl -= len; + _dcd.ep0_remain_datalen -= len; } - if (_dcd.remaining_ctrl == 0) { - // last packet, leave it RXRDYC to edpt0_xfer() + if (_dcd.ep0_remain_datalen == 0) { + // last packet: change state and leave RXRDY for edpt0_xfer(STATUS IN) to ack _dcd.ep0_state = EP0_STATE_STATUS_IN; } else { ep_csr->csr0l = MUSB_CSRL0_RXRDYC; } - dcd_event_xfer_complete(rhport, tu_edpt_addr(0, TUSB_DIR_OUT), pipe0->length - pipe0->remaining, - XFER_RESULT_SUCCESS, true); + dcd_event_xfer_complete(rhport, TU_EP0_OUT, len, XFER_RESULT_SUCCESS, true); break; } @@ -513,12 +494,14 @@ static void process_ep0(uint8_t rhport) { * - completion of sending any length packet TxPktRdy clear * - or status stage is complete (ZLP) after DataEnd is set */ switch (_dcd.ep0_state) { - case EP0_STATE_TX: - if (_dcd.remaining_ctrl == 0) { - // last packet + case EP0_STATE_DATA: + // csrl == 0 in DATA state = TXRDY just cleared, i.e. a DATA IN packet was successfully sent. If the just-sent + // packet was the last (DATAEND was set when ep0_remain_datalen hit zero), transition + // to STATUS_OUT to await the host's STATUS-OUT ZLP confirmation IRQ. + if (_dcd.ep0_remain_datalen == 0) { _dcd.ep0_state = EP0_STATE_STATUS_OUT; } - dcd_event_xfer_complete(rhport, 0x80, pipe0->length, XFER_RESULT_SUCCESS, true); + dcd_event_xfer_complete(rhport, TU_EP0_IN, pipe0->length, XFER_RESULT_SUCCESS, true); break; case EP0_STATE_STATUS_OUT: @@ -528,7 +511,7 @@ static void process_ep0(uint8_t rhport) { case EP0_STATE_STATUS_OUT_REQUESTED: _dcd.ep0_state = EP0_STATE_IDLE; - dcd_event_xfer_complete(rhport, 0, 0, XFER_RESULT_SUCCESS, true); + dcd_event_xfer_complete(rhport, TU_EP0_OUT, 0, XFER_RESULT_SUCCESS, true); break; case EP0_STATE_STATUS_IN: @@ -537,7 +520,7 @@ static void process_ep0(uint8_t rhport) { _dcd.pending_addr = 0; } _dcd.ep0_state = EP0_STATE_IDLE; - dcd_event_xfer_complete(rhport, 0x80, 0, XFER_RESULT_SUCCESS, true); + dcd_event_xfer_complete(rhport, TU_EP0_IN, 0, XFER_RESULT_SUCCESS, true); break; default: break; @@ -833,7 +816,7 @@ void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) { musb_ep_csr_t* ep_csr = get_ep_csr(musb_regs, epn); if (0 == epn) { - if (!ep_addr) { /* Ignore EP80 */ + if (ep_addr == TU_EP0_OUT) { /* Ignore EP0 OUT */ _dcd.ep0_state = EP0_STATE_IDLE; pipe_state_t* pipe0 = pipe_get(0, TUSB_DIR_OUT); pipe0->buf = NULL; -- cgit v1.3.1 From b87876b2760cf265b093637205c3e72e7550f521 Mon Sep 17 00:00:00 2001 From: hathach Date: Sat, 25 Apr 2026 17:03:59 +0700 Subject: separate pipe0 since it is 1 packet per transfer, merge PIPE0 STATUS PENDING --- src/portable/mentor/musb/dcd_musb.c | 185 +++++++++++++++++------------------- 1 file changed, 85 insertions(+), 100 deletions(-) (limited to 'src') diff --git a/src/portable/mentor/musb/dcd_musb.c b/src/portable/mentor/musb/dcd_musb.c index 66fa86c77..56429ac1f 100644 --- a/src/portable/mentor/musb/dcd_musb.c +++ b/src/portable/mentor/musb/dcd_musb.c @@ -67,51 +67,51 @@ typedef struct { bool use_fifo; /* true: buf is tu_fifo_t*; false: buf is plain byte pointer. */ } pipe_state_t; -// Pipe array layout (N = TUP_DCD_ENDPOINT_MAX): -// [0] : EP0 (shared between IN/OUT control stages) +// Pipe array layout (N = TUP_DCD_ENDPOINT_MAX). EP0 has its own scalars in +// dcd_data_t and does not occupy a pipe slot. // One-direction-only IPs (CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY=1): -// [1..N-1] : EP1..N-1 (single slot per endpoint) +// [0..N-2] : EP1..N-1 (single slot per endpoint) // Bidirectional-capable IPs: -// [1..N-1 ] : EP OUT -// [N..2*N-2] : EP IN +// [0..N-2 ] : EP1..N-1 OUT +// [N-1..2*N-3 ] : EP1..N-1 IN #if CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY - #define MUSB_PIPE_COUNT TUP_DCD_ENDPOINT_MAX + #define MUSB_PIPE_COUNT (TUP_DCD_ENDPOINT_MAX - 1u) #else - #define MUSB_PIPE_COUNT (2u * TUP_DCD_ENDPOINT_MAX - 1u) + #define MUSB_PIPE_COUNT (2u * (TUP_DCD_ENDPOINT_MAX - 1u)) #endif enum { - EP0_STATE_IDLE = 0, // no active control transfer - EP0_STATE_DATA, // DATA stage (IN or OUT — direction implied by CSR/dir) - EP0_STATE_STATUS_IN, // STATUS IN — device sends IN-ZLP; awaits send-ACK IRQ - EP0_STATE_STATUS_OUT, // post-DATAEND, neither edpt0_xfer(STATUS OUT) nor confirmation IRQ has happened yet - EP0_STATE_STATUS_OUT_REQUESTED, // edpt0_xfer(STATUS OUT) was called first; awaiting confirmation IRQ to fire complete - EP0_STATE_STATUS_OUT_SENT, // confirmation IRQ arrived first; awaiting edpt0_xfer(STATUS OUT) to fire complete + PIPE0_STATE_IDLE = 0, // no active control transfer + PIPE0_STATE_DATA, // DATA stage (IN or OUT — direction implied by CSR/dir) + PIPE0_STATE_STATUS_IN, // STATUS IN — device sends IN-ZLP; awaits send-ACK IRQ + PIPE0_STATE_STATUS_OUT, // post-DATAEND, neither edpt0_xfer(STATUS OUT) nor confirmation IRQ has happened yet + PIPE0_STATE_STATUS_OUT_PENDING, // one of {edpt0_xfer(STATUS OUT), confirmation IRQ} has happened; the other fires xfer_complete }; typedef struct { - uint16_t ep0_remain_datalen; /* The number of bytes remaining in data stage of control transfer. */ - uint8_t ep0_state; - uint8_t pending_addr; // new USB address latched by dcd_set_address, applied when STATUS IN completes + struct { + uint8_t *buf; // DATA OUT drain target (only valid while EP0 is in DATA OUT stage) + uint16_t xact_len; // chunk length most recently armed via edpt0_xfer; reported in xfer_complete + uint16_t remain_wlength; // bytes remaining in the control transfer's DATA stage + uint8_t state; + uint8_t pending_addr; // new USB address latched by dcd_set_address; applied when STATUS IN completes + } pipe0; pipe_state_t pipe[MUSB_PIPE_COUNT]; } dcd_data_t; static dcd_data_t _dcd; +// EP0 must not call this — it has its own scalars in dcd_data_t. TU_ATTR_ALWAYS_INLINE static inline pipe_state_t* pipe_get(uint8_t epnum, tusb_dir_t epdir) { + size_t idx = epnum - 1u; #if CFG_TUD_ENDPOINT_ONE_DIRECTION_ONLY (void) epdir; - return &_dcd.pipe[epnum]; #else - if (epnum == 0) { - return &_dcd.pipe[0]; - } - size_t idx = epnum; if (epdir == TUSB_DIR_IN) { idx += TUP_DCD_ENDPOINT_MAX - 1u; } - return &_dcd.pipe[idx]; #endif + return &_dcd.pipe[idx]; } //-------------------------------------------------------------------- @@ -240,7 +240,8 @@ static void pipe_write(musb_regs_t* musb_regs, pipe_state_t* pipe, uint8_t epnum // signal completion; otherwise queue the next packet. static void process_epin(uint8_t rhport, musb_regs_t *musb_regs, uint8_t epnum) { musb_ep_csr_t* ep_csr = get_ep_csr(musb_regs, epnum); - if (ep_csr->tx_csrl & MUSB_TXCSRL1_STALLED) { + const uint_fast8_t csrl = ep_csr->tx_csrl; + if (csrl & MUSB_TXCSRL1_STALLED) { ep_csr->tx_csrl &= ~(MUSB_TXCSRL1_STALLED | MUSB_TXCSRL1_UNDRN); return; // sent STALL, do nothing } @@ -254,7 +255,7 @@ static void process_epin(uint8_t rhport, musb_regs_t *musb_regs, uint8_t epnum) // hardware signals TXRDY clear as soon as a slot frees, not when the wire // transfer finishes). Defer completion until FIFONE == 0 so we don't emit // a duplicate xfer_complete before the final packet has been sent. - if (ep_csr->tx_csrl & MUSB_TXCSRL1_FIFONE) { + if (csrl & MUSB_TXCSRL1_FIFONE) { return; } const uint16_t xferred_len = pipe->length; @@ -353,60 +354,47 @@ static bool edpt_n_xfer(uint8_t rhport, uint8_t ep_addr, void *buffer, uint16_t return true; } -// EP0 transfer dispatcher. usbd_control.c drives this with one of: -// - DATA IN : ep=0x80, buffer != NULL, total_bytes > 0 (write a chunk) -// - DATA OUT : ep=0x00, buffer != NULL, total_bytes > 0 (arm to receive) -// - STATUS IN : ep=0x80, total_bytes == 0 (zero-len ack of OUT request) -// - STATUS OUT: ep=0x00, total_bytes == 0 (zero-len ack of IN request, -// HW already auto-handled it -// when DATAEND was set on the -// last DATA IN packet) static bool edpt0_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes, bool is_isr) { - TU_ASSERT(total_bytes <= CFG_TUD_ENDPOINT0_SIZE); /* Current implementation supports for only up to 64 bytes. */ + TU_ASSERT(total_bytes <= CFG_TUD_ENDPOINT0_SIZE); /* EP0 only supports 1 packet per dcd_edpt_xfer()*/ musb_regs_t* musb_regs = MUSB_REGS(rhport); musb_ep_csr_t* ep_csr = get_ep_csr(musb_regs, 0); - pipe_state_t* pipe0 = pipe_get(0, TUSB_DIR_OUT); const unsigned dir_in = tu_edpt_dir(ep_addr); - switch (_dcd.ep0_state) { - case EP0_STATE_DATA: { + switch (_dcd.pipe0.state) { + case PIPE0_STATE_DATA: { + _dcd.pipe0.xact_len = total_bytes; if (dir_in) { // DATA IN: load FIFO, set TXRDY. Add DATAEND on the last chunk // (ep0_remain_datalen == 0 after this load) to end the data stage. tu_hwfifo_write(&musb_regs->fifo[0], buffer, total_bytes, NULL); - pipe0->buf = buffer + total_bytes; - pipe0->length = total_bytes; - pipe0->remaining = 0; - - _dcd.ep0_remain_datalen -= total_bytes; - if (_dcd.ep0_remain_datalen == 0) { + _dcd.pipe0.remain_wlength -= total_bytes; + if (_dcd.pipe0.remain_wlength == 0) { ep_csr->csr0l = MUSB_CSRL0_TXRDY | MUSB_CSRL0_DATAEND; } else { ep_csr->csr0l = MUSB_CSRL0_TXRDY; } } else { - // DATA OUT: arm, ack RXRDY so host can send DATA OUT. - pipe0->buf = buffer; - pipe0->length = total_bytes; - pipe0->remaining = total_bytes; + // DATA OUT: arm drain target, ack RXRDY so host can send DATA OUT. + _dcd.pipe0.buf = buffer; ep_csr->csr0l = MUSB_CSRL0_RXRDYC; } break; } - case EP0_STATE_STATUS_IN: + case PIPE0_STATE_STATUS_IN: TU_ASSERT(dir_in && total_bytes == 0); // only STATUS IN allowed ep_csr->csr0l = MUSB_CSRL0_RXRDYC | MUSB_CSRL0_DATAEND; break; - case EP0_STATE_STATUS_OUT: + case PIPE0_STATE_STATUS_OUT: TU_ASSERT(!dir_in && total_bytes == 0); // only STATUS OUT allowed - _dcd.ep0_state = EP0_STATE_STATUS_OUT_REQUESTED; + // First event of the STATUS OUT pair — wait for the IRQ to fire complete. + _dcd.pipe0.state = PIPE0_STATE_STATUS_OUT_PENDING; break; - case EP0_STATE_STATUS_OUT_SENT: - // status is already sent to host, complete it here - _dcd.ep0_state = EP0_STATE_IDLE; + case PIPE0_STATE_STATUS_OUT_PENDING: + // Second event — IRQ already arrived, fire complete now. + _dcd.pipe0.state = PIPE0_STATE_IDLE; dcd_event_xfer_complete(rhport, ep_addr, 0, XFER_RESULT_SUCCESS, is_isr); break; @@ -420,12 +408,11 @@ static bool edpt0_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_ static void process_ep0(uint8_t rhport) { musb_regs_t* musb_regs = MUSB_REGS(rhport); musb_ep_csr_t* ep_csr = get_ep_csr(musb_regs, 0); - pipe_state_t* pipe0 = pipe_get(0, TUSB_DIR_OUT); uint_fast8_t csrl = ep_csr->csr0l; if (csrl & MUSB_CSRL0_STALLED) { ep_csr->csr0l = 0; - _dcd.ep0_state = EP0_STATE_IDLE; + _dcd.pipe0.state = PIPE0_STATE_IDLE; return; } @@ -433,7 +420,7 @@ static void process_ep0(uint8_t rhport) { // Host aborted the current control transfer (new SETUP or premature STATUS). // do nothing, it is probably another setup packet, usbd will reset its state. ep_csr->csr0l = MUSB_CSRL0_SETENDC; - _dcd.ep0_state = EP0_STATE_IDLE; + _dcd.pipe0.state = PIPE0_STATE_IDLE; if (!(csrl & MUSB_CSRL0_RXRDY)) { return; /* no SETUP waiting behind it */ } @@ -442,8 +429,8 @@ static void process_ep0(uint8_t rhport) { // Receive Data (Setup or OUT) if (csrl & MUSB_CSRL0_RXRDY) { const uint16_t count0 = ep_csr->count0; - switch (_dcd.ep0_state) { - case EP0_STATE_IDLE: + switch (_dcd.pipe0.state) { + case PIPE0_STATE_IDLE: TU_ASSERT(sizeof(tusb_control_request_t) == count0, ); union { tusb_control_request_t req; @@ -452,12 +439,12 @@ static void process_ep0(uint8_t rhport) { setup_packet.u32[0] = musb_regs->fifo[0]; setup_packet.u32[1] = musb_regs->fifo[0]; - _dcd.ep0_remain_datalen = setup_packet.req.wLength; + _dcd.pipe0.remain_wlength = setup_packet.req.wLength; if (setup_packet.req.wLength == 0) { - _dcd.ep0_state = EP0_STATE_STATUS_IN; + _dcd.pipe0.state = PIPE0_STATE_STATUS_IN; } else { - _dcd.ep0_state = EP0_STATE_DATA; + _dcd.pipe0.state = PIPE0_STATE_DATA; // If OUT (rx) direction, let edpt0_xfer() clear RXRDY when it's ready to receive data. if (setup_packet.req.bmRequestType & TUSB_DIR_IN_MASK) { ep_csr->csr0l = MUSB_CSRL0_RXRDYC; @@ -466,21 +453,20 @@ static void process_ep0(uint8_t rhport) { dcd_event_setup_received(rhport, (const uint8_t *)&setup_packet.req, true); break; - case EP0_STATE_DATA: { - const uint16_t len = tu_min16(pipe0->remaining, count0); - if (len) { - tu_hwfifo_read(&musb_regs->fifo[0], pipe0->buf, len, NULL); - pipe0->remaining -= len; - _dcd.ep0_remain_datalen -= len; + case PIPE0_STATE_DATA: { + // EP0 OUT is single-packet (TU_ASSERT total_bytes <= EP0_SIZE in edpt0_xfer) + // so the whole packet drains in one shot. + if (count0) { + tu_hwfifo_read(&musb_regs->fifo[0], _dcd.pipe0.buf, count0, NULL); + _dcd.pipe0.remain_wlength -= count0; } - - if (_dcd.ep0_remain_datalen == 0) { + if (_dcd.pipe0.remain_wlength == 0) { // last packet: change state and leave RXRDY for edpt0_xfer(STATUS IN) to ack - _dcd.ep0_state = EP0_STATE_STATUS_IN; + _dcd.pipe0.state = PIPE0_STATE_STATUS_IN; } else { ep_csr->csr0l = MUSB_CSRL0_RXRDYC; } - dcd_event_xfer_complete(rhport, TU_EP0_OUT, len, XFER_RESULT_SUCCESS, true); + dcd_event_xfer_complete(rhport, TU_EP0_OUT, count0, XFER_RESULT_SUCCESS, true); break; } @@ -493,33 +479,34 @@ static void process_ep0(uint8_t rhport) { /* When CSRL0 is zero, it means that either * - completion of sending any length packet TxPktRdy clear * - or status stage is complete (ZLP) after DataEnd is set */ - switch (_dcd.ep0_state) { - case EP0_STATE_DATA: + switch (_dcd.pipe0.state) { + case PIPE0_STATE_DATA: // csrl == 0 in DATA state = TXRDY just cleared, i.e. a DATA IN packet was successfully sent. If the just-sent // packet was the last (DATAEND was set when ep0_remain_datalen hit zero), transition // to STATUS_OUT to await the host's STATUS-OUT ZLP confirmation IRQ. - if (_dcd.ep0_remain_datalen == 0) { - _dcd.ep0_state = EP0_STATE_STATUS_OUT; + if (_dcd.pipe0.remain_wlength == 0) { + _dcd.pipe0.state = PIPE0_STATE_STATUS_OUT; } - dcd_event_xfer_complete(rhport, TU_EP0_IN, pipe0->length, XFER_RESULT_SUCCESS, true); + dcd_event_xfer_complete(rhport, TU_EP0_IN, _dcd.pipe0.xact_len, XFER_RESULT_SUCCESS, true); break; - case EP0_STATE_STATUS_OUT: - // edpt0_xfer() for this is not yet requested, let it call xfer_complete() later - _dcd.ep0_state = EP0_STATE_STATUS_OUT_SENT; + case PIPE0_STATE_STATUS_OUT: + // First event of the STATUS OUT pair — wait for edpt0_xfer(STATUS OUT) to fire complete. + _dcd.pipe0.state = PIPE0_STATE_STATUS_OUT_PENDING; break; - case EP0_STATE_STATUS_OUT_REQUESTED: - _dcd.ep0_state = EP0_STATE_IDLE; + case PIPE0_STATE_STATUS_OUT_PENDING: + // Second event — edpt0_xfer(STATUS OUT) already called, fire complete now. + _dcd.pipe0.state = PIPE0_STATE_IDLE; dcd_event_xfer_complete(rhport, TU_EP0_OUT, 0, XFER_RESULT_SUCCESS, true); break; - case EP0_STATE_STATUS_IN: - if (_dcd.pending_addr) { - musb_regs->faddr = _dcd.pending_addr; - _dcd.pending_addr = 0; + case PIPE0_STATE_STATUS_IN: + if (_dcd.pipe0.pending_addr) { + musb_regs->faddr = _dcd.pipe0.pending_addr; + _dcd.pipe0.pending_addr = 0; } - _dcd.ep0_state = EP0_STATE_IDLE; + _dcd.pipe0.state = PIPE0_STATE_IDLE; dcd_event_xfer_complete(rhport, TU_EP0_IN, 0, XFER_RESULT_SUCCESS, true); break; @@ -536,10 +523,10 @@ static void process_bus_reset(uint8_t rhport) { alloced_fifo_bytes = CFG_TUD_ENDPOINT0_SIZE; #endif - _dcd.ep0_state = EP0_STATE_IDLE; - /* When EP0 pipe buf has not NULL, DATA stage works in progress. */ - pipe_state_t* pipe0 = pipe_get(0, TUSB_DIR_OUT); - pipe0->buf = NULL; + _dcd.pipe0.state = PIPE0_STATE_IDLE; + _dcd.pipe0.buf = NULL; + _dcd.pipe0.xact_len = 0; + _dcd.pipe0.remain_wlength = 0; musb->intr_txen = 1; /* Enable only EP0 */ musb->intr_rxen = 0; @@ -608,13 +595,11 @@ void dcd_set_address(uint8_t rhport, uint8_t dev_addr) { musb_regs_t* musb_regs = MUSB_REGS(rhport); musb_ep_csr_t* ep_csr = get_ep_csr(musb_regs, 0); - pipe_state_t* pipe0 = pipe_get(0, TUSB_DIR_OUT); - _dcd.pending_addr = dev_addr; - pipe0->buf = NULL; - pipe0->length = 0; - pipe0->remaining = 0; - _dcd.ep0_state = EP0_STATE_STATUS_IN; + _dcd.pipe0.pending_addr = dev_addr; + _dcd.pipe0.buf = NULL; + _dcd.pipe0.xact_len = 0; + _dcd.pipe0.state = PIPE0_STATE_STATUS_IN; /* Send STATUS IN ZLP with DATAEND; host ACK fires the confirmation IRQ. */ ep_csr->csr0l = MUSB_CSRL0_RXRDYC | MUSB_CSRL0_DATAEND; } @@ -817,15 +802,15 @@ void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) { if (0 == epn) { if (ep_addr == TU_EP0_OUT) { /* Ignore EP0 OUT */ - _dcd.ep0_state = EP0_STATE_IDLE; - pipe_state_t* pipe0 = pipe_get(0, TUSB_DIR_OUT); - pipe0->buf = NULL; + _dcd.pipe0.state = PIPE0_STATE_IDLE; + _dcd.pipe0.buf = NULL; ep_csr->csr0l = MUSB_CSRL0_STALL; } } else { - const uint8_t is_rx = 1 - tu_edpt_dir(ep_addr); + const tusb_dir_t ep_dir = tu_edpt_dir(ep_addr); + const uint8_t is_rx = (ep_dir == TUSB_DIR_OUT ? 1u : 0u); ep_csr->maxp_csr[is_rx].csrl = MUSB_CSRL_SEND_STALL(is_rx); - pipe_state_t* pipe = pipe_get(epn, tu_edpt_dir(ep_addr)); + pipe_state_t* pipe = pipe_get(epn, ep_dir); pipe->armed = false; } -- cgit v1.3.1