use std::{fmt, ptr};
use anyhow::bail;
use thiserror::Error;
bitflags::bitflags! {
#[derive(Default, Clone, PartialEq)]
pub struct ScsiCommandRequestFlags: u8 {
const FINAL = 0x80;
const READ = 0x40;
const WRITE = 0x20;
const ATTR_MASK = 0b0000_0111;
}
}
impl TryFrom<u8> for ScsiCommandRequestFlags {
type Error = anyhow::Error;
fn try_from(value: u8) -> Result<Self, Self::Error> {
ScsiCommandRequestFlags::from_bits(value)
.ok_or_else(|| anyhow::anyhow!("invalid ScsiCommandFlags: {:#08b}", value))
}
}
impl fmt::Debug for ScsiCommandRequestFlags {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use ScsiCommandRequestFlags as F;
write!(f, "ScsiCommandRequestFlags(")?;
let mut sep = "";
if self.contains(F::FINAL) {
write!(f, "FINAL")?;
sep = "|";
}
if self.contains(F::READ) {
write!(f, "{sep}READ")?;
sep = "|";
}
if self.contains(F::WRITE) {
write!(f, "{sep}WRITE")?;
sep = "|";
}
let attr_bits = self.bits() & F::ATTR_MASK.bits();
let attr = TaskAttribute::from(attr_bits);
write!(f, "{sep}ATTR={attr:?}({attr_bits:#04x})")?;
write!(f, ")")
}
}
#[derive(Clone, Copy, PartialEq)]
pub enum TaskAttribute {
Untagged, Simple, Ordered, HeadOfQueue, ACA, Reserved(u8), }
impl From<u8> for TaskAttribute {
fn from(value: u8) -> Self {
match value {
0 => TaskAttribute::Untagged,
1 => TaskAttribute::Simple,
2 => TaskAttribute::Ordered,
3 => TaskAttribute::HeadOfQueue,
4 => TaskAttribute::ACA,
r @ 5..=7 => TaskAttribute::Reserved(r),
other => TaskAttribute::Reserved(other),
}
}
}
impl From<TaskAttribute> for u8 {
fn from(value: TaskAttribute) -> Self {
match value {
TaskAttribute::Untagged => 0,
TaskAttribute::Simple => 1,
TaskAttribute::Ordered => 2,
TaskAttribute::HeadOfQueue => 3,
TaskAttribute::ACA => 4,
TaskAttribute::Reserved(v) => v & ScsiCommandRequestFlags::ATTR_MASK.bits(),
}
}
}
impl fmt::Debug for TaskAttribute {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
TaskAttribute::Untagged => write!(f, "Untagged"),
TaskAttribute::Simple => write!(f, "Simple"),
TaskAttribute::Ordered => write!(f, "Ordered"),
TaskAttribute::HeadOfQueue => write!(f, "HeadOfQueue"),
TaskAttribute::ACA => write!(f, "ACA"),
TaskAttribute::Reserved(val) => write!(f, "Reserved({val})"),
}
}
}
bitflags::bitflags! {
#[derive(Debug, Default, Clone, PartialEq)]
pub struct ScsiCommandResponseFlags: u8 {
const FINAL = 0b1000_0000;
const O_SMALL = 0b0001_0000;
const U_SMALL = 0b0000_1000;
const O_BIG = 0b0000_0100;
const U_BIG = 0b0000_0010;
}
}
impl TryFrom<u8> for ScsiCommandResponseFlags {
type Error = anyhow::Error;
fn try_from(value: u8) -> Result<Self, Self::Error> {
let tmp = ScsiCommandResponseFlags::from_bits(value)
.ok_or_else(|| anyhow::anyhow!("invalid ScsiCommandFlags: {:#08b}", value))?;
if (tmp.contains(ScsiCommandResponseFlags::U_BIG)
&& tmp.contains(ScsiCommandResponseFlags::O_BIG))
|| (tmp.contains(ScsiCommandResponseFlags::U_SMALL)
&& tmp.contains(ScsiCommandResponseFlags::O_SMALL))
{
bail!("Protocol error cause U && O both presented")
}
Ok(tmp)
}
}
#[repr(u8)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ResponseCode {
CommandCompleted = 0x00,
TargetFailure = 0x01,
VendorSpecific(u8),
Reserved(u8),
}
#[derive(Debug, Error)]
#[error("invalid response code: 0x{0:02x}")]
pub struct UnknownResponseCode(pub u8);
impl From<&ResponseCode> for u8 {
fn from(value: &ResponseCode) -> Self {
unsafe { ptr::read_unaligned(value as *const ResponseCode as *const u8) }
}
}
impl TryFrom<u8> for ResponseCode {
type Error = UnknownResponseCode;
fn try_from(b: u8) -> Result<Self, Self::Error> {
match b {
0x00 => Ok(ResponseCode::CommandCompleted),
0x01 => Ok(ResponseCode::TargetFailure),
0x80..=0xFF => Ok(ResponseCode::VendorSpecific(b)),
r @ 0x02..=0x7F => Ok(ResponseCode::Reserved(r)),
}
}
}
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ScsiStatus {
Good = 0x00,
CheckCondition = 0x02,
Busy = 0x08,
ReservationConflict = 0x18,
TaskSetFull = 0x28,
AcaActive = 0x30,
TaskAborted = 0x40,
Other(u8),
}
#[derive(Debug, Error)]
#[error("invalid SCSI status: 0x{0:02x}")]
pub struct UnknownScsiStatus(pub u8);
impl From<&ScsiStatus> for u8 {
fn from(value: &ScsiStatus) -> Self {
unsafe { ptr::read_unaligned(value as *const ScsiStatus as *const u8) }
}
}
impl TryFrom<u8> for ScsiStatus {
type Error = UnknownScsiStatus;
fn try_from(b: u8) -> Result<Self, Self::Error> {
let s = match b {
0x00 => ScsiStatus::Good,
0x02 => ScsiStatus::CheckCondition,
0x08 => ScsiStatus::Busy,
0x18 => ScsiStatus::ReservationConflict,
0x28 => ScsiStatus::TaskSetFull,
0x30 => ScsiStatus::AcaActive,
0x40 => ScsiStatus::TaskAborted,
other => ScsiStatus::Other(other),
};
Ok(s)
}
}