Skip to content

feat(usb_device): mass storage (MSC) function — SD card and flash FAT media as USB drives - #798

Open
finger563 wants to merge 6 commits into
mainfrom
feat/usb-device-msc
Open

finger563 wants to merge 6 commits into
mainfrom
feat/usb-device-msc

Conversation

@finger563

Copy link
Copy Markdown
Contributor

Implements the long-reserved MscFunction on espp::UsbDevice: expose an SD card and/or a FAT partition in flash as USB drives, while the firmware keeps reading and writing files on the same media through the VFS. Motivated by esp-box-emu, which exposes its uSD card over MSC with hand-rolled TinyUSB descriptors today.

How it works

esp_tinyusb (≥ 2.0) already ships a complete MSC storage backend: SCSI callbacks, SD card and wear-levelled flash media, and the VFS hand-over between app and host. It also drives that hand-over from its own tud_mount_cb / tud_umount_cb, which still fire under UsbDevice. So this PR does not add a block layer. It adds the MSC interface to the sequential descriptor allocator and wraps the storage backend in the component's API.

Ownership model. Each medium belongs to one side at a time, so the firmware and a PC never write the same FAT volume at once:

Owner Firmware Host
MscOwner::App volume mounted at base_path (fopen, std::filesystem) sees "no medium"
MscOwner::Host base_path unmounted sees the drive

With auto_handover (default) the host takes the media when it mounts the device, and the app gets them back on eject or detach. Turn it off to call set_msc_owner() yourself (e.g. esp-box-emu's "USB drive" toggle).

API

espp::UsbDevice::MscMedium card;
card.type = espp::UsbDevice::MscMedium::Type::SdCard;
card.sd_card = sd_card;          // initialized sdmmc_card_t* (SDMMC or SDSPI host)
card.base_path = "/sdcard";

espp::UsbDevice::MscFunction msc;
msc.media = {card};              // up to two: one SD card + one flash partition
msc.on_event = [](size_t lun, MscEvent e, MscOwner o) { /* hand-over / format-required */ };
cfg.msc = msc;
  • set_msc_owner(lun, owner, ec), reporting failures that esp_tinyusb's own setter silently ignores (no_such_device for an unformatted medium, io_error otherwise)
  • msc_owner(lun), msc_capacity(lun), msc_lun_count(), format_msc_medium(lun, ec), set_msc_event_callback()
  • Media are created before the TinyUSB driver starts (an already-connected host finds them on its first mount) and torn down after it stops.
  • Validation: 1–2 media, at most one of each type, distinct absolute base paths, SD media only on targets with an SDMMC host, CONFIG_TINYUSB_MSC_ENABLED required.
  • Endpoint budget unchanged: MSC is one interface + bulk IN/OUT.

Example

New components/usb_device/msc_example, which needs no SD card, so it runs on any ESP32-S3 board. It exposes a 1 MiB flash FAT partition. On boot the app writes a boot counter and a README to it. The host sees those files when it mounts the drive, and after the host ejects, the app lists the directory again, including anything the PC added. Added to the CI matrix, the docs toctree and Doxygen. The README and docs page gain an "Enabling mass storage (MSC)" section, and the "(future)" notes are gone.

Limits worth knowing (all from esp_tinyusb's backend, documented in the header / README)

  • Formatting runs on FatFs drive 0, not the medium's own drive (f_mkfs("")). format_if_unformatted defaults off and is documented as safe only when no other FAT volume is mounted. This looks like an upstream bug worth reporting to esp-usb.
  • SCSI inquiry strings are esp_tinyusb's fixed TinyUSB / TEST MSC Storage. They are not weak symbols, so they cannot be overridden without replacing the backend.
  • LittleFS / SPIFFS cannot be exposed: hosts only read FAT. Use a FAT partition for storage shared with a PC.
  • Destroying the UsbDevice unmounts an app-owned medium's base_path. An SD card stays initialized but must be mounted again if the app still needs it. For esp-box-emu's enable/disable-USB flow that means remounting /sdcard after turning USB off. A small remount helper could be a follow-up.

Verified

  • msc_example builds for ESP32-S3 with and without the component manager (the CI command)
  • The existing example (CDC + vendor) and xinput_example still build (MSC-disabled path)
  • cppcheck: no new findings on the changed lines
  • Not hardware-tested: enumeration, hand-over on mount/eject, and the SD card path all still need a board.

🤖 Generated with Claude Code

https://claude.ai/code/session_01DFjjnq3XCTRAXENSxsCxJU

… media as USB drives

Implements the MscFunction extension point on espp::UsbDevice, built on
esp_tinyusb's MSC storage backend (which provides the SCSI handling):

- MscFunction exposes one or two media (LUNs): an initialized SD card
  (SDMMC or SDSPI host) and/or a `data, fat` flash partition, which the
  device mounts through wear levelling.
- Ownership model: while the application owns a medium its FAT volume is
  mounted at MscMedium::base_path (fopen / std::filesystem); while the
  host owns it the path is unmounted and the PC sees the drive. With
  auto_handover (default) the host takes the media on mount and the app
  gets them back on eject / detach; set_msc_owner() hands them over
  explicitly.
- msc_owner(), msc_capacity(), msc_lun_count(), format_msc_medium() and
  an MscEvent callback (hand-over started / done / failed, format
  required / failed) report and manage the state; set_msc_owner()
  reports failures esp_tinyusb's setter swallows.
- One interface + bulk IN/OUT from the sequential allocator (endpoint
  budget unchanged); media are created before the TinyUSB driver starts so
  an already-connected host finds them, and torn down after it stops.
- Validation: 1..2 media, at most one of each type (esp_tinyusb backends
  are singletons), distinct absolute base paths, SD media only on targets
  with an SDMMC host; CONFIG_TINYUSB_MSC_ENABLED is required.

New msc_example exposes a 1 MiB flash FAT partition, writes a boot
counter + README from the app, and lists the directory again after the
host ejects the drive. README, docs page, Doxygen, CI matrix and the
component manifest are updated; the "(future)" MSC notes are gone.

Documented esp_tinyusb limits: formatting runs on FatFs drive 0 (only safe
with no other FAT volume mounted), fixed SCSI inquiry strings, and LittleFS
/ SPIFFS cannot be exposed (hosts only read FAT).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DFjjnq3XCTRAXENSxsCxJU
Copilot AI lite review requested due to automatic review settings September 16, 2026 15:09
@github-actions

github-actions Bot commented Sep 16, 2026

Copy link
Copy Markdown

✅Static analysis result - no issues found! ✅

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Warning

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Pull request overview

Adds USB Mass Storage Class (MSC) support to espp::UsbDevice, allowing SD cards and/or FAT flash partitions to be exposed as USB drives with an explicit app/host ownership hand-over model.

Changes:

  • Implemented MscFunction (descriptor allocation + esp_tinyusb MSC storage backend integration) with ownership APIs and event callbacks.
  • Added a new msc_example project plus CI build entry.
  • Updated docs/README/Doxygen inputs to document and reference MSC support and the new example.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
doc/en/buses/usb_cdc.rst Documents MSC feature, usage, and endpoint budget table updates.
doc/en/buses/msc_example.md Adds doc page that includes the example README.
doc/Doxyfile Adds the MSC example source to Doxygen example inputs.
components/usb_device/src/usb_device.cpp Implements MSC driver install/storage creation, descriptor wiring, ownership/format APIs, and event bridging.
components/usb_device/include/usb_device.hpp Adds public MSC types (MscFunction, MscMedium, events/owners) and new MSC APIs.
components/usb_device/CMakeLists.txt Adds required/public deps for MSC types and private deps for flash MSC backend.
components/usb_device/idf_component.yml Updates description/tags and registers msc_example.
components/usb_device/README.md Adds MSC documentation and references new example.
components/usb_device/msc_example/* New example project: config, partition table, README, and main implementation.
.github/workflows/build.yml Adds CI build job for msc_example.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread components/usb_device/src/usb_device.cpp
Comment thread components/usb_device/src/usb_device.cpp
Comment thread components/usb_device/src/usb_device.cpp
- static analysis: drop the constParameterPointer suppression CI's
  cppcheck reports as unmatched, and restructure the SD-card validation so
  no statement follows the return selected when the target has no SDMMC
  host (unreachableCode)
- FormatFailed now logs a format-specific message (esp_tinyusb passes no
  error code; its own log has the FatFs result); OwnerChangeFailed names
  the side the hand-over was going to
- comments at the event bridge (esp_tinyusb emits MOUNT_START before
  updating the owner and MOUNT_COMPLETE after, so event->mount_point is the
  previous / new owner) and at the base_path assignment (the field is a
  non-const char *, hence data())

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DFjjnq3XCTRAXENSxsCxJU

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Ownership failure detection and teardown can report success or release resources while the backend still retains them.

Get a fresh assessment by requesting another Copilot review.

Review details

Suppressed comments (4)

Previously missed (3) — in code that hasn't changed since the last review.

components/usb_device/src/usb_device.cpp:982

  • Combining MSC with X-Input still satisfies the existing xinput_only predicate because it only excludes CDC, vendor, and HID. That makes an X-Input+MSC composite advertise the Microsoft VID/PID and 0xFF device class intended only for a standalone controller. Include !config_.msc in that predicate so composites retain the configured identity.
    .github/workflows/build.yml:360
  • The example matrix is required to stay alphabetized. Place msc_example before xinput_example.
    doc/Doxyfile:206
  • EXAMPLE_PATH entries are required to remain alphabetized. Move the MSC example before the X-Input example.

components/usb_device/src/usb_device.cpp:2169

  • tinyusb_msc_delete_storage() can fail while deferred writes are pending. Clearing the handle anyway and then unmounting wear levelling leaves esp_tinyusb's live LUN referencing released resources; the ignored driver-uninstall failure also prevents a later instance from initializing. Only clear/release resources after successful deletion, and explicitly handle or retry this failure.
    if (lun.storage) {
      esp_err_t err = tinyusb_msc_delete_storage(lun.storage);
      if (err != ESP_OK)
        logger_.warn("MSC medium {}: deleting the storage failed: {}", i, esp_err_to_name(err));
      lun.storage = nullptr;
  • Files reviewed: 15/15 changed files
  • Comments generated: 3
  • Review effort level: Balanced

Comment thread components/usb_device/src/usb_device.cpp Outdated
Comment thread components/usb_device/src/usb_device.cpp Outdated
Comment thread components/usb_device/src/usb_device.cpp Outdated
…er against the VFS

- esp_tinyusb frees a storage object still mapped as a LUN when the mount
  it performs during creation fails, returning no handle: the stale LUN
  could never be removed and SCSI requests would reach freed memory. Media
  are now always created host-owned (nothing can fail after the handle is
  ours) and handed to the application afterwards when initial_owner is App.
  A missing filesystem stays non-fatal; any other mount failure fails
  initialize() and tears the media down cleanly.
- set_msc_owner() and the initial hand-over go through hand_over_msc(),
  which confirms the outcome with esp_vfs_fat_info() instead of relying on
  events: esp_tinyusb's setter records the requested owner whatever the
  mount / unmount did, and several failure paths raise no event. A failed
  mount (other than "no filesystem", which stays app-owned for formatting)
  quietly resets the medium to the host and reports io_error; a volume
  still mounted after a hand-over to the host is unregistered before
  success is reported, else io_error.
- OwnerChangeFailed carries the side that still has the medium: the log now
  names the attempted destination, and the callback doc says so.
- The creating-LUN event fallback is gone (handles are known before any
  hand-over now).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DFjjnq3XCTRAXENSxsCxJU

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

MSC teardown, repeated unformatted handovers, dependency compatibility, and SD-card lifetime documentation need correction.

Get a fresh assessment by requesting another Copilot review.

Review details

Suppressed comments (4)

Previously missed (4) — in code that hasn't changed since the last review.

components/usb_device/include/usb_device.hpp:532

  • This API may run storage operations from an application task while auto_handover runs them in the TinyUSB task, but the component manifest still permits esp_tinyusb 2.0.0. Espressif's changelog says MSC storage-operation multitask protection was added only in 2.0.1, so projects locked to 2.0.0 can race these paths. Raise the esp_tinyusb dependency floor to >=2.0.1 (or add equivalent serialization here).
    .github/workflows/build.yml:360
  • The CI matrix is kept alphabetized (the surrounding entries run through touch, tla2528, tt21100, twai, usb_device, then usb_host), but this places msc_example after xinput_example. Put msc_example before xinput_example to preserve that ordering.
    components/usb_device/include/usb_device.hpp:259
  • This suggests obtaining sd_card from esp_vfs_fat_sdspi_mount() and then unmounting it, but ESP-IDF's matching esp_vfs_fat_sdcard_unmount() frees the helper-allocated sdmmc_card_t. Passing that pointer here would therefore leave MSC with a dangling card. Document only caller-owned storage initialized with sdmmc_card_init() (as the example already does).
    doc/Doxyfile:206
  • EXAMPLE_PATH is explicitly required to remain alphabetized (doc/Doxyfile:72-74), but msc_example is inserted after xinput_example. Move it between the base USB example and xinput_example.
  • Files reviewed: 15/15 changed files
  • Comments generated: 2
  • Review effort level: Balanced

Comment thread components/usb_device/src/usb_device.cpp
Comment thread components/usb_device/src/usb_device.cpp
…f-review fixes

Review:
- deinit_msc() only releases what sits behind a storage object that was
  actually deleted: a failed tinyusb_msc_delete_storage() keeps the handle
  and its wear-levelling mount (the LUN is still mapped), and the MSC
  driver is marked uninstalled only when uninstall succeeds.
- Asking for the application again for an unformatted medium now returns
  no_such_device instead of being misread as a failed mount and reverted
  to the host: a per-LUN no_filesystem flag (set on FormatRequired, cleared
  on a successful mount or format) survives esp_tinyusb's same-owner no-op,
  which emits no event. A medium marked application-owned with nothing
  mounted for another reason is reset quietly and mounted again.

Self-review:
- Host writes are queued and run later on the TinyUSB task, and a storage
  object with writes queued cannot be deleted. The destructor stopped the
  task first, which lost the last queued writes and leaked the storage. It
  now drops the pull-up (tud_disconnect), deletes the media while the task
  still runs, retrying (bounded, 1 s) until queued writes have run, and
  only then uninstalls the driver.
- format_msc_medium(): esp_tinyusb reports "no free FatFs drive" with the
  same ESP_ERR_NOT_FOUND as "filesystem exists", so that case was reported
  as file_exists. A mounted volume and a missing drive slot are now checked
  first (file_exists / device_or_resource_busy).
- set_msc_owner() doc: a host mount / eject during the call races it with
  auto_handover on; msc_owner() doc: an unformatted medium reports App.
- fatfs added to PRIV_REQUIRES (esp_vfs_fat_info, ff_diskio_get_drive).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DFjjnq3XCTRAXENSxsCxJU
@finger563

Copy link
Copy Markdown
Contributor Author

Self-review of the full MSC diff (fixes in 64e1850, alongside the two review threads above).

Found and fixed:

  • Queued host writes were lost at teardown. Host writes run later on the TinyUSB task, and the destructor stopped that task before deleting the storage, so the last queued writes never reached the medium. It now disconnects the host, lets the queue drain while deleting the media (bounded, 1 s), and only then stops the driver.
  • format_msc_medium() misreported "no free FatFs drive" as file_exists, because esp_tinyusb uses ESP_ERR_NOT_FOUND for both. A mounted volume (file_exists) and a missing drive slot (device_or_resource_busy) are now checked first.
  • Docs. set_msc_owner() notes that a host mount or eject during the call races it while auto_handover is on, so turn it off if the app drives ownership. msc_owner() notes that an unformatted medium reports App with nothing mounted.
  • fatfs added to PRIV_REQUIRES: the source now calls esp_vfs_fat_info() and ff_diskio_get_drive() directly.

Checked and left as is:

  • Validation, descriptor allocation (one interface, bulk IN/OUT on one endpoint number), and the endpoint budget.
  • Singleton and teardown ordering on every initialize() failure path: media are released on each one.
  • base_path lifetime: fixed std::array storage, so no reallocation.
  • Event bridge lookup after teardown (the singleton is cleared first).

Known limits that remain, all in esp_tinyusb and documented:

  • Formatting runs on FatFs drive 0.
  • The automatic hand-overs on host mount or eject run inside esp_tinyusb's own callbacks, so they are not verified the way set_msc_owner() is.
  • esp_tinyusb does not lock its owner field between its setter and those callbacks.

Verified: msc_example builds with and without the component manager; the CDC and X-Input examples still build; cppcheck at --check-level=exhaustive finds nothing new. Still not hardware-tested.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

MSC teardown can abort or strand backend state, and X-Input/MSC composites receive the wrong USB identity.

Get a fresh assessment by requesting another Copilot review.

Review details

Suppressed comments (4)

Previously missed (3) — in code that hasn't changed since the last review.

components/usb_device/src/usb_device.cpp:997

  • An MSC-enabled X-Input composite still satisfies the existing xinput_only predicate below because that predicate does not check config_.msc. The device will therefore advertise the Xbox VID/PID and 0xFF device class even though MSC is also present, contradicting the documented X-Input-only identity and preventing normal composite binding. Include !config_.msc in xinput_only (or reject this combination).
    .github/workflows/build.yml:360
  • The MSC matrix entry is after xinput_example, so this section is no longer alphabetical as required by the repository instructions. Move msc_example before xinput_example.
    doc/Doxyfile:206
  • The new example is out of alphabetical order: msc_example must precede xinput_example. The repository instructions require Doxyfile entries to remain alphabetized when an existing component changes.

components/usb_device/src/usb_device.cpp:528

  • If the one-second drain times out, the first deinit_msc() intentionally leaves the storage mapped, but these lines still stop the TinyUSB task and retry deletion afterward. Deferred writes can no longer complete, so deletion remains ESP_ERR_INVALID_STATE; the MSC driver and backing storage survive while impl_ (including the borrowed base_path) is destroyed, and a later instance cannot install the global MSC driver. Make teardown report whether all LUNs were released and do not stop TinyUSB until release is guaranteed.
    // Anything the drain above could not release (normally nothing).
    deinit_msc();
  • Files reviewed: 15/15 changed files
  • Comments generated: 1
  • Review effort level: Balanced

Comment thread components/usb_device/src/usb_device.cpp
…re deletion

An unformatted medium (or one whose mount failed) is marked application-owned
with no drive registered. esp_tinyusb's tinyusb_msc_delete_storage() does
ESP_ERROR_CHECK(msc_storage_unmount()), whose medium unmount returns
ESP_ERR_INVALID_STATE with no drive registered, aborting the device on
teardown. deinit_msc() now resets such a medium to the host first (the setter
records the owner even though its own unmount fails), so the delete skips the
unmount.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DFjjnq3XCTRAXENSxsCxJU

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

The allowed backend version and some teardown paths can lose deferred writes or leave dangling MSC resources.

Get a fresh assessment by requesting another Copilot review.

Review details

Suppressed comments (3)

Previously missed (3) — in code that hasn't changed since the last review.

components/usb_device/src/usb_device.cpp:1480

  • This failure path stops the TinyUSB task before deleting MSC storage, unlike the destructor's required ordering. A host can begin enumeration immediately after tinyusb_driver_install(), so if CDC initialization then fails with an MSC write deferred, deinit_msc() cannot drain it after uninstall and leaves the MSC singleton/storage installed; subsequent initialization will remain busy. Disconnect and run the timed MSC drain while the TinyUSB task is still alive, then uninstall it.
    .github/workflows/build.yml:360
  • The CI matrix is required to remain alphabetical. Move msc_example before xinput_example so the components/usb_device/* entries stay ordered.
    doc/Doxyfile:206
  • The Doxygen example list is required to remain alphabetical. Place msc_example between example and xinput_example; the current insertion after xinput_example breaks the maintained ordering.
  • Files reviewed: 15/15 changed files
  • Comments generated: 2
  • Review effort level: Balanced

Comment thread components/usb_device/src/usb_device.cpp Outdated
Comment thread components/usb_device/src/usb_device.cpp
…usb >= 2.0.1; composite identity

- Teardown: MSC media are released by release_msc_before_uninstall()
  (disconnect, delete while the TinyUSB task still runs queued writes,
  bounded 1 s), and deinit_msc() now reports whether everything was
  released. If a storage object is still mapped, the driver is NOT
  uninstalled and impl_ is deliberately leaked (esp_tinyusb still points
  at its base_path strings and media) instead of stopping the task and
  freeing state it references. The initialize() CDC-init failure path
  uses the same ordering (a host may already have queued writes).
- esp_tinyusb floor raised to >= 2.0.1: that release adds MSC
  storage-operation multitask protection and the delete_storage() guard
  against freeing a storage with queued writes (verified: esp-usb commit
  ec187f0 is in the v2.0.1 release). The vendored copy is 2.2.1.
- An X-Input + MSC composite no longer takes the standalone Xbox 360
  identity (xinput_only now also requires !msc).
- MscMedium::sd_card doc: caller-owned card from sdmmc_card_init(), not
  the one esp_vfs_fat_sd*_mount() allocates (its unmount frees it).
- msc_example ordered before xinput_example in the CI matrix and Doxyfile.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DFjjnq3XCTRAXENSxsCxJU
@finger563

Copy link
Copy Markdown
Contributor Author

Also addressed in afcbe06: the comments Copilot suppressed in its review summaries.

  • X-Input + MSC identity. xinput_only now also requires !msc, so that composite keeps the configured VID/PID instead of the standalone Xbox 360 identity.
  • SD card ownership doc. MscMedium::sd_card now asks for a caller-owned card from sdmmc_card_init(), not the one esp_vfs_fat_sdmmc_mount() / esp_vfs_fat_sdspi_mount() allocates, since esp_vfs_fat_sdcard_unmount() frees that one.
  • Ordering. msc_example now comes before xinput_example in the CI matrix and the Doxyfile EXAMPLE_PATH.
  • esp_tinyusb >= 2.0.1 for MSC multitask protection, answered in the thread above.

Verified: msc_example builds with and without the component manager, the CDC and X-Input examples still build, and exhaustive cppcheck reports nothing new.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Manual ownership transitions and formatting can race pending host I/O, and dual-LUN ejection has incorrect per-medium semantics.

Get a fresh assessment by requesting another Copilot review.

Review details

Suppressed comments (1)

Previously missed (1) — in code that hasn't changed since the last review.

components/usb_device/include/usb_device.hpp:295

  • This auto-handover description is not true per medium when two LUNs are configured. esp_tinyusb's tud_msc_start_stop_cb ignores the ejected LUN and calls msc_storage_mount_to_app(), which iterates every storage, so ejecting either host drive returns both media to the application and makes the other drive disappear too. Either use/pin a backend with per-LUN eject handling or explicitly expose/document auto-handover as an all-media operation.
  • Files reviewed: 15/15 changed files
  • Comments generated: 2
  • Review effort level: Balanced

Comment on lines +2368 to +2370
if (tinyusb_msc_set_storage_mount_point(lun.storage, to_app ? TINYUSB_MSC_STORAGE_MOUNT_APP
: TINYUSB_MSC_STORAGE_MOUNT_USB) !=
ESP_OK) {
return false;
}
}
const esp_err_t err = tinyusb_msc_format_storage(l.storage);
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.

2 participants