use crate::mem::{AlignedBuffer, AlignmentError};
use crate::util::usize_from_u32;
use core::alloc::LayoutError;
use core::marker::PhantomData;
use core::ptr;
use core::time::Duration;
use uefi_raw::protocol::ata::{
AtaCommandBlock, AtaPassThruCommandPacket, AtaPassThruLength, AtaStatusBlock,
};
pub mod pass_thru;
pub use uefi_raw::protocol::ata::AtaPassThruCommandProtocol;
#[derive(Debug)]
pub struct AtaRequest<'a> {
io_align: u32,
acb: AtaCommandBlock,
packet: AtaPassThruCommandPacket,
in_data_buffer: Option<AlignedBuffer>,
out_data_buffer: Option<AlignedBuffer>,
asb: AlignedBuffer,
_phantom: PhantomData<&'a u8>,
}
#[derive(Debug)]
pub struct AtaRequestBuilder<'a> {
req: AtaRequest<'a>,
}
impl<'a> AtaRequestBuilder<'a> {
fn new(
io_align: u32,
command: u8,
protocol: AtaPassThruCommandProtocol,
) -> Result<Self, LayoutError> {
let mut asb =
AlignedBuffer::from_size_align(size_of::<AtaStatusBlock>(), usize_from_u32(io_align))?;
Ok(Self {
req: AtaRequest {
io_align,
acb: AtaCommandBlock {
command,
..Default::default()
},
packet: AtaPassThruCommandPacket {
asb: asb.ptr_mut().cast(),
acb: ptr::null(), timeout: 0,
in_data_buffer: ptr::null_mut(),
out_data_buffer: ptr::null(),
in_transfer_length: 0,
out_transfer_length: 0,
protocol,
length: AtaPassThruLength::BYTES,
},
in_data_buffer: None,
out_data_buffer: None,
asb,
_phantom: PhantomData,
},
})
}
pub fn read_pio(io_align: u32, command: u8) -> Result<Self, LayoutError> {
Self::new(io_align, command, AtaPassThruCommandProtocol::PIO_DATA_IN)
}
pub fn read_udma(io_align: u32, command: u8) -> Result<Self, LayoutError> {
Self::new(io_align, command, AtaPassThruCommandProtocol::UDMA_DATA_IN)
}
pub fn write_udma(io_align: u32, command: u8) -> Result<Self, LayoutError> {
Self::new(io_align, command, AtaPassThruCommandProtocol::UDMA_DATA_OUT)
}
#[must_use]
pub const fn with_timeout(mut self, timeout: Duration) -> Self {
self.req.packet.timeout = (timeout.as_nanos() / 100) as u64;
self
}
#[must_use]
pub const fn with_features(mut self, features: u8) -> Self {
self.req.acb.features = features;
self
}
#[must_use]
pub const fn with_sector_number(mut self, sector_number: u8) -> Self {
self.req.acb.sector_number = sector_number;
self
}
#[must_use]
pub const fn with_cylinder(mut self, low: u8, high: u8) -> Self {
self.req.acb.cylinder_low = low;
self.req.acb.cylinder_high = high;
self
}
#[must_use]
pub const fn with_device_head(mut self, device_head: u8) -> Self {
self.req.acb.device_head = device_head;
self
}
#[must_use]
pub const fn with_sector_number_exp(mut self, sector_number_exp: u8) -> Self {
self.req.acb.sector_number_exp = sector_number_exp;
self
}
#[must_use]
pub const fn with_cylinder_exp(mut self, low_exp: u8, high_exp: u8) -> Self {
self.req.acb.cylinder_low_exp = low_exp;
self.req.acb.cylinder_high_exp = high_exp;
self
}
#[must_use]
pub const fn with_features_exp(mut self, features_exp: u8) -> Self {
self.req.acb.features_exp = features_exp;
self
}
#[must_use]
pub const fn with_sector_count(mut self, sector_count: u8) -> Self {
self.req.acb.sector_count = sector_count;
self
}
#[must_use]
pub const fn with_sector_count_exp(mut self, sector_count_exp: u8) -> Self {
self.req.acb.sector_count_exp = sector_count_exp;
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 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)
}
#[must_use]
pub fn build(self) -> AtaRequest<'a> {
self.req
}
}
#[derive(Debug)]
pub struct AtaResponse<'a> {
req: AtaRequest<'a>,
}
impl<'a> AtaResponse<'a> {
#[must_use]
pub const fn status(&self) -> &'a AtaStatusBlock {
unsafe {
self.req
.asb
.ptr()
.cast::<AtaStatusBlock>()
.as_ref()
.unwrap()
}
}
#[must_use]
pub const fn read_buffer(&self) -> Option<&'a [u8]> {
if self.req.packet.in_data_buffer.is_null() {
return None;
}
unsafe {
Some(core::slice::from_raw_parts(
self.req.packet.in_data_buffer.cast(),
self.req.packet.in_transfer_length as usize,
))
}
}
}