1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
|
************
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.
|