Skip to content

shared/usb: correctly report the SD LUN as removable media#10967

Open
mikeysklar wants to merge 2 commits intoadafruit:mainfrom
mikeysklar:pr-msc-prevent-allow
Open

shared/usb: correctly report the SD LUN as removable media#10967
mikeysklar wants to merge 2 commits intoadafruit:mainfrom
mikeysklar:pr-msc-prevent-allow

Conversation

@mikeysklar
Copy link
Copy Markdown

@mikeysklar mikeysklar commented Apr 22, 2026

What was wrong

The SD card exposed as a USB MSC LUN doesn't mount on macOS. ioreg shows the SCSI nub appears but never progresses to publishing an IOMedia. Linux handles the same firmware fine.

Why: CircuitPython was telling the host "this LUN's medium is always present" for every LUN, including the SD card. On macOS that means "skip TEST_UNIT_READY polling" — so if the SD wasn't ready at the one-shot enumeration probe, macOS never re-checked and gave up on the LUN. Linux ignored the hint and kept polling, which is why it worked there.

@hathach documented this exact macOS behavior back in #6555; the per-LUN distinction wasn't applied at the time.

What changed

Report the SD LUN as removable media, report internal flash and SAVES as non-removable. macOS now keeps polling TUR on the SD LUN and correctly picks up card insertion and removal.

~10 lines in supervisor/shared/usb/usb_msc_flash.c — per-LUN branch on the existing PREVENT_ALLOW_MEDIUM_REMOVAL handler. No RAM or flash cost.

Tested on

Board Result
Feather RP2040 Adalogger (with SD pin defines from #10968) SD mounts first try on macOS 26.x
Metro RP2040 (with SD pin defines from #10968) SD mounts
Metro RP2350 SD mounts, no regression
Fruit Jam (3-LUN: flash + CPSAVES + SD) All three mount; CPSAVES correctly stays non-removable
Linux (Ubuntu 24.04) /dev/sdc + /dev/sdd correct sizes, no regression

Windows not tested.

Related

Credit to @dhalbert for pointing at #6555 and @hathach for the original analysis.

Respond with "unsupported" for the SD LUN (removable media) and OK for
internal flash / SAVES LUNs (non-removable). Responding OK for a
removable LUN tells macOS the medium is always present, and macOS then
skips TEST_UNIT_READY polling. If the SD isn't ready at the single
enumeration probe, macOS never re-checks and LUN 1 fails to publish an
IOMedia node.

Hathach documented this behavior back in adafruit#6555; the SD case wasn't
differentiated at the time.

Fixes adafruit#10965.
Copy link
Copy Markdown
Member

@tannewt tannewt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we want them all removable.

Comment thread supervisor/shared/usb/usb_msc_flash.c Outdated
Per @tannewt review on adafruit#10967: tinyusb advertises is_removable=1 for
every LUN in its default INQUIRY response (msc_device.c line 781), so
the per-LUN branch was inconsistent with what the host already sees.
Collapse to a single unconditional ILLEGAL_REQUEST reply — host keeps
polling TUR on all LUNs, so eject/re-mount works uniformly across
CIRCUITPY, SAVES, and SD.
@mikeysklar
Copy link
Copy Markdown
Author

You're right, thanks — simplifying to apply the unsupported reply to all LUNs.

@mikeysklar
Copy link
Copy Markdown
Author

mikeysklar commented Apr 23, 2026

Quick cross-board, cross-OS regression test of this PR combined with #10963 on main (which already has #10968 merged as a37aaaf).

Board Arch Host OS LUNs enumerated CIRCUITPY mounts SD mounts Readable Regression?
Metro RP2040 (onboard SD) RP2040 macOS 26.x 2 ✅ (ADALOGGER) None
Metro RP2040 (onboard SD) RP2040 Ubuntu 24.04 2 ✅ (ADALOGGER) None
Metro RP2350 (onboard SD) RP2350 macOS 26.x 2 ✅ (ADALOGGER) None
Metro RP2350 (onboard SD) RP2350 Ubuntu 24.04 2 ✅ (ADALOGGER) None

Notes:

@dhalbert
Copy link
Copy Markdown
Collaborator

It would be good to test on Windows as well, and also on boards that are not RP2xxx, such as a PyPortal, and a Metro ESP32-S3 (onboard SD socket), etc. I'm assuming you don't have a Windows box or you would have done so. I could test some of these on Windows.

@bablokb
Copy link
Copy Markdown

bablokb commented Apr 23, 2026

I don't think that reporting /saves as removable is a good idea. Host polling seems to considerable slow down CircuitPython (see #10733). This is why I usually disable CIRCUITPY_SDCARD_USB. I never had problems with the saves-partition though. And from a logical perspective, I don't think it should be removable (of course you can umount it, but there are better ways to shoot yourself in the foot).

@dhalbert
Copy link
Copy Markdown
Collaborator

I don't think that reporting /saves as removable is a good idea.

I agree with this.

@mikeysklar
Copy link
Copy Markdown
Author

Thanks both.

On /saves: agreed. CPSAVES is a partition of the onboard QSPI flash (same chip as CIRCUITPY, just carved at offset 0), not physically removable — TUR polling on it is wasted traffic, and #10733 is a real concern I don't want to aggravate.

Comparing the two versions for the 3-LUN case (Fruit Jam: CIRCUITPY + SAVES + SD):

Current PR (all LUNs reply ILLEGAL_REQUEST):
  CIRCUITPY : TUR TUR TUR TUR TUR TUR ...   ← wasted (can't be re-inserted)
  SAVES     : TUR TUR TUR TUR TUR TUR ...   ← wasted (can't be re-inserted)
  SD        : TUR TUR TUR TUR TUR TUR ...   ← needed (catches card re-insert)

Per-LUN version (SD only — original PR):
  CIRCUITPY : TUR ─ [OK → host stops polling] ──────────
  SAVES     : TUR ─ [OK → host stops polling] ──────────
  SD        : TUR TUR TUR TUR TUR TUR ...

The per-LUN version keeps identical user-visible behavior (eject buttons from RMB=1 are unchanged, SD re-insert still auto-remounts) but drops 2/3 of the poll traffic on 3-LUN boards.

@tannewt — OK to revert to per-LUN given the polling-cost point?

On Windows / non-RP2xxx boards: I don't have a Windows host, but I do have a PyPortal and a Metro ESP32-S3 here — happy to run macOS + Linux checks on both. @dhalbert — gladly hand off Windows coverage to you. Let's agree on which version of the PR we're testing against (current / per-LUN revert) before I spin up the non-RP2xxx builds, so we're all benching the same stack.

@tannewt
Copy link
Copy Markdown
Member

tannewt commented Apr 24, 2026

I don't think the polling cost issue has been proven out. The linked issue says it occurs even when a host isn't connected. When I've looked at traces from Linux it only queried the drive state once a second. That'd shouldn't be too much overhead.

Although the flash can't be physically removed it is advantageous to "eject" it. The host flushes before doing this and then sends a signal to the device to eject. We have to remember this state so it doesn't immediately pop up again. Now, we can do storage.remount() to change writability and then "uneject" the LUN. So, it is good to tell the host it is removable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Metro RP2040: SD card LUN never probed by macOS via USB MSC (works on RP2350)

4 participants