summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorhathach <[email protected]>2026-06-15 16:42:10 +0700
committerhathach <[email protected]>2026-06-15 16:42:10 +0700
commitabc3114d53aee421a0a457ece561ac000ace2c67 (patch)
tree4a83a5bcd549d9863e86de62727250bd5bfe1980 /src
parent3bdf52fc1ba06eb96ca28e9c3656b0d6a59cd201 (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.c49
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);