summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex-Schaefer <[email protected]>2026-05-09 11:14:00 +0200
committerAlex-Schaefer <[email protected]>2026-05-09 11:15:29 +0200
commit16bc0548dc0793404a5885aa90d78100b604c227 (patch)
tree88fe4f445599e023c9c62e10f3641c3919865629
parentcfdab2564fa5735f0a7d5653aa6eb5388456028e (diff)
dwc2: guard EP0 status completion with armed ZLP state
Track when an EP0 OUT zero-length transfer is actually armed and require that state before synthesizing a status-stage completion ahead of a co-reported SETUP event. This preserves the validated status-before-SETUP ordering fix while avoiding stale zero-length state from producing spurious EP0 OUT completions.
-rw-r--r--src/portable/synopsys/dwc2/dcd_dwc2.c21
1 files changed, 21 insertions, 0 deletions
diff --git a/src/portable/synopsys/dwc2/dcd_dwc2.c b/src/portable/synopsys/dwc2/dcd_dwc2.c
index 9b8f44df8..72beb1b80 100644
--- a/src/portable/synopsys/dwc2/dcd_dwc2.c
+++ b/src/portable/synopsys/dwc2/dcd_dwc2.c
@@ -62,6 +62,7 @@ static xfer_ctl_t xfer_status[DWC2_EP_MAX][2];
typedef struct {
// EP0 transfers are limited to 1 packet - larger sizes has to be split
uint16_t ep0_pending[2]; // Index determines direction as tusb_dir_t type
+ bool ep0_out_zlp_armed; // EP0 OUT ZLP transfer is armed and waiting for completion
uint16_t dfifo_top; // top free location in DFIFO in words
// Number of IN endpoints active
@@ -665,6 +666,9 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t to
// EP0 can only handle one packet
if (epnum == 0) {
_dcd_data.ep0_pending[dir] = total_bytes;
+ if (dir == TUSB_DIR_OUT) {
+ _dcd_data.ep0_out_zlp_armed = (total_bytes == 0);
+ }
}
// Schedule packets to be sent within interrupt
@@ -744,6 +748,9 @@ static void handle_bus_reset(uint8_t rhport) {
tu_memclr(xfer_status, sizeof(xfer_status));
+ _dcd_data.ep0_pending[TUSB_DIR_OUT] = 0;
+ _dcd_data.ep0_pending[TUSB_DIR_IN] = 0;
+ _dcd_data.ep0_out_zlp_armed = false;
_dcd_data.sof_en = false;
_dcd_data.allocated_epin_count = 0;
@@ -949,12 +956,16 @@ static void handle_epout_slave(uint8_t rhport, uint8_t epnum, dwc2_doepint_t doe
xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT);
const bool ep0_status_complete_before_setup = (epnum == 0) && doepint_bm.setup_phase_done &&
doepint_bm.xfer_complete &&
+ _dcd_data.ep0_out_zlp_armed &&
(_dcd_data.ep0_pending[TUSB_DIR_OUT] == 0) &&
(xfer->total_len == 0);
if (doepint_bm.setup_phase_done) {
if (ep0_status_complete_before_setup) {
+ _dcd_data.ep0_out_zlp_armed = false;
dcd_event_xfer_complete(rhport, epnum, 0, XFER_RESULT_SUCCESS, true);
+ } else if (epnum == 0) {
+ _dcd_data.ep0_out_zlp_armed = false;
}
// Cleanup previous pending EP0 IN transfer if any
@@ -976,6 +987,9 @@ static void handle_epout_slave(uint8_t rhport, uint8_t epnum, dwc2_doepint_t doe
// EP0 can only handle one packet, Schedule another packet to be received.
edpt_schedule_packets(rhport, epnum, TUSB_DIR_OUT);
} else {
+ if (epnum == 0) {
+ _dcd_data.ep0_out_zlp_armed = false;
+ }
dcd_event_xfer_complete(rhport, epnum, xfer->total_len, XFER_RESULT_SUCCESS, true);
}
}
@@ -1017,12 +1031,16 @@ static void handle_epout_dma(uint8_t rhport, uint8_t epnum, dwc2_doepint_t doepi
xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT);
const bool ep0_status_complete_before_setup = (epnum == 0) && doepint_bm.setup_phase_done &&
doepint_bm.xfer_complete &&
+ _dcd_data.ep0_out_zlp_armed &&
(_dcd_data.ep0_pending[TUSB_DIR_OUT] == 0) &&
(xfer->total_len == 0);
if (doepint_bm.setup_phase_done) {
if (ep0_status_complete_before_setup) {
+ _dcd_data.ep0_out_zlp_armed = false;
dcd_event_xfer_complete(rhport, epnum, 0, XFER_RESULT_SUCCESS, true);
+ } else if (epnum == 0) {
+ _dcd_data.ep0_out_zlp_armed = false;
}
// Cleanup previous pending EP0 IN transfer if any
@@ -1058,6 +1076,9 @@ static void handle_epout_dma(uint8_t rhport, uint8_t epnum, dwc2_doepint_t doepi
dma_setup_prepare(rhport);
}
+ if (epnum == 0) {
+ _dcd_data.ep0_out_zlp_armed = false;
+ }
dcd_dcache_invalidate(xfer->buffer, xfer->total_len);
dcd_event_xfer_complete(rhport, epnum, xfer->total_len, XFER_RESULT_SUCCESS, true);
}