From 8456821fc2bf54297932fd7e8381c3fe50359b0a Mon Sep 17 00:00:00 2001 From: hathach Date: Wed, 17 Jun 2026 16:23:19 +0700 Subject: dcd/stm32_fsdev: make CH32 ep0_set_type read-modify-write atomic ep0_set_type() read the EP0 register outside any critical section and only ep_write() masked the USB IRQ around the store. A USB interrupt landing between the read and the write (e.g. a new SETUP whose handler installs the BULK gate) was silently undone when the task resumed and wrote back its pre-interrupt snapshot with the type forced to CONTROL, re-exposing the unsolicited EP0 OUT ACK the workaround blocks. Bracket the whole read-modify-write with fsdev_int_disable/enable when called with need_exclusive (task context). ISR-context callers pass false and are unaffected (the ISR cannot preempt itself). Co-Authored-By: Claude Fable 5 --- src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 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 957795e09..9c3431c20 100644 --- a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c +++ b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c @@ -159,12 +159,19 @@ TU_ATTR_ALWAYS_INLINE static inline xfer_ctl_t *xfer_ctl_ptr(uint8_t epnum, uint } #if defined(TUP_USBIP_FSDEV_CH32) -// CH32 FSDEV workaround: gate EP0 handshakes by switching type between CONTROL and BULK. +// CH32 EP0 workaround: gate handshakes by switching EP0 type CONTROL<->BULK. +// need_exclusive brackets the read-modify-write so the USB ISR can't race it. TU_ATTR_ALWAYS_INLINE static inline void ep0_set_type(uint32_t ep_type, bool need_exclusive) { + if (need_exclusive) { + fsdev_int_disable(0); + } uint32_t ep_reg = ep_read(0) | U_EP_CTR_TX | U_EP_CTR_RX; ep_reg &= U_EPREG_MASK; ep_reg = (ep_reg & ~U_EP_T_FIELD) | ep_type; - ep_write(0, ep_reg, need_exclusive); + ep_write(0, ep_reg, false); + if (need_exclusive) { + fsdev_int_enable(0); + } } #endif -- cgit v1.3.1