Commands¶
- class squishy.scsi.command.GroupCode(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)¶
SCSI Command Group Code
Each SCSI command belongs to a group that defines its characteristics. It also defines which commands are optional (
o
), mandatory (m
), reserved (r
), shared (s
), or vendor specific (v
).If a command is optional (
o
) then the target does not need to implement it. If the command is mandatory (m
) then it must be implemented by the target. If a command is reserved (r
) then it should not be implemented by the target, if a command is shared (s
) then the opcode depends on the target device class, and may have multiple meanings. Finally, if the command is vendor specific (v
) then the command might be implemented depending on the target vendor and its meaning is not standardized.Each group code is three-bits long, allowing for a maximum of eight groups, with the remaining five-bits in the command opcode for the command identifier itself.
Groupe Code
Group Description
0b000
Six-byte Commands
0b001
Ten-byte Commands
0b010
Reserved Groups
0b011
0b100
0b101
Twelve-byte Commands
0b110
Vendor Specific
0b111
- GROUP0 = 0¶
Six-byte Commands
opcode
Type
Command
0x00
o
Test Unit Ready
0x01
s
0x02
v
0x03
m
Request Sense
0x04
&0x05
s
0x06
v
0x07
&0x08
s
0x09
v
0x0A
&0x0B
s
0x0C
to0x0E
v
0x0F
to0x11
s
0x12
m
Inquiry
0x13
to0x17
s
0x18
o
Copy
0x19
to0x`B
s
0x1C
o
Recv Diagnostic
0x1D
Send Diagnostic
0x1E
s
0x1F
r
- GROUP1 = 1¶
Ten-byte Commands
opcode
Type
Command
0x00
to0x04
v
0x05
s
0x06
&0x07
v
0x08
s
0x09
v
0x0A
&0x0B
s
0x0C
&0x0D
v
0x0E
to0x13
s
0x14
to0x18
r
0x19
o
Compare
0x1A
Copy and Verify
0x1B
to0x1F
r
- GROUP2 = 2¶
Reserved Group
opcode
Type
Command
0x00
to0x0F
r
- GROUP3 = 3¶
Reserved Group
opcode
Type
Command
0x00
to0x0F
r
- GROUP4 = 4¶
Reserved Group
opcode
Type
Command
0x00
to0x0F
r
- GROUP5 = 5¶
Twelve-byte Commands
opcode
Type
Command
0x00
to0x0F
v
0x10
to0x1F
r
- GROUP6 = 6¶
Vendor Specific Commands
opcode
Type
Command
0x00
to0x1F
v
- GROUP7 = 7¶
Vendor Specific Commands
opcode
Type
Command
0x00
to0x1F
v
- class squishy.scsi.command.SCSICommand(opcode: int, group_code: GroupCode, *subcons, size: int | None = None, **subconskw)¶
SCSI Command Structure
Creates a
construct.Struct
for arbitrary SCSI Commands. This wraps the structure and gives it some useful common operations needed to consume and generate SCSI commands.This class automatically creates the opcode and control sections of the command and surrounds the provided
*subcons
with them.The rough layout of a SCSI command looks like this:
Byte
7
6
5
4
3
2
1
0
0
opcode
n
data
n+1
control
Both the
opcode
andcontrol
fields of the command are fixed size at 8 bits, but thedata
section may span multiple bytes and is command dependent.The size of the command represents the full size of the structure, where the size of the command data itself is
sizeof(data) - 2
to account for theopcode
andcontrol
bytes. Therefore a Six-byte command has four-bytes of actual command data for example.The layout of the
opcode
itself is a ‘sub structure’ which describes the group the command is in as well as the command itself.7
6
5
4
3
2
1
0
group code
command code
The
group code
(GroupCode
) is the top three bits of the opcode, followed by thecommand code
which is the remaining seven bits.Due to the split between
group code
andcommand code
as well as the different classes that implement commands, there is a large re-use ofopcode
’s, this complicates things, as we need to know ahead of time what the device is in order to succesfully dispatch and parse commands.The
control
byte also has it’s own ‘sub structure’.7
6
5
4
3
2
1
0
vendor
reserved
flag
link
The
vendor
field makes up the top two bits of the control byte, and is vendor unique.The
reserved
field follows and is made up of the next four bits.The
flag
bit can only be set if thelink
bit is set. Iflink
is set and the command completes successfully then the target sends aLINKED COMMAND COMPLETE
message ifflag
is zero and aLINKED COMMAND COMPLETE WITH FLAG
message ifflag
is set. This behavior is commonly used to trigger an interrupt in the initiator.The
link
bit represents an automatic link to the next command is requested by the initiator if the command is successful. Implementation of this is optional, and depending on if it is supported or not, one of the following two behaviors is expected if thelink
flag is set.If supported, when a command completes successfully. The target will return a
INTERMEDIATE
status and then send a message depending on the state offlag
.If unsupported, the target will return a
CHECK CONDITION
status and depending if extended sense is implemented, it will set the sense key toILLEGAL REQUEST
if eitherflag
orlink
are set.
Examples
You can define a new
SCSICommand
like any other construct structure.Command = SCSICommand( # Final command opcode is calculated by ``opcode | group_code`` # And then prefixed prior to the given fields 0x01, # Command Opcode GroupCode.GROUP0, # Command Group 'Foo' / construct.Int8ul, 'Bar' / construct.BitStruct( 'Nya', construct.BitsInteger(3), 'UwU', construct.BitsInteger(5) ) # The SCSI 'control' byte is then added after. )
You can also use the special
SCSICommandField
class to simply attach defaults, descriptions, and automatically compute sizes.Command = SCSICommand( 0x01, GroupCode.GROUP0, 'Foo' / SCSICommandField('This is a field', default = 0, length = 3), 'Bar' / SCSICommandField('This is also a field', length = 5) )
- Parameters:
opcode (int) – The operation code (opcode) for the given command.
group_code (GroupCode) – The specific SCSI group code for this command.
size (None, int) – The size of the command, if not provided, this is inferred from the
group_code
if possible.*subcons (list[construct.Subconstruct]) – The collection of construct
construct.Subconstruct
members representing the command.
- Variables:
Note
The result of the
sizeof()
call returns the size of the SCSI command in bits. To get the size in bytes either call thelen()
method or divide the result of thesizeof()
call by8
.- parse(data, **ctxkw)¶
Parse an in-memory buffer (often bytes object). Strings, buffers, memoryviews, and other complete buffers can be parsed with this method.
Whenever data cannot be read, ConstructError or its derivative is raised. This method is NOT ALLOWED to raise any other exceptions although (1) user-defined lambdas can raise arbitrary exceptions which are propagated (2) external libraries like numpy can raise arbitrary exceptions which are propagated (3) some list and dict lookups can raise IndexError and KeyError which are propagated.
Context entries are passed only as keyword parameters **contextkw.
- Parameters:
**contextkw – context entries, usually empty
- Returns:
some value, usually based on bytes read from the stream but sometimes it is computed from nothing or from the context dictionary, sometimes its non-deterministic
- Raises:
ConstructError – raised for any reason
- class squishy.scsi.command.SCSICommand6(opcode: int, *subcons, **subconmskw)¶
Six-byte SCSI Command
This is a specialization of the
SCSICommand
class that deals with six-byte SCSI commands. It automatically sets the group code to Group 0.The rough layout of the six-byte SCSI commands are the
opcode
followed by fourdata
bytes, and then thecontrol
byte.Byte
7
6
5
4
3
2
1
0
0
opcode
1
data
2
3
4
5
control
- Parameters:
opcode (int) – The operation code (opcode) for the given command.
*subcons (list[construct.Subconstruct]) –
The collection of construct
construct.Subconstruct
members representing the command.
- Variables:
Note
The result of the
sizeof()
call returns the size of the SCSI command in bits. To get the size in bytes either call thelen()
method or divide the result of thesizeof()
call by8
.
- class squishy.scsi.command.SCSICommand10(opcode: int, *subcons, **subconmskw)¶
Ten-byte SCSI Command
This is a specialization of the
SCSICommand
class that deals with ten-byte SCSI commands. It automatically sets the group code to Group 1.The rough layout of the ten-byte SCSI commands are the
opcode
followed by eightdata
bytes, and then thecontrol
byte.Byte
7
6
5
4
3
2
1
0
0
opcode
1
data
2
3
4
5
6
7
8
9
control
- Parameters:
opcode (int) – The operation code (opcode) for the given command.
*subcons (list[construct.Subconstruct]) –
The collection of construct
construct.Subconstruct
members representing the command.
- Variables:
Note
The result of the
sizeof()
call returns the size of the SCSI command in bits. To get the size in bytes either call thelen()
method or divide the result of thesizeof()
call by8
.
- class squishy.scsi.command.SCSICommand12(opcode: int, *subcons, **subconmskw)¶
Twelve-byte SCSI Command
This is a specialization of the
SCSICommand
class that deals with twelve-byte SCSI commands. It automatically sets the group code to Group 5.The rough layout of the ten-byte SCSI commands are the
opcode
followed by tendata
bytes, and then thecontrol
byte.Byte
7
6
5
4
3
2
1
0
0
opcode
1
data
2
3
4
5
6
7
8
9
A
B
control
- Parameters:
opcode (int) – The operation code (opcode) for the given command.
*subcons (list[construct.Subconstruct]) –
The collection of construct
construct.Subconstruct
members representing the command.
- Variables:
Note
The result of the
sizeof()
call returns the size of the SCSI command in bits. To get the size in bytes either call thelen()
method or divide the result of thesizeof()
call by8
.
- class squishy.scsi.command.SCSICommandField(description: str = '', default: Any = None, *, length: int | None = None)¶
SCSI Command Field
This is a wrapper
construct.Subconstruct
to allow for some metadata and automatic type deduction to be preformed based on the field name.By default SCSI Command fields don’t have any type prefixing in their name, however this class allows for names with a prefix to automatically set the size and type.
The following table lists the field name prefixes and their type information:
Prefix
Type
u8l
construct.Int8ul
u16l
construct.Int16ul
u24l
construct.Int24ul
u32l
construct.Int32ul
u64l
construct.Int64ul
s8l
construct.Int8sl
s16l
construct.Int16sl
s24l
construct.Int24sl
s32l
construct.Int32sl
s64l
construct.Int64sl
u8b
construct.Int8ub
u16b
construct.Int16ub
u24b
construct.Int24ub
u32b
construct.Int32ub
u64b
construct.Int64ub
s8b
construct.Int8sb
s16b
construct.Int16sb
s24b
construct.Int24bl
s32b
construct.Int32sb
s64b
construct.Int64sb
b#
construct.BitsInteger
An example prefixed field name would be
u8lAllocLen
which would define an unsigned eight-bit unsigned little-endian integer calledAllocLen
.However, if the
length
argument is passed, that will override the prefix calculated by the name if any is present.
- class squishy.scsi.command.CommandEmitter(command: SCSICommand)¶
Creates an emitter based on the specified SCSI command.
Given a SCSI command like the following:
Command = SCSICommand6(0x00, 'Foo' / SCSICommandField(default = 0, length = 8), 'Bar' / SCSICommandField(length = 8) )
You are then able to construct an emitter and use it like so:
e = CommandEmitter(Command) e.Bar = 0xab # b'\x00\xab' data = e.emit()
It is also possible to use it within a context like the following:
with CommandEmitter(Command) as cmd: cmd.Bar = 0x15 # b'\x00\x15' cmd.emit()
- Parameters:
command (SCSICommand) – The
SCSICommand
to wrap.
- emit() bytes ¶
Emit bytes
Takes the assigned fields and generates a byte string from the specified format.
Warning
This method wraps the internal
construct.Subconstruct
in aconstruct.Bitwise()
to serialize the structure to bytes.