summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhathach <[email protected]>2026-07-10 00:17:52 +0700
committerhathach <[email protected]>2026-07-17 17:26:03 +0700
commit439a60a87f4039beba5a1d202b7ff6f9d93745dc (patch)
treef77496ad9618c7ca043b646c702532390c9f967c
parentfa1fee0a5f82b5a78ace26ee1722c1d636c5b32a (diff)
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) <[email protected]> Claude-Session: https://claude.ai/code/session_01ExGPLP5eU43LR7o6yYLpNi
-rw-r--r--src/portable/chipidea/ci_fs/dcd_ci_fs.c11
1 files changed, 11 insertions, 0 deletions
diff --git a/src/portable/chipidea/ci_fs/dcd_ci_fs.c b/src/portable/chipidea/ci_fs/dcd_ci_fs.c
index 0f3675349..b03670551 100644
--- a/src/portable/chipidea/ci_fs/dcd_ci_fs.c
+++ b/src/portable/chipidea/ci_fs/dcd_ci_fs.c
@@ -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);