ricardgb commented on issue #19435:
URL: https://github.com/apache/nuttx/issues/19435#issuecomment-4984407428
## Bug 5 — the one that still broke macOS after the previous fixes: the
rp2040/rp23xx DCD drops requests submitted while an endpoint is halted (the
CSW-loss race)
macOS still failed to mount with fixes 1-3 applied. The failing command
turned out to be
one the earlier validation never sent: macOS probes **MODE SENSE(6) with
PAGE CODE 0x00**
(write-protect header sniff; the IOUSBMassStorageDriver RwC trace shows CDB
`1A 00 00 00`,
allocation length 4 and then 0xC0). `usbmsc_modepage()` only implements
pages 0x08/0x3F,
so the command fails — which is fine and spec-legal: for a failed Hi>Di
command with no
data, `usbmsc_cmdfinishstate()` halts the bulk-IN endpoint (BOT 6.7.2) and
`usbmsc_cmdstatusstate()` then submits the CSW (status = failed).
The bug: `rp23xx_epsubmit()` / `rp2040_epsubmit()` **reject IN requests
submitted while
the endpoint is halted** (`stalled && in` -> abort with -EBUSY). The CSW is
therefore
dropped on the floor. When the host clears the halt and reads the CSW, the
endpoint NAKs
forever; macOS times out after 30 s and enters a
Bulk-Only-reset/device-reset spiral that
ends with the port disabled ("persistent enumeration failures").
This is precisely the race documented for over a decade in the header
comment of
`drivers/usbdev/usbmsc_scsi.c` (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 was active
(these chips do not
declare `CONFIG_ARCH_USBDEV_STALLQUEUE`), but it only survives hosts that
clear the halt
within its 100 ms sleep. Linux's usb-storage does; macOS does not — and
Linux's SCSI probe
also never sends a failing MODE SENSE page (it uses 0x3F), which is why the
earlier
validation passed while macOS kept failing.
### Fix (both rp2040_usbdev.c and rp23xx_usbdev.c): implement stall queuing
in the DCD
1. `epsubmit()`: while the endpoint is halted, requests are queued but the
hardware is
not armed (IN and OUT — arming an OUT endpoint rewrites the DPSRAM
buffer-control
word and would silently clear the STALL bit). The -EBUSY abort is removed.
2. `epstall(stall)`: after applying the hardware stall, cancel in-flight IN
requests —
their armed buffer was just disarmed and would never complete.
3. `epstall(resume)`: after clearing the halt (and resetting the data
toggle), re-arm
the head of the request queue — this delivers the queued CSW.
4. Buffer-status IRQ: ignore completions latched for transfers aborted by a
halt.
5. `arch/arm/Kconfig`: RP2040/RP23XX now `select ARCH_USBDEV_STALLQUEUE`,
which also
retires the RACEWAR's two 100 ms sleeps per failed command.
The class driver is untouched by this fix (its behavior is BOT-correct).
### Validation (silicon, RP2350 / Pico 2 W, composite ACM+NCM+MSC, Linux
host)
An important subtlety: **Linux cannot reproduce this bug via
sg_raw/usb-storage**, because
usb-storage clears the halt within microseconds — inside the RACEWAR's 100
ms window — so
the CSW submit finds the endpoint already resumed. (`sg_raw -r 4 /dev/sgX 1a
00 00 00 04
00` on the *broken* firmware completes fine in 0.23 s, of which 0.2 s are
the two RACEWAR
sleeps.) The repro needs macOS's *timing* over raw usbfs: CBW MODE SENSE(6)
page 0x00
alloc 4 → data-IN stall → **wait 1 s** (past the RACEWAR window) →
clear-halt → read CSW.
A/B on the same board, same script, same 1 s delay:
- **Before (bugs 1-3 fixed, no stall queuing):** data-IN `EPIPE` at +0.10 s
(RACEWAR sleep
visible), clear-halt OK, **CSW read `ETIMEDOUT`** — the CSW had been
aborted with -EBUSY
at submit time. This is the macOS failure exactly (its log then shows a 30
s bulk-IN
timeout followed by the BOT-reset/device-reset spiral until the port is
disabled).
- **After (stall queuing):** data-IN `EPIPE` immediately (RACEWAR retired),
clear-halt OK,
**CSW delivered instantly**: status=FAIL(1), dCSWDataResidue=4, tag
correct.
Full macOS-equivalent probe replay over raw usbfs (TUR, INQUIRY ×2, MODE
SENSE page 0
alloc 4 and 0xC0 with clear-halt recovery and CSW
signature/tag/status/residue checks,
REQUEST SENSE, READ CAPACITY exact-8, MODE SENSE 0x3F control, START STOP
UNIT ×2
variants, MSRESET + clear-halts mid-probe): **5 cycles, zero failures**.
Regressions, all clean: page-0 MODE SENSE through usb-storage now returns
CHECK
CONDITION / Illegal Request in 0.017 s (previously 0.23 s through the
RACEWAR sleeps);
MODE SENSE 0x3F, INQUIRY 255, REQUEST SENSE 252, READ CAPACITY exact-8 all
Good;
GET MAX LUN + MSRESET 10/10 via raw usbfs; FAT drive mounts and full-reads;
CDC-NCM and
CDC-ACM up; warm reboots re-enumerate cleanly.
macOS mount retest is next; will report the result here.
### Remaining loose end (honest)
In the macOS failure log there is also a ~1 s EP0 control-transfer timeout
immediately
after the first bulk-IN stall, before the 30 s CSW timeout. Clear-halt is
handled directly
in the USB IRQ and should be instant, so this is not fully explained by the
CSW loss;
candidates are a SIE disturbance from writing buffer-control while the host
is actively
IN-polling the endpoint, or the deferred MSRESET response racing a 1 s host
deadline. If
it persists on the macOS retest with this fix it will be investigated
separately.
## Disclosure
As above: 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]