summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHiFiPhile <[email protected]>2026-05-20 23:22:02 +0200
committerHiFiPhile <[email protected]>2026-05-22 13:19:39 +0200
commit650e8a194fcfaa4b91203e9aedfecd614ea70105 (patch)
tree36f180f694e54a6b22e55d490f12a24b3f3b6f65
parent16bc0548dc0793404a5885aa90d78100b604c227 (diff)
dwc2: handle EP0 status OUT in RXFLVL interrupt
Signed-off-by: HiFiPhile <[email protected]>
-rw-r--r--src/portable/synopsys/dwc2/dcd_dwc2.c65
1 files changed, 24 insertions, 41 deletions
diff --git a/src/portable/synopsys/dwc2/dcd_dwc2.c b/src/portable/synopsys/dwc2/dcd_dwc2.c
index 72beb1b80..bdab49f1b 100644
--- a/src/portable/synopsys/dwc2/dcd_dwc2.c
+++ b/src/portable/synopsys/dwc2/dcd_dwc2.c
@@ -62,7 +62,6 @@ 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
@@ -70,6 +69,9 @@ typedef struct {
// SOF enabling flag - required for SOF to not get disabled in ISR when SOF was enabled by
bool sof_en;
+
+ // EP0 status OUT flag
+ bool ep0_status_out;
} dcd_data_t;
static dcd_data_t _dcd_data;
@@ -667,7 +669,7 @@ bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t to
if (epnum == 0) {
_dcd_data.ep0_pending[dir] = total_bytes;
if (dir == TUSB_DIR_OUT) {
- _dcd_data.ep0_out_zlp_armed = (total_bytes == 0);
+ _dcd_data.ep0_status_out = (total_bytes == 0);
}
}
@@ -748,11 +750,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;
+ _dcd_data.ep0_status_out = false;
// 1. NAK for all OUT endpoints
for (uint8_t n = 0; n < ep_count; n++) {
@@ -907,6 +907,9 @@ static void handle_rxflvl_irq(uint8_t rhport) {
// 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);
+
+ // Clear previous pending EP0 OUT if any
+ _dcd_data.ep0_status_out = false;
break;
}
@@ -946,6 +949,12 @@ static void handle_rxflvl_irq(uint8_t rhport) {
// Out packet done
// After this entry is popped from the receive FIFO, dwc2 asserts a Transfer Completed interrupt on
// the specified OUT endpoint which will be handled by handle_epout_irq()
+
+ // EP0 status OUT is complete
+ if (epnum == 0 && _dcd_data.ep0_status_out) {
+ _dcd_data.ep0_status_out = false;
+ dcd_event_xfer_complete(rhport, epnum, 0, XFER_RESULT_SUCCESS, true);
+ }
break;
default: break; // nothing to do
@@ -953,21 +962,7 @@ static void handle_rxflvl_irq(uint8_t rhport) {
}
static void handle_epout_slave(uint8_t rhport, uint8_t epnum, dwc2_doepint_t doepint_bm) {
- 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
dwc2_dep_t* epin0 = &DWC2_REG(rhport)->epin[0];
if (edpt_is_enabled(epin0)) {
@@ -983,13 +978,16 @@ static void handle_epout_slave(uint8_t rhport, uint8_t epnum, dwc2_doepint_t doe
// Note: even though (xfer_complete + status_phase_rx) is for buffered DMA only, for STM32L47x (dwc2 v3.00a) they
// can is set when GRXSTS_PKTSTS_SETUP_RX is popped therefore they can bet set before/together with setup_phase_done
if (!doepint_bm.status_phase_rx && !doepint_bm.setup_packet_rx) {
- if ((epnum == 0) && _dcd_data.ep0_pending[TUSB_DIR_OUT]) {
- // 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;
+ xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT);
+ if (epnum == 0) {
+ if (_dcd_data.ep0_pending[TUSB_DIR_OUT]) {
+ // EP0 can only handle one packet, Schedule another packet to be received.
+ edpt_schedule_packets(rhport, epnum, TUSB_DIR_OUT);
+ } else if (xfer->total_len > 0) {
+ // EP0 status out is handled in handle_rxflvl_irq
+ dcd_event_xfer_complete(rhport, epnum, xfer->total_len, XFER_RESULT_SUCCESS, true);
}
+ } else {
dcd_event_xfer_complete(rhport, epnum, xfer->total_len, XFER_RESULT_SUCCESS, true);
}
}
@@ -1028,21 +1026,8 @@ static void handle_epin_slave(uint8_t rhport, uint8_t epnum, dwc2_diepint_t diep
#if CFG_TUD_DWC2_DMA_ENABLE
static void handle_epout_dma(uint8_t rhport, uint8_t epnum, dwc2_doepint_t doepint_bm) {
dwc2_regs_t* dwc2 = DWC2_REG(rhport);
- 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
dwc2_dep_t* epin0 = &DWC2_REG(rhport)->epin[0];
if (edpt_is_enabled(epin0)) {
@@ -1064,6 +1049,7 @@ static void handle_epout_dma(uint8_t rhport, uint8_t epnum, dwc2_doepint_t doepi
edpt_schedule_packets(rhport, epnum, TUSB_DIR_OUT);
} else {
dwc2_dep_t* epout = &dwc2->epout[epnum];
+ xfer_ctl_t* xfer = XFER_CTL_BASE(epnum, TUSB_DIR_OUT);
// determine actual received bytes
const dwc2_ep_tsize_t tsiz = {.value = epout->tsiz};
@@ -1076,9 +1062,6 @@ 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);
}