use crate::mem::{AlignedBuffer, AlignmentError};
use core::alloc::LayoutError;
use core::marker::PhantomData;
use core::ptr;
use core::time::Duration;
use uefi_raw::protocol::nvme::{
NvmExpressCommand, NvmExpressCommandCdwValidity, NvmExpressPassThruCommandPacket,
};
pub mod pass_thru;
pub type NvmeCompletion = uefi_raw::protocol::nvme::NvmExpressCompletion;
pub type NvmeQueueType = uefi_raw::protocol::nvme::NvmExpressQueueType;
#[derive(Debug)]
pub struct NvmeRequest<'buffers> {
io_align: u32,
cmd: NvmExpressCommand,
packet: NvmExpressPassThruCommandPacket,
transfer_buffer: Option<AlignedBuffer>,
meta_data_buffer: Option<AlignedBuffer>,
_phantom: PhantomData<&'buffers u8>,
}
macro_rules! define_nvme_command_builder_with_cdw {
($fnname:ident: $fieldname:ident => $flagmask:expr) => {
#[must_use]
pub const fn $fnname(mut self, $fieldname: u32) -> Self {
self.req.cmd.$fieldname = $fieldname;
self.req.cmd.flags |= $flagmask.bits();
self
}
};
}
#[derive(Debug)]
pub struct NvmeRequestBuilder<'buffers> {
req: NvmeRequest<'buffers>,
}
impl<'buffers> NvmeRequestBuilder<'buffers> {
#[must_use]
pub fn new(io_align: u32, opcode: u8, queue_type: NvmeQueueType) -> Self {
Self {
req: NvmeRequest {
io_align,
cmd: NvmExpressCommand {
cdw0: opcode as u32,
..Default::default()
},
packet: NvmExpressPassThruCommandPacket {
command_timeout: 0,
transfer_buffer: ptr::null_mut(),
transfer_length: 0,
meta_data_buffer: ptr::null_mut(),
meta_data_length: 0,
queue_type,
nvme_cmd: ptr::null(), nvme_completion: ptr::null_mut(), },
transfer_buffer: None,
meta_data_buffer: None,
_phantom: PhantomData,
},
}
}
#[must_use]
pub const fn with_timeout(mut self, timeout: Duration) -> Self {
self.req.packet.command_timeout = (timeout.as_nanos() / 100) as u64;
self
}
define_nvme_command_builder_with_cdw!(with_cdw2: cdw2 => NvmExpressCommandCdwValidity::CDW_2);
define_nvme_command_builder_with_cdw!(with_cdw3: cdw3 => NvmExpressCommandCdwValidity::CDW_3);
define_nvme_command_builder_with_cdw!(with_cdw10: cdw10 => NvmExpressCommandCdwValidity::CDW_10);
define_nvme_command_builder_with_cdw!(with_cdw11: cdw11 => NvmExpressCommandCdwValidity::CDW_11);
define_nvme_command_builder_with_cdw!(with_cdw12: cdw12 => NvmExpressCommandCdwValidity::CDW_12);
define_nvme_command_builder_with_cdw!(with_cdw13: cdw13 => NvmExpressCommandCdwValidity::CDW_13);
define_nvme_command_builder_with_cdw!(with_cdw14: cdw14 => NvmExpressCommandCdwValidity::CDW_14);
define_nvme_command_builder_with_cdw!(with_cdw15: cdw15 => NvmExpressCommandCdwValidity::CDW_15);
pub fn use_transfer_buffer(
mut self,
bfr: &'buffers mut AlignedBuffer,
) -> Result<Self, AlignmentError> {
bfr.check_alignment(self.req.io_align as usize)?;
self.req.transfer_buffer = None;
self.req.packet.transfer_buffer = bfr.ptr_mut().cast();
self.req.packet.transfer_length = bfr.size() as u32;
Ok(self)
}
pub fn with_transfer_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.transfer_buffer = bfr.ptr_mut().cast();
self.req.packet.transfer_length = bfr.size() as u32;
self.req.transfer_buffer = Some(bfr);
Ok(self)
}
pub fn use_metadata_buffer(
mut self,
bfr: &'buffers mut AlignedBuffer,
) -> Result<Self, AlignmentError> {
bfr.check_alignment(self.req.io_align as usize)?;
self.req.meta_data_buffer = None;
self.req.packet.meta_data_buffer = bfr.ptr_mut().cast();
self.req.packet.meta_data_length = bfr.size() as u32;
Ok(self)
}
pub fn with_metadata_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.meta_data_buffer = bfr.ptr_mut().cast();
self.req.packet.meta_data_length = bfr.size() as u32;
self.req.meta_data_buffer = Some(bfr);
Ok(self)
}
#[must_use]
pub fn build(self) -> NvmeRequest<'buffers> {
self.req
}
}
#[derive(Debug)]
pub struct NvmeResponse<'buffers> {
req: NvmeRequest<'buffers>,
completion: NvmeCompletion,
}
impl<'buffers> NvmeResponse<'buffers> {
#[must_use]
pub const fn transfer_buffer(&self) -> Option<&'buffers [u8]> {
if self.req.packet.transfer_buffer.is_null() {
return None;
}
unsafe {
Some(core::slice::from_raw_parts(
self.req.packet.transfer_buffer.cast(),
self.req.packet.transfer_length as usize,
))
}
}
#[must_use]
pub const fn metadata_buffer(&self) -> Option<&'buffers [u8]> {
if self.req.packet.meta_data_buffer.is_null() {
return None;
}
unsafe {
Some(core::slice::from_raw_parts(
self.req.packet.meta_data_buffer.cast(),
self.req.packet.meta_data_length as usize,
))
}
}
#[must_use]
pub const fn completion(&self) -> &NvmeCompletion {
&self.completion
}
}