summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHiFiPhile <[email protected]>2026-05-22 13:42:08 +0200
committerHiFiPhile <[email protected]>2026-05-27 00:35:25 +0200
commita560281051022473c844bdb258be452da52ce5c3 (patch)
treef3d3391bd573a0f45f718030a0ade29f14794756
parente520cff090e5da57e38ef3c7af250aab6d57a186 (diff)
dwc2: fix EP0 DMA setup race condition
Signed-off-by: HiFiPhile <[email protected]>
-rw-r--r--src/portable/synopsys/dwc2/dcd_dwc2.c29
1 files changed, 15 insertions, 14 deletions
diff --git a/src/portable/synopsys/dwc2/dcd_dwc2.c b/src/portable/synopsys/dwc2/dcd_dwc2.c
index 23fe671d4..1c119eef4 100644
--- a/src/portable/synopsys/dwc2/dcd_dwc2.c
+++ b/src/portable/synopsys/dwc2/dcd_dwc2.c
@@ -73,8 +73,9 @@ typedef struct {
static dcd_data_t _dcd_data;
-CFG_TUD_MEM_SECTION static struct {
- TUD_EPBUF_DEF(setup_packet, 8);
+CFG_TUD_MEM_SECTION static union {
+ TUD_EPBUF_DEF(setup_buffer, 8);
+ tusb_control_request_t setup_packet;
} _dcd_usbbuf;
static tud_configure_dwc2_t _tud_cfg = CFG_TUD_CONFIGURE_DWC2_DEFAULT;
@@ -138,7 +139,7 @@ static void dma_setup_prepare(uint8_t rhport) {
// Receive only 1 packet
dwc2->epout[0].doeptsiz = (1 << DOEPTSIZ_STUPCNT_Pos) | (1 << DOEPTSIZ_PKTCNT_Pos) | (8 << DOEPTSIZ_XFRSIZ_Pos);
- dwc2->epout[0].doepdma = (uintptr_t) _dcd_usbbuf.setup_packet;
+ dwc2->epout[0].doepdma = (uintptr_t) _dcd_usbbuf.setup_buffer;
dwc2->epout[0].doepctl |= DOEPCTL_EPENA | DOEPCTL_USBAEP;
}
@@ -896,7 +897,7 @@ static void handle_rxflvl_irq(uint8_t rhport) {
case GRXSTS_PKTSTS_SETUP_RX: {
// Setup packet received
- uint32_t* setup = (uint32_t*)(uintptr_t) _dcd_usbbuf.setup_packet;
+ uint32_t* setup = (uint32_t*)(uintptr_t) _dcd_usbbuf.setup_buffer;
// We can receive up to three setup packets in succession, but only the last one is valid.
setup[0] = (*rx_fifo);
setup[1] = (*rx_fifo);
@@ -956,7 +957,7 @@ static void handle_epout_slave(uint8_t rhport, uint8_t epnum, dwc2_doepint_t doe
if (edpt_is_enabled(epin0)) {
edpt_disable(rhport, 0x80, false);
}
- dcd_event_setup_received(rhport, _dcd_usbbuf.setup_packet, true);
+ dcd_event_setup_received(rhport, _dcd_usbbuf.setup_buffer, true);
return;
}
@@ -1021,9 +1022,13 @@ static void handle_epout_dma(uint8_t rhport, uint8_t epnum, dwc2_doepint_t doepi
if (edpt_is_enabled(epin0)) {
edpt_disable(rhport, 0x80, false);
}
- dma_setup_prepare(rhport);
- dcd_dcache_invalidate(_dcd_usbbuf.setup_packet, 8);
- dcd_event_setup_received(rhport, _dcd_usbbuf.setup_packet, true);
+ dcd_dcache_invalidate(_dcd_usbbuf.setup_buffer, 8);
+ dcd_event_setup_received(rhport, _dcd_usbbuf.setup_buffer, true);
+
+ // Prepare EP0 for next setup if this setup has no data stage
+ if (_dcd_usbbuf.setup_packet.wLength == 0) {
+ dma_setup_prepare(rhport);
+ }
return;
}
@@ -1044,9 +1049,8 @@ static void handle_epout_dma(uint8_t rhport, uint8_t epnum, dwc2_doepint_t doepi
const uint16_t remain = tsiz.xfer_size;
xfer->total_len -= remain;
- // this is ZLP, so prepare EP0 for next setup
- // TODO use status phase rx
- if(epnum == 0 && xfer->total_len == 0) {
+ // prepare EP0 for next setup
+ if(epnum == 0) {
dma_setup_prepare(rhport);
}
@@ -1065,9 +1069,6 @@ static void handle_epin_dma(uint8_t rhport, uint8_t epnum, dwc2_diepint_t diepin
// EP0 can only handle one packet. Schedule another packet to be transmitted.
edpt_schedule_packets(rhport, epnum, TUSB_DIR_IN);
} else {
- if(epnum == 0) {
- dma_setup_prepare(rhport);
- }
dcd_event_xfer_complete(rhport, epnum | TUSB_DIR_IN_MASK, xfer->total_len, XFER_RESULT_SUCCESS, true);
}
}