use std::{fmt, ptr};
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))
}
}
#[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})"),
}
}
}
impl fmt::Debug for ScsiCommandRequestFlags {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut parts: Vec<String> = Vec::new();
if self.contains(ScsiCommandRequestFlags::FINAL) {
parts.push("FINAL".into());
}
if self.contains(ScsiCommandRequestFlags::READ) {
parts.push("READ".into());
}
if self.contains(ScsiCommandRequestFlags::WRITE) {
parts.push("WRITE".into());
}
let raw_attr = self.bits() & ScsiCommandRequestFlags::ATTR_MASK.bits();
let attr = TaskAttribute::from(raw_attr);
parts.push(format!("{attr:?}"));
write!(f, "ScsiCommandRequestFlags({})", parts.join("|"))
}
}
bitflags::bitflags! {
#[derive(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> {
ScsiCommandResponseFlags::from_bits(value)
.ok_or_else(|| anyhow::anyhow!("invalid ScsiCommandFlags: {:#08b}", value))
}
}
impl fmt::Debug for ScsiCommandResponseFlags {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut parts: Vec<String> = Vec::new();
if self.contains(ScsiCommandResponseFlags::FINAL) {
parts.push("FINAL".into());
}
if self.contains(ScsiCommandResponseFlags::O_SMALL) {
parts.push("O_SMALL".into());
}
if self.contains(ScsiCommandResponseFlags::U_SMALL) {
parts.push("U_SMALL".into());
}
if self.contains(ScsiCommandResponseFlags::O_BIG) {
parts.push("O_BIG".into());
}
if self.contains(ScsiCommandResponseFlags::U_BIG) {
parts.push("U_BIG".into());
}
write!(f, "ScsiCommandResponseFlags({})", parts.join("|"))
}
}
#[repr(u8)]
#[derive(Clone, Copy, 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)),
}
}
}
impl fmt::Debug for ResponseCode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
ResponseCode::CommandCompleted => write!(f, "CommandCompleted(0x00)"),
ResponseCode::TargetFailure => write!(f, "TargetFailure(0x01)"),
ResponseCode::VendorSpecific(v) => write!(f, "VendorSpecific(0x{v:02x})"),
ResponseCode::Reserved(r) => write!(f, "Reserved(0x{r:02x})"),
}
}
}
#[repr(u8)]
#[derive(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)
}
}
impl fmt::Debug for ScsiStatus {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
ScsiStatus::Good => write!(f, "Good(0x00)"),
ScsiStatus::CheckCondition => write!(f, "CheckCondition(0x02)"),
ScsiStatus::Busy => write!(f, "Busy(0x08)"),
ScsiStatus::ReservationConflict => write!(f, "ReservationConflict(0x18)"),
ScsiStatus::TaskSetFull => write!(f, "TaskSetFull(0x28)"),
ScsiStatus::AcaActive => write!(f, "AcaActive(0x30)"),
ScsiStatus::TaskAborted => write!(f, "TaskAborted(0x40)"),
ScsiStatus::Other(v) => write!(f, "Other(0x{v:02x})"),
}
}
}