From 060019fa60c46eaf4c76334aa07750b8861b1805 Mon Sep 17 00:00:00 2001 From: mikeysklar Date: Wed, 22 Apr 2026 16:45:15 -0700 Subject: [PATCH 1/2] shared/usb: per-LUN response to PREVENT_ALLOW_MEDIUM_REMOVAL 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 #6555; the SD case wasn't differentiated at the time. Fixes #10965. --- supervisor/shared/usb/usb_msc_flash.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/supervisor/shared/usb/usb_msc_flash.c b/supervisor/shared/usb/usb_msc_flash.c index 5ea457ef328cc..f394040b74ad8 100644 --- a/supervisor/shared/usb/usb_msc_flash.c +++ b/supervisor/shared/usb/usb_msc_flash.c @@ -214,8 +214,22 @@ int32_t tud_msc_scsi_cb(uint8_t lun, const uint8_t scsi_cmd[16], void *buffer, u switch (scsi_cmd[0]) { case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL: - // Host is about to read/write etc ... better not to disconnect disk - resplen = 0; + #ifdef SDCARD_LUN + if (lun == SDCARD_LUN) { + // Removable media (SD card). Respond "unsupported" so macOS + // keeps sending TEST_UNIT_READY periodically to detect card + // insertion/removal. Responding OK here causes macOS to skip + // TUR polling and miss media-present events on the SD LUN. + // See the discussion in adafruit/circuitpython#6555. + tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); + resplen = -1; + } else + #endif + { + // Non-removable media (internal flash, SAVES). OK is fine; + // host assumes medium always present and skips TUR polling. + resplen = 0; + } break; default: From a2b9e970b3f5e8f53af213681ef16059dcfebd2c Mon Sep 17 00:00:00 2001 From: Mikey Sklar Date: Thu, 23 Apr 2026 10:07:18 -0700 Subject: [PATCH 2/2] shared/usb: reply unsupported to PREVENT_ALLOW on all LUNs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per @tannewt review on #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. --- supervisor/shared/usb/usb_msc_flash.c | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/supervisor/shared/usb/usb_msc_flash.c b/supervisor/shared/usb/usb_msc_flash.c index f394040b74ad8..2ddf38939d3a0 100644 --- a/supervisor/shared/usb/usb_msc_flash.c +++ b/supervisor/shared/usb/usb_msc_flash.c @@ -214,22 +214,13 @@ int32_t tud_msc_scsi_cb(uint8_t lun, const uint8_t scsi_cmd[16], void *buffer, u switch (scsi_cmd[0]) { case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL: - #ifdef SDCARD_LUN - if (lun == SDCARD_LUN) { - // Removable media (SD card). Respond "unsupported" so macOS - // keeps sending TEST_UNIT_READY periodically to detect card - // insertion/removal. Responding OK here causes macOS to skip - // TUR polling and miss media-present events on the SD LUN. - // See the discussion in adafruit/circuitpython#6555. - tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); - resplen = -1; - } else - #endif - { - // Non-removable media (internal flash, SAVES). OK is fine; - // host assumes medium always present and skips TUR polling. - resplen = 0; - } + // All LUNs advertise is_removable=1 in INQUIRY (tinyusb default). + // Reply "unsupported" so the host keeps polling TUR and can + // re-mount after an eject. Responding OK tells the host to skip + // TUR polling, which breaks re-mount and can miss SD insertion + // events at boot. See adafruit/circuitpython#6555. + tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00); + resplen = -1; break; default: