Send SCSI Command Descriptor Blocks (CDBs) to a device using a BlueSCSI, PiSCSI, or Linux SG as an initiator.
$ scsiexec --target 1 --cdb 12:00:00:00:24:00
00000000 00:00:02:02:39:00:00:18:51:55:41:4e:54:55:4d:20 '....9...QUANTUM '
00000010 46:49:52:45:42:41:4c:4c:20:20:20:20:20:20:20:20 'FIREBALL '
00000020 31:2e:30:20 '1.0 'Inspired by the useful utility s2pexec from scsi2pi by Uwe Seimet.
You do not have to specify transfer length. scsiexec works it out from the opcode:
| metadata | length |
|---|---|
| fixed-length command | a constant (READ CAPACITY(10) is 8, READ POSITION 20) |
| block-counted command | field value x device block size |
| everything else | the field value, in bytes |
with the 6-byte special case that a READ(6)/WRITE(6) count of 0 means 256 blocks. Block size comes from a cached READ CAPACITY(10), overridable with --block-size. -n forces a length.
$ scsiexec --dry-run -c 28:00:00:00:00:00:00:00:01:00
READ(10) cdb=28:00:00:00:00:00:00:00:01:00 dir=in length=512 block_size=512--dry-run needs no device, so a CDB can be checked before it reaches the bus.
Opcodes 0xc0-0xff are decoded via a flat file lookup table:
$ scsiexec --cdb-meta bluescsi-toolbox -i 1 -c d0:00:00:00:00:00:00:00:02:00# opcode len dir xoff xw unit loff lw name
d0 10 in 6 4 bytes 0 0 TOOLBOX LIST FILES
d8 10 none 0 0 bytes 0 0 TOOLBOX SET NEXT CD
Profiles are searched in $SCSIEXEC_PROFILE_PATH, $XDG_CONFIG_HOME/scsiexec/profiles, ~/.config/scsiexec/profiles, /etc/scsiexec/profiles and the installed share directory. profiles/ ships the BlueSCSI Toolbox and DaynaPORT sets.
--device /dev/sg3 names a node directly and works everywhere. -i <ID> looks the target up on the host bus.
$ scsiexec -i 0 -c 00:00:00:00:00:00
scsiexec: SCSI id 0 is ambiguous, 8 controllers offer it:
/dev/sg0 host2 ahci
/dev/sg8 host13 aic7xxx
...
narrow it with --hba <driver> or name the node with --device--bluescsi <port> sends the command over the USB serial console of a BlueSCSI running in initiator mode.
$ scsiexec --bluescsi /dev/ttyACM0 -i 1 -c 12:00:00:00:24:00
00000000 00:00:02:02:39:00:00:18:51:55:41:4e:54:55:4d:20 '....9...QUANTUM '
$ scsiexec --bluescsi /dev/ttyACM0 --list-devices
ID DEVICE TYPE BLOCK
1 /dev/ttyACM0 disk (0) 512
3 /dev/ttyACM0 cdrom (5) 2048The wire protocol is line-oriented text, you can use the console yourself via any console app.
> scsi 1.0 12:00:00:00:24:00 in 36
scsi> status 00
scsi> len 36
scsi> data 00000202390000185155414e54554d20
scsi> data 2020202020312e3020000000000000
scsi> end 0
The board must be booted in initiator mode (InitiatorMode=1 in bluescsi.ini,
or the DIP switch).
--gpio makes scsiexec the initiator, bit-banging the SCSI bus through a PiSCSI GPIO.
$ sudo scsiexec --gpio --list-devices
ID DEVICE TYPE BLOCK
1 gpio disk (0) 512
3 gpio cdrom (5) 2048
$ sudo scsiexec --gpio -i 1 -c 12:00:00:00:24:00
$ sudo scsiexec --gpio -i 1:1 -c 12:00:00:00:24:00 # LUN via IDENTIFY--initiator-id sets the initiator's own SCSI id, default 7.
The GPIO registers are mapped to /dev/mem, which is root-only. But if your user is in the gpio group it can use /dev/gpiomem, though drive strength is not changable to 16ma, you will get a notice. Run as sudo if you have an issue.
$ scsiexec --gpio -i 1 -c 12:00:00:00:24:00
scsiexec: note: mapped /dev/gpiomem instead of /dev/mem, so GPIO drive strength
is left at the 8 mA default rather than raised to 16 mA. ...Raspberry Pi 1 to 4 only. The Pi 5 puts its GPIO behind the RP1 southbridge
over PCIe with a different register layout; the board is detected from
/proc/device-tree/model.
| platform | backend | state |
|---|---|---|
| any POSIX | --bluescsi, a BlueSCSI in initiator mode over USB serial |
working |
| Linux | SG_IO on /dev/sgN, /dev/sdX, /dev/srX, /dev/bsg/* |
working |
| Raspberry Pi 1-4 | --gpio, bit-banged initiator |
working |
NOTE: SG_IO needs root or CAP_SYS_RAWIO, or a udev rule granting access to the sg
nodes.
scsiexec 0.1.0 - send raw SCSI CDBs and decode the response
Usage: scsiexec [options] -c <CDB>
Target:
-i, --target ID[:LUN] SCSI target id on the host bus
--device PATH device node directly (/dev/sgN, /dev/sdX, ...)
--list-devices print id/path/type/blocksize and exit
--bluescsi PORT send through a BlueSCSI in initiator mode
over its USB serial console (--serial is an alias)
--gpio drive the bus from this Raspberry Pi's GPIO
(PiSCSI / SCSI2Pi board, needs root)
--initiator-id N this initiator's own SCSI id (default 7)
--no-irq-mask do not mask interrupts during a --gpio transfer
--gpio-selftest[=O:I] check the GPIO register access and exit, without
putting anything on the SCSI bus. O:I adds a
jumper loopback between two BCM pins
--hba DRIVER only consider targets on this controller
Command:
-c, --cdb HEX CDB, colon/space separated or bare hex
-f, --binary-input FILE DATA OUT payload from a binary file
-d, --data HEX DATA OUT payload as hex (@FILE reads hex from a file)
-n, --length BYTES force the transfer length
-b, --buffer-size N cap an inferred DATA IN length (default 65536)
--block-size N block size for block-counted commands
-t, --timeout SECS command timeout (default 20)
-r, --reset-bus reset the bus and exit
-R, --request-sense send REQUEST SENSE after an error
Output:
-F, --binary-output FILE write DATA IN to a binary file
-T, --hex-output FILE write DATA IN as hex text
-x, --hex-only drop the offset and ASCII columns
--format FMT s2pexec (default), plain or json
--decode-sense HEX decode sense bytes and exit, no device needed
--dump-hex HEX render hex bytes as a dump and exit
Metadata:
--cdb-meta FILE load a vendor opcode profile (repeatable)
--chunk allow splitting an oversized transfer
--dry-run show the resolved command without opening a device
-L, -l accepted for s2pexec compatibility, ignored
-h, --help this help
-v, --version versionWith nix, straight from the repo - no clone, no build steps of your own:
$ nix profile add github:BlueSCSI/scsiexec # nix < 2.25: profile install
$ nix run github:BlueSCSI/scsiexec -- --version # or just run it once
$ nix profile add github:BlueSCSI/scsiexec/v0.1.0 # pinned to a release tagSince this is a developer tool intended to build simply on modern Linux systems we don't provide a precompiled binary other than Nix.
cmake -B build && cmake --build build -j
ctest --test-dir buildRequires a C++17 compiler. Tests use doctest, found on the system or fetched.
There is a nix flake; nix build and nix develop work as usual.
./cross.sh builds a static aarch64 binary; give it a host and it installs it
too.
$ ./cross.sh # build only, into build-arm64/
$ ./cross.sh eric@piscsi # build, scp, install to /usr/local/binBSD 3-Clause. Derived from SCSI2Pi by
Uwe Seimet, also BSD 3-Clause; see NOTICE for what came from where.