use crate::mem::{AlignedBuffer, AlignmentError};
use core::alloc::LayoutError;
use core::marker::PhantomData;
use core::ptr;
use core::time::Duration;
use uefi_raw::protocol::scsi::{
ScsiIoDataDirection, ScsiIoHostAdapterStatus, ScsiIoScsiRequestPacket, ScsiIoTargetStatus,
};
#[cfg(doc)]
use crate::Status;
pub mod pass_thru;
pub type ScsiRequestDirection = uefi_raw::protocol::scsi::ScsiIoDataDirection;
#[derive(Debug)]
pub struct ScsiRequest<'a> {
packet: ScsiIoScsiRequestPacket,
io_align: u32,
in_data_buffer: Option<AlignedBuffer>,
out_data_buffer: Option<AlignedBuffer>,
sense_data_buffer: Option<AlignedBuffer>,
cdb_buffer: Option<AlignedBuffer>,
_phantom: PhantomData<&'a u8>,
}
#[derive(Debug)]
pub struct ScsiRequestBuilder<'a> {
req: ScsiRequest<'a>,
}
impl ScsiRequestBuilder<'_> {
#[must_use]
pub fn new(direction: ScsiRequestDirection, io_align: u32) -> Self {
Self {
req: ScsiRequest {
in_data_buffer: None,
out_data_buffer: None,
sense_data_buffer: None,
cdb_buffer: None,
packet: ScsiIoScsiRequestPacket {
timeout: 0,
in_data_buffer: ptr::null_mut(),
out_data_buffer: ptr::null_mut(),
sense_data: ptr::null_mut(),
cdb: ptr::null_mut(),
in_transfer_length: 0,
out_transfer_length: 0,
cdb_length: 0,
data_direction: direction,
host_adapter_status: ScsiIoHostAdapterStatus::default(),
target_status: ScsiIoTargetStatus::default(),
sense_data_length: 0,
},
io_align,
_phantom: Default::default(),
},
}
}
#[must_use]
pub fn read(io_align: u32) -> Self {
Self::new(ScsiIoDataDirection::READ, io_align)
}
#[must_use]
pub fn write(io_align: u32) -> Self {
Self::new(ScsiIoDataDirection::WRITE, io_align)
}
#[must_use]
pub fn bidirectional(io_align: u32) -> Self {
Self::new(ScsiIoDataDirection::BIDIRECTIONAL, io_align)
}
}
impl<'a> ScsiRequestBuilder<'a> {
#[must_use]
pub const fn with_timeout(mut self, timeout: Duration) -> Self {
self.req.packet.timeout = (timeout.as_nanos() / 100) as u64;
self
}
pub fn use_read_buffer(mut self, bfr: &'a mut AlignedBuffer) -> Result<Self, AlignmentError> {
bfr.check_alignment(self.req.io_align as usize)?;
self.req.in_data_buffer = None;
self.req.packet.in_data_buffer = bfr.ptr_mut().cast();
self.req.packet.in_transfer_length = bfr.size() as u32;
Ok(self)
}
pub fn with_read_buffer(mut self, len: usize) -> Result<Self, LayoutError> {
let mut bfr = AlignedBuffer::from_size_align(len, self.req.io_align as usize)?;
self.req.packet.in_data_buffer = bfr.ptr_mut().cast();
self.req.packet.in_transfer_length = bfr.size() as u32;
self.req.in_data_buffer = Some(bfr);
Ok(self)
}
pub fn with_sense_buffer(mut self, len: u8) -> Result<Self, LayoutError> {
let mut bfr = AlignedBuffer::from_size_align(len as usize, self.req.io_align as usize)?;
self.req.packet.sense_data = bfr.ptr_mut().cast();
self.req.packet.sense_data_length = len;
self.req.sense_data_buffer = Some(bfr);
Ok(self)
}
pub fn use_write_buffer(mut self, bfr: &'a mut AlignedBuffer) -> Result<Self, AlignmentError> {
bfr.check_alignment(self.req.io_align as usize)?;
self.req.out_data_buffer = None;
self.req.packet.out_data_buffer = bfr.ptr_mut().cast();
self.req.packet.out_transfer_length = bfr.size() as u32;
Ok(self)
}
pub fn with_write_data(mut self, data: &[u8]) -> Result<Self, LayoutError> {
let mut bfr = AlignedBuffer::from_size_align(data.len(), self.req.io_align as usize)?;
bfr.copy_from_slice(data);
self.req.packet.out_data_buffer = bfr.ptr_mut().cast();
self.req.packet.out_transfer_length = bfr.size() as u32;
self.req.out_data_buffer = Some(bfr);
Ok(self)
}
pub fn use_command_buffer(
mut self,
data: &'a mut AlignedBuffer,
) -> Result<Self, AlignmentError> {
assert!(data.size() <= 255);
data.check_alignment(self.req.io_align as usize)?;
self.req.cdb_buffer = None;
self.req.packet.cdb = data.ptr_mut().cast();
self.req.packet.cdb_length = data.size() as u8;
Ok(self)
}
pub fn with_command_data(mut self, data: &[u8]) -> Result<Self, LayoutError> {
assert!(data.len() <= 255);
let mut bfr = AlignedBuffer::from_size_align(data.len(), self.req.io_align as usize)?;
bfr.copy_from_slice(data);
self.req.packet.cdb = bfr.ptr_mut().cast();
self.req.packet.cdb_length = bfr.size() as u8;
self.req.cdb_buffer = Some(bfr);
Ok(self)
}
#[must_use]
pub fn build(self) -> ScsiRequest<'a> {
self.req
}
}
#[derive(Debug)]
#[repr(transparent)]
pub struct ScsiResponse<'a>(ScsiRequest<'a>);
impl<'a> ScsiResponse<'a> {
#[must_use]
pub const fn read_buffer(&self) -> Option<&'a [u8]> {
if self.0.packet.in_data_buffer.is_null() {
return None;
}
unsafe {
Some(core::slice::from_raw_parts(
self.0.packet.in_data_buffer.cast(),
self.0.packet.in_transfer_length as usize,
))
}
}
#[must_use]
pub const fn sense_data(&self) -> Option<&'a [u8]> {
if self.0.packet.sense_data.is_null() {
return None;
}
unsafe {
Some(core::slice::from_raw_parts(
self.0.packet.sense_data.cast(),
self.0.packet.sense_data_length as usize,
))
}
}
#[must_use]
pub const fn host_adapter_status(&self) -> ScsiIoHostAdapterStatus {
self.0.packet.host_adapter_status
}
#[must_use]
pub const fn target_status(&self) -> ScsiIoTargetStatus {
self.0.packet.target_status
}
}