ricardgb opened a new pull request, #19452:
URL: https://github.com/apache/nuttx/pull/19452
# PR 3 — rp2040/rp23xx: implement stall queueing and GET STATUS responses
Branch: `rp2040-rp23xx-stallqueue-getstatus` (worktree
`~/nuttxspace/wt-pr-stallq`)
Base: apache/nuttx master (1b6f918c3f)
References: #19435 (bugs 5 and 6 of that report)
## Summary
Two device-controller (DCD) defects in `rp2040_usbdev.c` / `rp23xx_usbdev.c`
that break standard host error recovery. Either one alone is fatal to macOS
mass-storage mounting; both are invisible to Linux hosts, whose SCSI stack
neither sends the triggering requests nor recovers with the triggering
timing.
## Fix 1: stall queueing (the lost-CSW race)
`epsubmit()` aborted (-EBUSY) any IN request submitted while the endpoint was
halted. The mass-storage class halts bulk-IN for a failed device-to-host
command (BOT-legal) and then submits the CSW — which was silently dropped, so
after the host's Clear-Halt the endpoint NAKed forever and the host timed out
(macOS: 30 s, then a Bulk-Only-reset/device-reset spiral until it disables
the port). This is exactly the race documented for years in the
`usbmsc_scsi.c` header comment (David Hewson's analysis: "the subsequent
transfer for the CSW would be dropped on the floor (by the epsubmit()
function) if the end point was still stalled"). The `USBMSC_STALL_RACEWAR`
sleep workaround only survives hosts that clear the halt within its 100 ms
window — Linux does, macOS does not.
This PR implements the stall-queueing contract that
`CONFIG_ARCH_USBDEV_STALLQUEUE` documents (and that `usbmsc_scsi.c` has been
waiting on):
- `epsubmit()`: requests submitted while an endpoint is halted are queued
without arming the hardware (IN and OUT — arming an OUT endpoint rewrites
the DPSRAM buffer-control word and would silently clear the STALL bit).
- `epstall(stall)`: cancels any in-flight IN transfer (its hardware buffer is
disarmed by the STALL write and would otherwise never complete).
- `epstall(resume)`: resets the data toggle (USB 2.0 9.4.5) and re-arms the
head of the request queue — this is what delivers the queued CSW.
- Buffer-status IRQ: completions latched for transfers aborted by a halt are
ignored instead of being attributed to post-halt queued requests.
- `arch/arm/Kconfig`: RP2040/RP23XX now `select ARCH_USBDEV_STALLQUEUE`,
which retires the RACEWAR's two 100 ms sleeps per failed command.
- The now-unused `*_abortrequest()` helpers are removed.
## Fix 2: GET STATUS was never answered
The `USB_REQ_GETSTATUS` case in `*_ep0setup()` validated the request but
never queued the two-byte response — for all three recipients
(device/interface/endpoint). EP0 then NAKs the host's data-IN stage forever
and every GET STATUS times out. macOS issues GetPipeStatus = GET
STATUS(endpoint) on a halted pipe ~50 ms after the stall, before Clear-Halt,
and hit this on every probe; `lsusb -v`'s device-status query hangs on it
too. The handler now sends the response (endpoint: halt bit; device:
self-powered; interface: zeros) via `epwrite(ep0, response, 2)`, with the
status stage armed by `handle_zlp()` exactly as for class-dispatched IN
transfers.
## Validation
- **RP2350 (Raspberry Pi Pico 2 W) hardware, composite CDC-ACM + CDC-NCM +
USBMSC, Linux host, raw-usbfs replay of macOS's exact commands and
timing:**
- Pre-fix repro, deterministic: CBW (failing MODE SENSE page 0) → data-IN
stall → wait 1 s → clear-halt → **CSW read ETIMEDOUT** (fix 1); all five
GET STATUS variants **ETIMEDOUT after 1.5 s** (fix 2).
- Post-fix: CSW survives the halt and is delivered after Clear-Halt with
correct tag/status/residue across a **post-stall delay sweep of
0/30/50/80/150/500/1000 ms** in macOS's recovery order (GetPipeStatus →
clear-halt → GET STATUS → CSW), with the halt bit correctly reported as
1 before and 0 after the clear; all GET STATUS variants answer in <1 ms.
- Full macOS-equivalent BOT probe replay ×5 cycles: zero failures. No
regressions: exact-length SCSI suite, GET MAX LUN + Bulk-Only reset
10/10, FAT mount + full reads, CDC-NCM/ACM, warm reboots.
- **End-to-end: macOS now mounts the volume** (together with the companion
usbmsc fixes in the separate class-driver PR). The success log shows the
expected one-time transient bulk-IN halts each cleanly recovered, and no
EP0 timeouts/resets/port-disable.
- **RP2040:** identical shared code, identical fix; build-tested
(raspberrypi-pico:usbnsh and raspberrypi-pico:composite, the latter
compiling usbmsc with ARCH_USBDEV_STALLQUEUE in effect). No RP2040
hardware validation performed.
Full failure chronology, host traces, and the silicon A/B evidence are in
#19435.
## Impact
Any class driver that stalls an endpoint and expects queued requests to
survive (mass storage is the canonical case). GET STATUS affects every
configuration — any host or tool that sends it got a control timeout.
With ARCH_USBDEV_STALLQUEUE selected, usbmsc also stops sleeping 200 ms per
failed command (RACEWAR retired).
## AI disclosure
This investigation and the fixes were performed by an AI agent (Claude Code),
operated and directed by the submitter, and reviewed by the submitter before
posting.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]