use core::fmt;
use std::convert::TryFrom;
use thiserror::Error;
use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
const OPCODE_MASK: u8 = 0b0011_1111;
const I_MASK: u8 = 0b0100_0000;
#[repr(u8)]
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub enum Opcode {
#[default]
NopOut = 0x00,
ScsiCommandReq = 0x01,
ScsiTaskMgmtReq = 0x02,
LoginReq = 0x03,
TextReq = 0x04,
ScsiDataOut = 0x05,
LogoutReq = 0x06,
NopIn = 0x20,
ScsiCommandResp = 0x21,
ScsiTaskMgmtResp = 0x22,
LoginResp = 0x23,
TextResp = 0x24,
ScsiDataIn = 0x25,
LogoutResp = 0x26,
ReadyToTransfer = 0x31,
Reject = 0x3F,
}
impl Opcode {
#[inline]
pub fn from_u6(v: u8) -> Option<Self> {
Some(match v {
0x00 => Self::NopOut,
0x01 => Self::ScsiCommandReq,
0x02 => Self::ScsiTaskMgmtReq,
0x03 => Self::LoginReq,
0x04 => Self::TextReq,
0x05 => Self::ScsiDataOut,
0x06 => Self::LogoutReq,
0x20 => Self::NopIn,
0x21 => Self::ScsiCommandResp,
0x22 => Self::ScsiTaskMgmtResp,
0x23 => Self::LoginResp,
0x24 => Self::TextResp,
0x25 => Self::ScsiDataIn,
0x26 => Self::LogoutResp,
0x31 => Self::ReadyToTransfer,
0x3F => Self::Reject,
_ => return None,
})
}
}
#[derive(Debug, Error)]
#[error("invalid opcode: 0x{0:02x}")]
pub struct UnknownOpcode(pub u8);
#[derive(Debug, PartialEq, Eq, Default)]
#[repr(C)]
pub struct BhsOpcode {
pub flags: bool,
pub opcode: Opcode,
}
impl TryFrom<u8> for BhsOpcode {
type Error = anyhow::Error;
fn try_from(byte: u8) -> Result<Self, Self::Error> {
let flags = (byte & I_MASK) != 0;
let code = byte & OPCODE_MASK;
let opcode = Opcode::from_u6(code).ok_or(UnknownOpcode(code))?;
Ok(Self { flags, opcode })
}
}
impl From<&BhsOpcode> for u8 {
fn from(b: &BhsOpcode) -> u8 {
let mut raw = b.opcode.clone() as u8;
if b.flags {
raw |= I_MASK;
}
raw
}
}
#[repr(transparent)]
#[derive(Clone, Default, PartialEq, Eq, FromBytes, IntoBytes, KnownLayout, Immutable)]
pub struct RawBhsOpcode(u8);
impl RawBhsOpcode {
#[inline]
pub const fn raw(&self) -> u8 {
self.0
}
#[inline]
pub const fn from_raw(v: u8) -> Self {
Self(v)
}
#[inline]
pub const fn i(&self) -> bool {
(self.0 & I_MASK) != 0
}
#[inline]
pub fn set_i(&mut self) {
self.0 |= I_MASK
}
#[inline]
pub const fn opcode_raw(&self) -> u8 {
self.0 & OPCODE_MASK
}
#[inline]
pub fn set_opcode_raw(&mut self, v: u8) {
self.0 = (self.0 & !OPCODE_MASK) | (v & OPCODE_MASK)
}
#[inline]
pub fn opcode_known(&self) -> Option<Opcode> {
Opcode::from_u6(self.opcode_raw())
}
#[inline]
pub fn set_opcode_known(&mut self, k: Opcode) {
self.set_opcode_raw(k as u8);
}
}
impl fmt::Debug for RawBhsOpcode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match BhsOpcode::try_from(self.0) {
Ok(bhs) => {
let mut tmp = f.debug_struct("RawBhsOpcode");
if bhs.flags {
tmp.field("I", &bhs.flags);
}
tmp.field("opcode", &bhs.opcode).finish()
},
Err(_) => {
let mut tmp = f.debug_struct("RawBhsOpcode");
if self.i() {
tmp.field("I", &self.i());
}
tmp.field("opcode_raw", &format_args!("0x{:02X}", self.opcode_raw()))
.finish()
},
}
}
}