mirror of
https://github.com/hathach/tinyusb.git
synced 2026-08-18 11:02:16 +00:00
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 <noreply@anthropic.com>
This commit is contained in:
@ -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);
|
||||
|
||||
Reference in New Issue
Block a user