blob: 25020dd3a688ab9a3af7d53c5a15f3bf45d93209 [file]
************
Mass Storage
************
Roles: device and host. TinyUSB implements the USB Mass Storage Bulk-Only
Transport (BOT) and common SCSI commands. It exposes logical blocks; a
filesystem such as FatFs is a separate application layer.
Device
======
Enable ``CFG_TUD_MSC`` and set ``CFG_TUD_MSC_EP_BUFSIZE``. The driver supports
a single Mass Storage function, so add one ``TUD_MSC_DESCRIPTOR``. Multiple
logical units (LUNs) share that function and are selected through the ``lun``
callback argument.
``CFG_TUD_MSC_EP_BUFSIZE`` has no default and is required when MSC is enabled.
It is the class transfer-buffer size, not the media capacity. Using at least
one logical block is efficient; larger values improve large transfers at the
cost of static RAM. It must be at least 64 and below 65535 bytes, and should be
a multiple of the bulk endpoint's maximum packet size (512 at high speed) so
that READ10/WRITE10 data is split only on packet boundaries. Keep it
consistent with controller/DMA constraints.
The application must implement ``tud_msc_test_unit_ready_cb()``,
``tud_msc_capacity_cb()``, ``tud_msc_read10_cb()``, ``tud_msc_write10_cb()``
and ``tud_msc_scsi_cb()``. The other callbacks below have weak defaults.
.. list-table::
:header-rows: 1
:widths: 38 62
* - Callback
- Purpose
* - ``tud_msc_inquiry2_cb()``
- Fill the INQUIRY response and return its length. The older
``tud_msc_inquiry_cb()`` still works when this returns 0.
* - ``tud_msc_test_unit_ready_cb()``
- Report whether media is present and usable.
* - ``tud_msc_capacity_cb()``
- Return the logical block count and block size.
* - ``tud_msc_read10_cb()``
- Read ``bufsize`` bytes from ``lba`` plus ``offset``.
* - ``tud_msc_write10_cb()``
- Write ``bufsize`` bytes to ``lba`` plus ``offset``.
* - ``tud_msc_scsi_cb()``
- Handle commands not implemented by the class driver.
* - ``tud_msc_get_maxlun_cb()``
- Returns the number of LUNs (default 1); the driver reports this minus
one to the host. Implement it for multiple LUNs.
* - ``tud_msc_is_writable_cb()``
- Reports write protection before WRITE10.
* - ``tud_msc_start_stop_cb()``
- Handles load/eject and start/stop requests, including safe-eject policy.
Read and write callbacks may cover only part of a logical block; honor both
``lba`` and ``offset`` instead of assuming one callback per block.
``TUD_MSC_RET_ERROR`` from read10/write10 overwrites the stored sense with
NOT READY / MEDIUM NOT PRESENT. For other command callbacks, such as
``tud_msc_scsi_cb()``, set useful sense data with ``tud_msc_set_sense()``
before returning an error.
For temporarily busy media, return ``TUD_MSC_RET_BUSY``; TinyUSB will invoke
the callback again with the same parameters. For true background I/O, return
``TUD_MSC_RET_ASYNC`` and later call ``tud_msc_async_io_done()`` with the byte
count or error. Do not report a write complete until data has reached the
durability level promised by the product: finish that storage work before
returning success from ``tud_msc_write10_cb()`` or calling
``tud_msc_async_io_done()``. ``tud_msc_write10_complete_cb()`` runs only after
the host has received the command status, so a cache flush there is
best-effort and cannot make an acknowledged write durable.
See :doc:`../../examples/device/cdc_msc` for a RAM disk and
:doc:`../../examples/device/msc_dual_lun` for multiple LUNs.
Host
====
Set ``CFG_TUH_MSC`` to 1 to enable the driver; it keeps state for every device
address, so ``CFG_TUH_DEVICE_MAX`` sets how many Mass Storage devices can be
used at once. ``CFG_TUH_MSC_MAXLUN`` is the LUN limit per device. After
``tuh_msc_mount_cb(dev_addr)``, query cached geometry with
``tuh_msc_get_block_count()`` and ``tuh_msc_get_block_size()``.
Pass a LUN below both ``tuh_msc_get_maxlun()`` and ``CFG_TUH_MSC_MAXLUN``: the
LUN argument is not range-checked, and ``tuh_msc_get_maxlun()`` returns the
LUN count the device reports without clamping it. ``CFG_TUH_MSC_MAXLUN``
defaults to 4 and allocates cached state per possible LUN for each of the
``CFG_TUH_DEVICE_MAX`` addresses. Enumeration only reads the geometry of
LUN 0, so other LUNs read back as 0.
``tuh_msc_read10()`` and ``tuh_msc_write10()`` are asynchronous. Keep the
buffer valid, correctly aligned, cache coherent, and accessible to the USB
controller until the ``tuh_msc_complete_cb_t`` callback runs. Check
``tuh_msc_ready()`` before starting another command and check
``cb_data->csw->status`` in the completion callback. The callback has no
separate USB transfer result, so it does not report transfer failures on its
own.
.. list-table::
:header-rows: 1
:widths: 40 60
* - Host API or callback
- What it does
* - ``tuh_msc_mounted()`` / ``tuh_msc_ready()``
- Tests whether the MSC device is present or currently able to accept a
new SCSI command.
* - ``tuh_msc_get_maxlun()`` /
``tuh_msc_get_block_count()`` /
``tuh_msc_get_block_size()``
- Returns the device-reported LUN count and the geometry cached during
enumeration (LUN 0 only).
* - ``tuh_msc_read10()`` / ``tuh_msc_write10()``
- Queues an integral number of logical blocks and completes through the
supplied callback. Returns ``false`` without queuing if the device is
not mounted or ``block_count`` x block size exceeds the 32-bit CBW
data length.
* - ``tuh_msc_inquiry()`` / ``tuh_msc_request_sense()``
- Queues standard SCSI identification or detailed-error requests into an
application-owned response buffer.
* - ``tuh_msc_scsi_command()``
- Queues a caller-built command block wrapper for commands without a
convenience API.
* - ``tuh_msc_mount_cb()`` / ``tuh_msc_umount_cb()``
- Announces cached capacity availability or invalidates all filesystem and
media state for the address.
TinyUSB does not mount a filesystem automatically. Connect the sector API to
your filesystem's disk I/O layer and invalidate that state in
``tuh_msc_umount_cb()``. The :doc:`../../examples/host/msc_file_explorer`
example demonstrates this with FatFs.
Practical notes
===============
* Hosts cache filesystem data. Physical removal or firmware reset without an
eject/unmount can lose data even when USB transfers succeeded.
* Use the medium-present and write-protect responses consistently; mismatched
capacity or readiness information causes repeated SCSI recovery traffic.
* Validate every LUN and block range before calculating a storage address.
Specification used: *USB Mass Storage Class Bulk-Only Transport*, Revision
1.0. Command formats and sense data come from the applicable SCSI command set.