From 030bb962aed6efba443221660fb13f6dbd3ae710 Mon Sep 17 00:00:00 2001 From: HiFiPhile Date: Tue, 23 Jun 2026 10:06:50 +0200 Subject: [PATCH 1/3] dcd/ch32hs: defer bus reset event for proper speed detection Signed-off-by: HiFiPhile --- src/portable/wch/dcd_ch32_usbhs.c | 42 +++++++++++++++---------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/portable/wch/dcd_ch32_usbhs.c b/src/portable/wch/dcd_ch32_usbhs.c index ea3b052ad1..4ab92817e8 100644 --- a/src/portable/wch/dcd_ch32_usbhs.c +++ b/src/portable/wch/dcd_ch32_usbhs.c @@ -59,6 +59,7 @@ static xfer_ctl_t xfer_status[EP_MAX][2]; TU_ATTR_ALIGNED(4) static uint8_t ep0_buffer[CFG_TUD_ENDPOINT0_SIZE]; static bool ep0_tog; static bool ep_data_tog[EP_MAX][2]; +static bool reset_evt_pending; static void set_ep_toggle(uint8_t ep_num, tusb_dir_t ep_dir, bool data1) { if (ep_dir == TUSB_DIR_IN) { @@ -424,6 +425,23 @@ void dcd_int_handler(uint8_t rhport) { } USBHSD->INT_FG = (int_flag & USBHS_TRANSFER_FLAG); /* Clear flag */ } else if (int_flag & USBHS_SETUP_FLAG) { + if (reset_evt_pending) { + reset_evt_pending = false; + tusb_speed_t actual_speed; + switch(USBHSD->SPEED_TYPE & USBHS_SPEED_TYPE_MASK){ + case USBHS_SPEED_TYPE_FULL: + actual_speed = TUSB_SPEED_FULL; + break; + case USBHS_SPEED_TYPE_LOW: + actual_speed = TUSB_SPEED_LOW; + break; + default: + actual_speed = TUSB_SPEED_HIGH; + break; + } + dcd_event_bus_reset(0, actual_speed, true); + } + tusb_control_request_t const* setup = (tusb_control_request_t const*) ep0_buffer; ep0_tog = true; @@ -434,27 +452,9 @@ void dcd_int_handler(uint8_t rhport) { USBHSD->INT_FG = USBHS_SETUP_FLAG; /* Clear flag */ } else if (int_flag & USBHS_BUS_RST_FLAG) { - // TODO CH32 does not detect actual speed at this time (should be known at end of reset) - // This interrupt probably triggered at start of bus reset - // tusb_speed_t actual_speed; - // switch(USBHSD->SPEED_TYPE & USBHS_SPEED_TYPE_MASK){ - // case USBHS_SPEED_TYPE_HIGH: - // actual_speed = TUSB_SPEED_HIGH; - // break; - // case USBHS_SPEED_TYPE_FULL: - // actual_speed = TUSB_SPEED_FULL; - // break; - // case USBHS_SPEED_TYPE_LOW: - // actual_speed = TUSB_SPEED_LOW; - // break; - // default: - // TU_ASSERT(0,); - // break; - // } - // dcd_event_bus_reset(0, actual_speed, true); - - dcd_event_bus_reset(0, TUSB_SPEED_HIGH, true); - + // This interrupt probably triggered at start of bus reset before the speed negotiation has completed. + // Defer the bus reset event until the next setup packet is received in order to determine the actual speed of the bus. + reset_evt_pending = true; USBHSD->DEV_AD = 0; memset(ep_data_tog, 0, sizeof(ep_data_tog)); ep0_tog = true; From ca89edf2ef46484cf56bf1efbaf3a7b14616eeac Mon Sep 17 00:00:00 2001 From: HiFiPhile Date: Tue, 23 Jun 2026 10:07:24 +0200 Subject: [PATCH 2/3] dcd/ch32hs: drop OUT packet whose data toggle doesn't match Signed-off-by: HiFiPhile --- src/portable/wch/dcd_ch32_usbhs.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/portable/wch/dcd_ch32_usbhs.c b/src/portable/wch/dcd_ch32_usbhs.c index 4ab92817e8..99dab70863 100644 --- a/src/portable/wch/dcd_ch32_usbhs.c +++ b/src/portable/wch/dcd_ch32_usbhs.c @@ -418,7 +418,9 @@ void dcd_int_handler(uint8_t rhport) { if (token == USBHS_TOKEN_PID_SOF) { uint32_t frame_count = USBHSD->FRAME_NO & USBHS_FRAME_NO_NUM_MASK; dcd_event_sof(rhport, frame_count, true); - } else if (token == USBHS_TOKEN_PID_OUT) { + // Drop an OUT packet whose data toggle doesn't match what we expect -- a host retransmit + // after a lost ACK, or a host that doesn't alternate DATA0/DATA1. + } else if (token == USBHS_TOKEN_PID_OUT && (int_status & USBHS_DEV_UIS_TOG_OK)) { update_out(rhport, ep_num, len); } else if (token == USBHS_TOKEN_PID_IN) { update_in(rhport, ep_num, false); From de01a33b84243b3e10fcd225faf368d5cefaed57 Mon Sep 17 00:00:00 2001 From: HiFiPhile Date: Tue, 23 Jun 2026 14:10:23 +0200 Subject: [PATCH 3/3] dcd/ch32hs: implement speed selection Signed-off-by: HiFiPhile --- src/portable/wch/dcd_ch32_usbhs.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/portable/wch/dcd_ch32_usbhs.c b/src/portable/wch/dcd_ch32_usbhs.c index 99dab70863..d5bebedde9 100644 --- a/src/portable/wch/dcd_ch32_usbhs.c +++ b/src/portable/wch/dcd_ch32_usbhs.c @@ -176,7 +176,6 @@ static void update_out(uint8_t rhport, uint8_t ep_num, uint16_t rx_len) { bool dcd_init(uint8_t rhport, const tusb_rhport_init_t *rh_init) { (void)rhport; - (void)rh_init; memset(&xfer_status, 0, sizeof(xfer_status)); memset(ep_data_tog, 0, sizeof(ep_data_tog)); @@ -187,12 +186,15 @@ bool dcd_init(uint8_t rhport, const tusb_rhport_init_t *rh_init) { USBHSD->CONTROL = 0; - #if TUD_OPT_HIGH_SPEED - USBHSD->CONTROL = USBHS_DMA_EN | USBHS_INT_BUSY_EN | USBHS_HIGH_SPEED; - #else - #error OPT_MODE_FULL_SPEED not currently supported on CH32 - USBHSD->CONTROL = USBHS_DMA_EN | USBHS_INT_BUSY_EN | USBHS_FULL_SPEED; - #endif + if (rh_init->speed == TUSB_SPEED_HIGH || rh_init->speed == TUSB_SPEED_AUTO) { + USBHSD->CONTROL = USBHS_DMA_EN | USBHS_INT_BUSY_EN | USBHS_HIGH_SPEED; + } else if (rh_init->speed == TUSB_SPEED_FULL) { + USBHSD->CONTROL = USBHS_DMA_EN | USBHS_INT_BUSY_EN | USBHS_FULL_SPEED; + } else if (rh_init->speed == TUSB_SPEED_LOW) { + USBHSD->CONTROL = USBHS_DMA_EN | USBHS_INT_BUSY_EN | USBHS_LOW_SPEED; + } else { + return false; + } USBHSD->INT_EN = 0; USBHSD->INT_EN = USBHS_SETUP_ACT_EN | USBHS_TRANSFER_EN | USBHS_BUS_RST_EN | USBHS_SUSPEND_EN;