diff options
| author | hathach <[email protected]> | 2026-06-15 16:42:10 +0700 |
|---|---|---|
| committer | hathach <[email protected]> | 2026-06-15 16:42:10 +0700 |
| commit | abc3114d53aee421a0a457ece561ac000ace2c67 (patch) | |
| tree | 4a83a5bcd549d9863e86de62727250bd5bfe1980 /src | |
| parent | 3bdf52fc1ba06eb96ca28e9c3656b0d6a59cd201 (diff) | |
dcd/musb: end EP0 IN data stage on short packet, split DATA case
A short IN control response (device sends fewer bytes than wLength —
e.g. the 18-byte device descriptor answering a 64-byte GET_DESCRIPTOR)
left remain_wlength != 0, so the DATA_IN -> STATUS_OUT transition never
fired and pipe0 stayed in DATA_IN through the status stage. usbd then
armed the status-OUT while state was still DATA_IN. Set DATAEND and
transition on the last packet: remain_wlength == 0, or a short packet
(incl. a terminating ZLP) which ends the data stage.
With state now tracking the stage, split edpt0_xfer's DATA handling
into separate DATA_IN / DATA_OUT cases dispatching on state (asserting
state == call direction) instead of the combined dir_in branch.
Verified: HIL pass on ek_tm4c123gxl and max32666fthr (13/13 each),
including the #3643 high-CPU-load IRQ-toggle coalescing stress.
Co-Authored-By: Claude Fable 5 <[email protected]>
Diffstat (limited to 'src')
| -rw-r--r-- | src/portable/mentor/musb/dcd_musb.c | 49 |
1 files changed, 22 insertions, 27 deletions
diff --git a/src/portable/mentor/musb/dcd_musb.c b/src/portable/mentor/musb/dcd_musb.c index f52ac10e3..261ff64e8 100644 --- a/src/portable/mentor/musb/dcd_musb.c +++ b/src/portable/mentor/musb/dcd_musb.c @@ -423,35 +423,31 @@ static bool edpt0_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_ const unsigned dir_in = tu_edpt_dir(ep_addr); switch (pipe0->state) { - // Combined: usbd can arm the opposite-direction status/ZLP while pipe0 is still in a DATA - // state, so dispatch on the call direction (dir_in), not the state. (Splitting into separate - // DATA_IN/DATA_OUT cases mis-routes those dir != state calls and breaks ADI MUSB.) + // DATA stage exits on its last packet, so state matches the call direction here. case PIPE0_STATE_DATA_IN: - case PIPE0_STATE_DATA_OUT: { + TU_ASSERT(dir_in); pipe0->xact_len = total_bytes; - if (dir_in) { - // Replayed SETUP keeps its RXRDY parked until here; ack it before loading the shared FIFO. - if (pipe0->rxrdy_consumed) { - ep_csr->csr0l = MUSB_CSRL0_RXRDYC; - pipe0->rxrdy_consumed = false; - } - // DATA IN: load FIFO, set TXRDY. Add DATAEND on the last chunk - // (remain_wlength == 0 after this load) to end the data stage. - tu_hwfifo_write(&musb_regs->fifo[0], buffer, total_bytes, NULL); - pipe0->remain_wlength -= total_bytes; - if (pipe0->remain_wlength == 0) { - ep_csr->csr0l = MUSB_CSRL0_TXRDY | MUSB_CSRL0_DATAEND; - } else { - ep_csr->csr0l = MUSB_CSRL0_TXRDY; - } - } else { - // DATA OUT: arm drain target, ack RXRDY so host can send DATA OUT. - pipe0->buf = buffer; + if (pipe0->rxrdy_consumed) { // replayed SETUP: ack its parked RXRDY before loading the FIFO ep_csr->csr0l = MUSB_CSRL0_RXRDYC; pipe0->rxrdy_consumed = false; } + tu_hwfifo_write(&musb_regs->fifo[0], buffer, total_bytes, NULL); + pipe0->remain_wlength -= total_bytes; + // DATAEND on the last packet: wLength met, or a short packet (incl. ZLP) ends the data stage. + if (pipe0->remain_wlength == 0 || total_bytes < CFG_TUD_ENDPOINT0_SIZE) { + ep_csr->csr0l = MUSB_CSRL0_TXRDY | MUSB_CSRL0_DATAEND; + } else { + ep_csr->csr0l = MUSB_CSRL0_TXRDY; + } + break; + + case PIPE0_STATE_DATA_OUT: + TU_ASSERT(!dir_in); + pipe0->xact_len = total_bytes; + pipe0->buf = buffer; // arm drain target, ack RXRDY so host can send DATA OUT + ep_csr->csr0l = MUSB_CSRL0_RXRDYC; + pipe0->rxrdy_consumed = false; break; - } case PIPE0_STATE_STATUS_IN: TU_ASSERT(dir_in && total_bytes == 0); // only STATUS IN allowed @@ -616,10 +612,9 @@ static void process_ep0(uint8_t rhport) { * - or status stage is complete (ZLP) after DataEnd is set */ switch (pipe0->state) { case PIPE0_STATE_DATA_IN: - // csrl == 0 in DATA IN = TXRDY just cleared, i.e. a DATA IN packet was successfully sent. If the - // just-sent packet was the last (DATAEND set when remain_wlength hit 0), transition to STATUS_OUT - // to await the host's STATUS-OUT ZLP confirmation IRQ. - if (pipe0->remain_wlength == 0) { + // DATA IN packet sent (TXRDY cleared). On the last packet (DATAEND condition above) move to + // STATUS_OUT to await the host's STATUS-OUT ZLP IRQ. + if (pipe0->remain_wlength == 0 || pipe0->xact_len < CFG_TUD_ENDPOINT0_SIZE) { pipe0->state = PIPE0_STATE_STATUS_OUT; } dcd_event_xfer_complete(rhport, TU_EP0_IN, pipe0->xact_len, XFER_RESULT_SUCCESS, true); |
