summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhathach <[email protected]>2026-06-17 16:26:38 +0700
committerhathach <[email protected]>2026-06-18 15:56:23 +0700
commitde341b14abe3bc3e25962cbb26aa61bc0126adcf (patch)
tree4999729614bdf87ff36a3bcd3af6103698913588
parent47f534680215a68391a46cd45decde9b6f0a6573 (diff)
dcd/stm32_fsdev: restore CH32 EP0 CONTROL type atomically with arming
edpt_xfer() restored EP0 to CONTROL at the top of the function, before the OUT stage was armed (rx bufsize + STAT_RX=VALID written further down, with interrupts enabled in between). That re-enabled the CH32 blind OUT ACK while STAT_RX was still NAK and the buffer size stale, so a host-retried DATA OUT / status ZLP could be ACKed into the wrong buffer in the gap. Fold the CONTROL restore into the single exclusive write that programs STAT_RX=VALID for the OUT direction, after the buffer size is set, so type and arming go live together. The IN direction keeps the (now atomic) early restore, where no pending OUT exists to be blind-ACKed. Co-Authored-By: Claude Fable 5 <[email protected]>
-rw-r--r--src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c18
1 files changed, 12 insertions, 6 deletions
diff --git a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c
index 8dd790404..6279f8102 100644
--- a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c
+++ b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c
@@ -754,14 +754,13 @@ static bool edpt_xfer(uint8_t rhport, uint8_t ep_num, tusb_dir_t dir) {
xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir);
const uint8_t ep_idx = xfer->ep_idx;
+ if (dir == TUSB_DIR_IN) {
#if defined(TUP_USBIP_FSDEV_CH32)
- // Re-enable normal control transfer semantics when EP0 transfer is explicitly armed.
- if (ep_num == 0u) {
- ep0_set_type(U_EP_CONTROL, true);
- }
+ // Safe to restore CONTROL before arming IN: no pending OUT for the errata to blind-ACK.
+ if (ep_num == 0u) {
+ ep0_set_type(U_EP_CONTROL, true);
+ }
#endif
-
- if (dir == TUSB_DIR_IN) {
dcd_transmit_packet(xfer, ep_idx);
} else {
uint32_t ep_reg = ep_read(ep_idx) | U_EP_CTR_TX | U_EP_CTR_RX; // reserve CTR
@@ -781,6 +780,13 @@ static bool edpt_xfer(uint8_t rhport, uint8_t ep_num, tusb_dir_t dir) {
btable_set_rx_bufsize(ep_idx, BTABLE_BUF_RX, cnt);
}
+#if defined(TUP_USBIP_FSDEV_CH32)
+ // Restore CONTROL in the same write as STAT_RX=VALID (after bufsize): a separate earlier
+ // write would re-enable the blind OUT ACK while still NAK'd with a stale buffer.
+ if (ep_num == 0u) {
+ ep_reg = (ep_reg & ~U_EP_T_FIELD) | U_EP_CONTROL;
+ }
+#endif
ep_change_status(&ep_reg, dir, EP_STAT_VALID);
ep_write(ep_idx, ep_reg, true);
}