![]() |
RT-Thread RTOS
An open source embedded real-time operating system
|
RT-Thread provides a small SCSI core for storage backends: a host (rt_scsi_host) scans targets, issues commands through **rt_scsi_ops::transfer**, and attaches type-specific upper drivers (disk sd*, CD-ROM, …) that register **rt_blk_disk** devices.
The core is not a device-model bus (see comment in scsi.c) — there is no rt_scmi-style match/probe bus. Host adapters call **rt_scsi_host_register()** after filling **rt_scsi_host**.
| Piece | Path |
|---|---|
| API / CDB layouts | components/drivers/include/drivers/scsi.h |
| Core | components/drivers/scsi/scsi.c |
| Block disk (type 0) | components/drivers/scsi/scsi_sd.c |
| CD-ROM (type 5) | components/drivers/scsi/scsi_cdrom.c |
Kconfig: RT_USING_SCSI (needs RT_USING_DM). Options RT_SCSI_SD / RT_SCSI_CDROM (need RT_USING_BLK).
Unregister: rt_scsi_host_unregister() walks **lun_nodes**, optional **ops->reset**, type **remove**, frees each **rt_scsi_device**.
Build CDBs with packed structs in **scsi.h** (rt_scsi_inquiry, rt_scsi_read10, …) or fill **cmd.op** manually. Set **op_size** to the CDB length your transport accepts.
High-level helpers in **scsi.c** (rt_scsi_inquiry, rt_scsi_read10, rt_scsi_write16, rt_scsi_read_capacity10, …) assemble **rt_scsi_cmd** and call **host->ops->transfer**. On failure they often issue **rt_scsi_request_sense**.
**rt_scsi_cmd_is_write(cmd)** — used by VirtIO-SCSI to pick IN vs OUT virtqueue buffers.
| Field | Rule |
|---|---|
dev | Non-NULL |
ops | Non-NULL; **transfer** required |
max_id / max_lun | Both non-zero; define scan grid |
parallel_io | Set if adapter can pipeline multiple LUN commands |
lun_nodes | Do not pre-fill; core initializes the list |
id ∈ [0, max_id)**, **lun ∈ [0, max_lun)**.rt_scsi_inquiry(&tmp_sdev, NULL)** — failure skips the LUN.devtype >= SCSI_DEVICE_TYPE_MAX**, scan stops and **max_id / max_lun** are clipped to the current index (simple bus assumption).rt_scsi_device** is copied from the temp device (id, lun, devtype, removable).scsi_device_setup**: optional **reset** → wait TEST UNIT READY (up to ~5 s) → **driver_table[devtype].probe**.Returns **RT_EOK** if at least one LUN probed; **-RT_EEMPTY** if none; **-RT_EINVAL** / **-RT_ENOMEM** on bad args or OOM (partial LUNs may remain on ENOMEM mid-scan).
cmd->data** (ptr + size for READ/WRITE; embedded structs for INQUIRY / capacity).RT_EOK** on command completion; RT error codes on transport failure.cmd->data.request_sense**; helpers already call REQUEST SENSE after some failures.devtype (SCSI_DEVICE_TYPE_*) | Kconfig | Probe | Block device |
|---|---|---|---|
0x00 DIRECT | RT_SCSI_SD | scsi_sd_probe | sd*a — read/write/sync/erase |
0x05 CDROM | RT_SCSI_CDROM | scsi_cdrom_probe | sr*a — read-only |
Other inquiry types log not supported and are skipped.
**scsi_sd_probe**: READ CAPACITY 10; if last LBA is 0xffffffff, READ CAPACITY 16 and use READ/WRITE 16 ops; sets geometry from **sdev->block_size** / **last_block**; **rt_hw_blk_disk_register**. Honors **host->parallel_io** on the disk.
**scsi_cdrom_probe**: capacity + read-only **rt_blk_disk** (uses READ 10/12/16 by LBA width).
These embed **struct rt_scsi_host parent** and call **rt_scsi_host_register** after hardware init:
| Backend | Source | Notes |
|---|---|---|
| AHCI | ata/ahci.c | SATA/ATAPI → ahci_scsi_ops.transfer; see ATA / AHCI |
| UFS | ufs/ufs.c | UTP carries SCSI CDBs; ufs_host_ops, often parallel_io = RT_TRUE |
| VirtIO SCSI | virtio/virtio-scsi.c | RT_VIRTIO_DRIVER_EXPORT; 3 queues; hotplug via config_changed + work queue re-register |
Typical host setup (pattern):
Teardown: **rt_scsi_host_unregister(&hba->parent)** before freeing the controller.
Opcodes and structs are defined for common storage commands:
| Opcode macro | Struct | Helper |
|---|---|---|
RT_SCSI_CMD_INQUIRY | rt_scsi_inquiry | rt_scsi_inquiry() |
RT_SCSI_CMD_TEST_UNIT_READY | rt_scsi_test_unit_ready | rt_scsi_test_unit_ready() |
RT_SCSI_CMD_READ_CAPACITY10/16 | capacity structs | rt_scsi_read_capacity10/16() |
RT_SCSI_CMD_READ10/12/16 | read structs | rt_scsi_read10/12/16() |
RT_SCSI_CMD_WRITE10/12/16 | write structs | rt_scsi_write10/12/16() |
RT_SCSI_CMD_SYNCHRONIZE_CACHE10/16 | sync cache | rt_scsi_synchronize_cache10/16() |
RT_SCSI_CMD_WRITE_SAME10/16 | write same | rt_scsi_write_same10/16() (erase path in scsi_sd) |
| Mode select/sense | mode structs | rt_scsi_mode_* (auto-refresh in scsi_sd) |
Use these when implementing **transfer** for a new HBA or when extending **driver_table** for another device type.
| Symptom | Checks |
|---|---|
-RT_EEMPTY | No LUN answered INQUIRY — wiring, power, max_id/max_lun, firmware |
-RT_ETIMEOUT in setup | Media not ready — TEST UNIT READY loop |
-RT_ENOSYS | Unsupported devtype — enable RT_SCSI_SD / RT_SCSI_CDROM or add driver_table entry |
| READ/WRITE fail | Sense via rt_scsi_request_sense; verify cmd->data.size is in sectors for READ/WRITE helpers |
| VirtIO hotplug | virtio_scsi_config_changed → rescan; log virtio.dev.scsi |
sd*blk) — block layer APIrt_blk_disk) — rt_blk_disk registrationcomponents/drivers/include/drivers/scsi.hcomponents/drivers/scsi/scsi.ccomponents/drivers/virtio/virtio-scsi.c