squishy.scsi.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 to 0x0E

v

0x0F to 0x11

s

0x12

m

Inquiry

0x13 to 0x17

s

0x18

o

Copy

0x19 to 0x`B

s

0x1C

o

Recv Diagnostic

0x1D

Send Diagnostic

0x1E

s

0x1F

r

GROUP1 = 1#

Ten-byte Commands

opcode

Type

Command

0x00 to 0x04

v

0x05

s

0x06 & 0x07

v

0x08

s

0x09

v

0x0A & 0x0B

s

0x0C & 0x0D

v

0x0E to 0x13

s

0x14 to 0x18

r

0x19

o

Compare

0x1A

Copy and Verify

0x1B to 0x1F

r

GROUP2 = 2#

Reserved Group

opcode

Type

Command

0x00 to 0x0F

r

GROUP3 = 3#

Reserved Group

opcode

Type

Command

0x00 to 0x0F

r

GROUP4 = 4#

Reserved Group

opcode

Type

Command

0x00 to 0x0F

r

GROUP5 = 5#

Twelve-byte Commands

opcode

Type

Command

0x00 to 0x0F

v

0x10 to 0x1F

r

GROUP6 = 6#

Vendor Specific Commands

opcode

Type

Command

0x00 to 0x1F

v

GROUP7 = 7#

Vendor Specific Commands

opcode

Type

Command

0x00 to 0x1F

v

class squishy.scsi.command.SCSICommand(opcode: int, group_code: GroupCode, *subcons, size: int = 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 and control fields of the command are fixed size at 8 bits, but the data 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 the opcode and control 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 the command code which is the remaining seven bits.

Due to the split between group code and command code as well as the different classes that implement commands, there is a large re-use of opcode’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 the link bit is set. If link is set and the command completes successfully then the target sends a LINKED COMMAND COMPLETE message if flag is zero and a LINKED COMMAND COMPLETE WITH FLAG message if flag 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 the link 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 of flag.

  • If unsupported, the target will return a CHECK CONDITION status and depending if extended sense is implemented, it will set the sense key to ILLEGAL REQUEST if either flag or link 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:
  • group_code (GroupCode) – The SCSI commands group code.

  • size (int) – The total size of the SCSI command.

Note

The result of the sizeof() call returns the size of the SCSI command in bits. To get the size in bytes either call the len() method or divide the result of the sizeof() call by 8.

len() int#

Return structure length in bytes

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 four data bytes, and then the control byte.

Byte

7

6

5

4

3

2

1

0

0

opcode

1

data

2

3

4

5

control

Parameters:
Variables:
  • group_code (GroupCode) – The SCSI commands group code.

  • size (int) – The total size of the SCSI command.

Note

The result of the sizeof() call returns the size of the SCSI command in bits. To get the size in bytes either call the len() method or divide the result of the sizeof() call by 8.

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 eight data bytes, and then the control byte.

Byte

7

6

5

4

3

2

1

0

0

opcode

1

data

2

3

4

5

6

7

8

9

control

Parameters:
Variables:
  • group_code (GroupCode) – The SCSI commands group code.

  • size (int) – The total size of the SCSI command.

Note

The result of the sizeof() call returns the size of the SCSI command in bits. To get the size in bytes either call the len() method or divide the result of the sizeof() call by 8.

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 ten data bytes, and then the control 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:
Variables:
  • group_code (GroupCode) – The SCSI commands group code.

  • size (int) – The total size of the SCSI command.

Note

The result of the sizeof() call returns the size of the SCSI command in bits. To get the size in bytes either call the len() method or divide the result of the sizeof() call by 8.

class squishy.scsi.command.SCSICommandField(description: str = '', default: Any = None, *, length: int = 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 called AllocLen.

However, if the length argument is passed, that will override the prefix calculated by the name if any is present.

Parameters:
  • description (str) – The description of this field.

  • default (Any) – The default value for this field if any.

Keyword Arguments:

length (int) – The length of the field in bits.

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 a construct.Bitwise() to serialize the structure to bytes.

Returns:

The byte string of the serialized command.

Return type:

bytes

Raises:

KeyError – If missing a required field for the command.