dcd_ci_fs: disarm sibling BDT on short-packet OUT completion

A multi-packet OUT transfer speculatively arms both even/odd BDTs to avoid
NAK. When the host ends the transfer early with a short packet, the sibling
BDT was left armed (own=1), desyncing the even/odd ping-pong so the next OUT
packet landed at buffer+max_packet_size instead of buffer and the stack read
stale data. Disarm the sibling on completion.

Fixes device/mtp on Kinetis (GetDeviceInfo command was received into the wrong
buffer half -> hang). Pre-existing (MSC only arms single-packet command
receives so it never hit the double-buffer path). HIL: frdm_kl25z & frdm_k64f
device 13/13.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ExGPLP5eU43LR7o6yYLpNi
This commit is contained in:
hathach
2026-07-10 00:17:52 +07:00
parent fa1fee0a5f
commit 439a60a87f

View File

@ -175,6 +175,17 @@ static void process_tokdne(uint8_t rhport)
return;
}
const unsigned length = ep->length;
/* Transfer is complete. For OUT, a multi-packet transfer speculatively arms the
* sibling (even/odd) BDT to avoid NAK. When the transfer ends early - e.g. the host
* sends a short packet before filling both buffers - that sibling is left armed
* (own=1). A leftover armed BDT desyncs the even/odd ping-pong so the next OUT
* packet lands in the wrong buffer half (buffer + max_packet_size instead of
* buffer), making the stack read stale data. Disarm it here. */
if (dir == TUSB_DIR_OUT) {
_dcd.bdt[epnum][dir][odd ^ 1].own = 0;
}
dcd_event_xfer_complete(rhport,
tu_edpt_addr(epnum, dir),
length - remaining, XFER_RESULT_SUCCESS, true);