use std::{fmt, io, time::Duration};
#[cfg(target_os = "macos")]
pub mod darwin;
#[cfg(target_os = "linux")]
pub mod linux;
pub mod usb;
#[cfg(target_os = "windows")]
pub mod windows;
#[cfg(target_os = "linux")]
const SENSE_REQUEST_LEN: usize = 96;
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("io error: {0}")]
Io(#[from] io::Error),
#[error("timed out after {0:?}")]
Timeout(Duration),
}
pub enum Data<'a> {
None,
In(&'a mut [u8]),
Out(&'a [u8]),
}
impl<'a> Data<'a> {
pub(crate) fn reborrow(&mut self) -> Data<'_> {
match self {
Data::None => Data::None,
Data::In(buf) => Data::In(buf),
Data::Out(buf) => Data::Out(buf),
}
}
}
impl fmt::Debug for Data<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Data::None => write!(f, "None"),
Data::In(b) => write!(f, "In({} bytes)", b.len()),
Data::Out(b) => write!(f, "Out({} bytes)", b.len()),
}
}
}
#[derive(Clone, PartialEq, Eq)]
pub struct Completion {
pub status: Status,
pub sense: Option<Sense>,
pub transferred: usize,
}
#[derive(Clone, PartialEq, Eq)]
pub struct Sense {
pub key: u8,
pub asc: u8,
pub ascq: u8,
pub tsc: Option<u8>,
pub ili: bool,
pub information: Option<u32>,
pub raw: Vec<u8>,
}
impl fmt::Debug for Sense {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:02X}h-{:02X}h-{:02X}h", self.key, self.asc, self.ascq)?;
if self.ili {
write!(f, " ILI")?;
if let Some(n) = self.information {
write!(f, "({n})")?;
}
}
match self.tsc {
Some(t) => write!(f, "-{t:02X}h")?,
None => write!(f, "-??")?,
}
write!(f, " raw={:02X?}", self.raw)
}
}
#[cfg(any(target_os = "linux", target_os = "windows", target_os = "macos"))]
fn sense_from_fixed(buffer: &[u8], tsc: Option<u8>) -> Sense {
Sense {
key: buffer[2] & 0xF,
ili: buffer[2] & 0x20 != 0,
information: (buffer[0] & 0x80 != 0)
.then(|| u32::from_be_bytes([buffer[3], buffer[4], buffer[5], buffer[6]])),
asc: buffer[12],
ascq: buffer[13],
tsc,
raw: buffer.to_vec(),
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Status {
Good,
CheckCondition,
Busy,
ReservationConflict,
Other(u8),
}
impl From<u8> for Status {
fn from(value: u8) -> Self {
match value {
0x00 => Self::Good,
0x02 => Self::CheckCondition,
0x08 => Self::Busy,
0x18 => Self::ReservationConflict,
x => Self::Other(x),
}
}
}
pub trait Transport: Send {
fn max_transfer(&self) -> usize;
fn execute(&mut self, cdb: &[u8], data: Data, timeout: Duration) -> Result<Completion, Error>;
}