diff options
| author | Alex-Schaefer <[email protected]> | 2026-05-09 11:14:00 +0200 |
|---|---|---|
| committer | Alex-Schaefer <[email protected]> | 2026-05-09 11:15:29 +0200 |
| commit | 16bc0548dc0793404a5885aa90d78100b604c227 (patch) | |
| tree | 88fe4f445599e023c9c62e10f3641c3919865629 | |
| parent | cfdab2564fa5735f0a7d5653aa6eb5388456028e (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.c | 21 |
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); } |
