summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorhathach <[email protected]>2026-07-12 05:20:17 +0700
committerhathach <[email protected]>2026-07-12 05:20:17 +0700
commitb640bd73b91d9acc409e76aad68356f3b2849185 (patch)
tree12b133e6a2c9500bc43fecfb379eb8d6520952f9 /src
parent741776311b8f6e0c3fdbf4e00a9c3aff7cd577b2 (diff)
ch32h417: fix USBHS EP0 SETUP re-detect, EP0-OUT toggle, SOF dispatch
Apply correctness-review findings on the USB2 HS dcd (still pre-hardware): - EP0 control OUT arming now writes the full UEP0_RX_CTRL byte (DATA1|ACK) instead of a read-modify-write, so RB_UEP_R_SETUP_IS is cleared - otherwise the data/ status OUT following a SETUP would be re-detected as a new SETUP and every control transfer after the first would break. The SETUP handler likewise writes full TX/RX bytes. This also fixes the missing DATA1 expected-toggle on a control-write data stage. - Add the SOF interrupt branch (USBHS_UDIF_RX_SOF is a top-level INT_FG bit on this IP, not an INT_ST token): dcd_sof_enable() armed SOF but the ISR dropped it, so dcd_event_sof() was never delivered. - Drop the extra INT_FG=USBHS_UDIF_TRANSFER write: the vendor acknowledges a transfer solely by clearing the per-endpoint DONE bit; the aggregate write could clear a second endpoint's still-pending completion (only one is serviced per IRQ). Build-verified (SPEED=high links). Hardware validation still pending. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01QWhrtHjbSVMKcY68vxBwBm
Diffstat (limited to 'src')
-rw-r--r--src/portable/wch/dcd_ch32h417_usbhs.c22
1 files changed, 15 insertions, 7 deletions
diff --git a/src/portable/wch/dcd_ch32h417_usbhs.c b/src/portable/wch/dcd_ch32h417_usbhs.c
index e9ad4e525..8e8bb9a2a 100644
--- a/src/portable/wch/dcd_ch32h417_usbhs.c
+++ b/src/portable/wch/dcd_ch32h417_usbhs.c
@@ -336,10 +336,12 @@ bool ch32h417_usb2_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, u
xfer->valid = true;
if (ep_num == 0 && dir == TUSB_DIR_OUT) {
- // status/data-out stage always DATA1 for a zero-length status, else toggles per packet
- if (total_bytes == 0) {
- EP_RX_CTRL(0) = (uint8_t)((EP_RX_CTRL(0) & ~USBHS_UEP_R_TOG_MASK) | USBHS_UEP_R_TOG_DATA1);
- }
+ // The EP0 data/status OUT stage after a SETUP is DATA1. Write the full control byte (not a
+ // read-modify-write) so RB_UEP_R_SETUP_IS is cleared - otherwise the next OUT would be
+ // re-detected as a SETUP in the ISR. Applies to both the zero-length status and a
+ // (single-packet, EP0-sized) control-write data stage.
+ EP_RX_CTRL(0) = USBHS_UEP_R_TOG_DATA1 | USBHS_UEP_R_RES_ACK;
+ return true;
}
if (dir == TUSB_DIR_IN) {
@@ -407,18 +409,24 @@ void ch32h417_usb2_int_handler(uint8_t rhport) {
} else {
// SETUP or OUT transaction
if (ep_num == 0 && (EP_RX_CTRL(0) & USBHS_UEP_R_SETUP_IS)) {
- EP_RX_CTRL(0) &= (uint8_t)~USBHS_UEP_R_DONE;
tusb_control_request_t const *setup = (tusb_control_request_t const *)ep0_buffer;
ep0_tog = true;
+ // Full-byte writes (DATA1) clear RB_UEP_R_SETUP_IS + DONE and set the post-SETUP toggle
EP_TX_CTRL(0) = USBHS_UEP_T_RES_NAK | USBHS_UEP_T_TOG_DATA1;
- set_rx_res(0, (setup->wLength == 0) ? USBHS_UEP_R_RES_ACK : USBHS_UEP_R_RES_NAK);
+ EP_RX_CTRL(0) = (uint8_t)(((setup->wLength == 0) ? USBHS_UEP_R_RES_ACK : USBHS_UEP_R_RES_NAK) |
+ USBHS_UEP_R_TOG_DATA1);
dcd_event_setup_received(rhport, ep0_buffer, true);
} else {
EP_RX_CTRL(ep_num) &= (uint8_t)~USBHS_UEP_R_DONE;
update_out(rhport, ep_num, EP_RX_LEN(ep_num));
}
}
- USBHSD->INT_FG = USBHS_UDIF_TRANSFER;
+ // The transfer interrupt is acknowledged by clearing the per-endpoint DONE bit above (matching
+ // the vendor driver); do NOT also write INT_FG, which would clear the aggregate flag and could
+ // drop a second endpoint's pending completion (only one is serviced per IRQ).
+ } else if (intflag & USBHS_UDIF_RX_SOF) {
+ dcd_event_sof(rhport, USBHSD->FRAME_NO & USBHS_UD_FRAME_NO, true);
+ USBHSD->INT_FG = USBHS_UDIF_RX_SOF;
} else if (intflag & USBHS_UDIF_BUS_RST) {
dcd_event_bus_reset(rhport, TUSB_SPEED_HIGH, true);
USBHSD->DEV_AD = 0;