| ************ |
| 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``. Add one |
| ``TUD_MSC_DESCRIPTOR`` for each Mass Storage function. Multiple logical units |
| (LUNs) normally share one 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 per instance, not the media capacity. |
| Using at least one logical block is efficient; larger values improve large |
| transfers at the cost of static RAM. Keep it below 65536 bytes and consistent |
| with controller/DMA constraints. |
| |
| The minimum storage callbacks are: |
| |
| .. list-table:: |
| :header-rows: 1 |
| :widths: 38 62 |
| |
| * - Callback |
| - Purpose |
| * - ``tud_msc_inquiry_cb()`` |
| - Return fixed-width vendor, product, and revision fields. |
| * - ``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 highest valid zero-based LUN number; 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. On failure, |
| set useful sense data with ``tud_msc_set_sense()`` and return |
| ``TUD_MSC_RET_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. ``tud_msc_write10_complete_cb()`` is |
| a useful place to flush a cache. |
| |
| 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 the maximum simultaneous Mass Storage devices and |
| ``CFG_TUH_MSC_MAXLUN`` to 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()``. |
| |
| ``CFG_TUH_MSC_MAXLUN`` defaults to 4 and allocates cached state per possible |
| LUN for every enabled MSC device. Set it to the maximum needed by the product, |
| not necessarily the maximum value a device claims. |
| |
| ``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 inspect the completion |
| callback's transfer result and command status. |
| |
| .. 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 cached LUN range and geometry populated during enumeration. |
| * - ``tuh_msc_read10()`` / ``tuh_msc_write10()`` |
| - Queues an integral number of logical blocks and completes through the |
| supplied callback. |
| * - ``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. |