[−][src]Trait scsi::scsi::ScsiResponder
A trait to describe a device to respond to SCSI command, such as a flash drive.
This trait allows for more easily creating new SCSI-speaking devices by mapping
each command into 1 or 2 type-checking functions and then handling the command-independent
boiler plate in the included process_command
function. Each function is passed the command
that triggered it, even if that command contains no information (such as TestUnitReady or RequestSense).
All SCSI sessions end with the device sending a CommandStatusWrapper
back to the host; therefore,
nearly all functions here expect the device to construct one to be returned. Usually this will be done
via Ok(CommandStatsWrapper::default())
, but error situations can be set as necessary as well.
Associated Types
type BlockType: AsRef<[u8]> + AsMut<[u8]>
The type to use as a memory buffer for per-block operations, mainly read
or write transfers. Usually this would be of the form [u8 ; N]
, where
N
is a multiple of 256.
Required methods
fn read_capacity(
&mut self,
command: ReadCapacityCommand
) -> Result<(ReadCapacityResponse, CommandStatusWrapper), ScsiError>
&mut self,
command: ReadCapacityCommand
) -> Result<(ReadCapacityResponse, CommandStatusWrapper), ScsiError>
Called in response to a ReadCapacityCommand
from the host.
Since the command itself doesn't actually carry information, it should generally be ignored. However, it is still passed in just in case.
fn inquiry(
&mut self,
command: InquiryCommand
) -> Result<(InquiryResponse, CommandStatusWrapper), ScsiError>
&mut self,
command: InquiryCommand
) -> Result<(InquiryResponse, CommandStatusWrapper), ScsiError>
Called in response to a InquiryCommand
from the host.
Currently, the library does not yet include support for allocation_length
s
not equal to 36; in the future more research will be done into which fields
are added and removed at different values.
fn request_sense(
&mut self,
command: RequestSenseCommand
) -> Result<CommandStatusWrapper, ScsiError>
&mut self,
command: RequestSenseCommand
) -> Result<CommandStatusWrapper, ScsiError>
Called in response to a InquiryCommand
from the host.
Currently it is not known what the command's allocation_length
field does,
but it is still passed in to the command regardless.
fn test_unit_ready(
&mut self,
command: TestUnitReady
) -> Result<CommandStatusWrapper, ScsiError>
&mut self,
command: TestUnitReady
) -> Result<CommandStatusWrapper, ScsiError>
Called in response to a TestUnitReady
from the host.
All of the response information is encoded in the CommandStatusWrapper
;
a responder that never fails should use Ok(CommandStatusWrapper::default())
.
fn read10_start(&mut self, command: Read10Command) -> Result<(), ScsiError>
Called when the host sends the Read10
command itself over the
wire.
The responder should prepare for the upcoming read_block
calls; this
could involve things like pre-loading the relevant sections into RAM, setting
up indices, etc.
fn read_block(
&mut self,
buffer: &mut [u8]
) -> Result<Option<CommandStatusWrapper>, ScsiError>
&mut self,
buffer: &mut [u8]
) -> Result<Option<CommandStatusWrapper>, ScsiError>
Called multiple times after a read10_start
command to pull the relevant data out of the responder.
buffer
will be guranteed to be equal to the block length of the device as specified by the responder's
memory_buffer
method; in nearly all cases, it will be the same buffer. The method will keep being called until
it returns Some(_)
, even if this leads to a different number of blocks being read than expected by the original
Read10Command
; it is up to the responder to gurantee that the number of blocks read is correct.
fn write10_start(&mut self, command: Write10Command) -> Result<(), ScsiError>
Called when the host sends the Write10
command itself over the
wire.
The responder should prepare for the upcoming write_block
calls; this
could involve things like pre-loading the relevant sections into RAM, setting
up indices, etc.
fn write_block(
&mut self,
buffer: &[u8]
) -> Result<Option<CommandStatusWrapper>, ScsiError>
&mut self,
buffer: &[u8]
) -> Result<Option<CommandStatusWrapper>, ScsiError>
Called multiple times after a write10_start
command to pull the relevant data out of the responder.
buffer
will be guranteed to be equal to the block length of the device as specified by the responder's
memory_buffer
method; in nearly all cases, it will be the same buffer. The method will keep being called until
it returns Some(_)
, even if this leads to a different number of blocks being written to than expected by the original
Write10Command
; it is up to the responder to gurantee that the number of blocks read is correct.
fn memory_buffer(&mut self) -> Self::BlockType
Generates a new, owned instance of the responder's block buffer.
Usually, this can be implemented as just [0 ; N]
, where N
is the same
as the one picked for Self::BlockType
.
Provided methods
fn process_command<C: CommunicationChannel>(
&mut self,
channel: &mut C
) -> Result<(), ScsiError>
&mut self,
channel: &mut C
) -> Result<(), ScsiError>
Processes a single command from a host, from reading the CBW to outputting the CSW.
First, the CBW and command header is read from channel
via in_transfer
;
the correct method is then called on self
based on which opcode was read.
Next, if necessary for that particular command (currently, only Write10
),
a new block buffer will be allocated via self.memory_buffer()
and any needed input blocks
will be pulled from channel
and routed to the relevant method on self
.
Next, if necessary for that particular command (currently, only Read10
),
a new block buffer will be allocated via self.memory_buffer()
and any needed ouput blocks
will be pulled from the relevant method on self
and pushed to channel
.
Next, if the command has an extra specialized response struct, it will be sent via channel.out_transfer
using a 31-length buffer.
Finally, the CSW struct's tag is set to match the input CBW's and it is sent across the channel using a 31-length buffer.