summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhathach <[email protected]>2026-08-14 11:29:37 +0700
committerhathach <[email protected]>2026-08-17 12:17:00 +0700
commitf75b247304397a1bd8bd60867c947c5be0f72743 (patch)
tree1f633669512b096d21899996881d1625405439de
parent7530f13031c8e316131fe6272f7905db1245cfd9 (diff)
portable/nrf5x: complete zero-length bulk OUT transfersclaude/mtp-data-phase-fixes
A zero-length OUT read (e.g. the MTP driver's read for the host's terminating data ZLP) could never complete when the ZLP's EPDATA arrived after the read was armed: actual_len < total_len is always false for total_len == 0, so the handler only set data_received and the armed transfer hung forever, wedging both bulk endpoints (host sees pure NAK; EP0 stays alive). Run the 0-byte DMA in that case to drain the endpoint and complete the transfer, matching what the arm path already does when the ZLP arrives first. Found via usbmon on the HIL rig: device/mtp on feather_nrf52840_express wedged after SendObject's terminating ZLP whenever the class armed the read before the host's (NAK-delayed) ZLP retry landed - deterministic under CI load, which is why PR CI only passed this test on a re-flash retry (21.4 s vs the normal 2.2 s).
-rw-r--r--src/portable/nordic/nrf5x/dcd_nrf5x.c5
1 files changed, 4 insertions, 1 deletions
diff --git a/src/portable/nordic/nrf5x/dcd_nrf5x.c b/src/portable/nordic/nrf5x/dcd_nrf5x.c
index 5170e0645..5c5f8ba8d 100644
--- a/src/portable/nordic/nrf5x/dcd_nrf5x.c
+++ b/src/portable/nordic/nrf5x/dcd_nrf5x.c
@@ -805,7 +805,10 @@ void dcd_int_handler(uint8_t rhport) {
if (tu_bit_test(data_status, 16 + epnum) || (epnum == 0 && is_control_out)) {
xfer_td_t* xfer = get_td(epnum, TUSB_DIR_OUT);
- if (xfer->started && xfer->actual_len < xfer->total_len) {
+ // total_len == 0: an armed zero-length read (e.g. MTP's terminating ZLP) still
+ // needs the 0-byte DMA to drain the endpoint and complete the transfer;
+ // actual_len < total_len can never be true for it.
+ if (xfer->started && (xfer->total_len == 0 || xfer->actual_len < xfer->total_len)) {
xact_out_dma(epnum);
} else {
// Data overflow !!! Nah, nRF will auto accept next Bulk/Interrupt OUT packet