iscsi_client_rs/models/
opcode.rs1use core::fmt;
26use std::convert::TryFrom;
27
28use thiserror::Error;
29use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
30
31const OPCODE_MASK: u8 = 0b0011_1111;
33const I_MASK: u8 = 0b0100_0000;
35
36#[repr(u8)]
38#[derive(Debug, Default, Clone, PartialEq, Eq)]
39pub enum Opcode {
40 #[default]
41 NopOut = 0x00,
42 ScsiCommandReq = 0x01,
43 ScsiTaskMgmtReq = 0x02,
44 LoginReq = 0x03,
45 TextReq = 0x04,
46 ScsiDataOut = 0x05,
47 LogoutReq = 0x06,
48 NopIn = 0x20,
50 ScsiCommandResp = 0x21,
51 ScsiTaskMgmtResp = 0x22,
52 LoginResp = 0x23,
53 TextResp = 0x24,
54 ScsiDataIn = 0x25,
55 LogoutResp = 0x26,
56 ReadyToTransfer = 0x31,
57 Reject = 0x3F,
59}
60
61impl Opcode {
62 #[inline]
63 pub fn from_u6(v: u8) -> Option<Self> {
64 Some(match v {
65 0x00 => Self::NopOut,
66 0x01 => Self::ScsiCommandReq,
67 0x02 => Self::ScsiTaskMgmtReq,
68 0x03 => Self::LoginReq,
69 0x04 => Self::TextReq,
70 0x05 => Self::ScsiDataOut,
71 0x06 => Self::LogoutReq,
72 0x20 => Self::NopIn,
73 0x21 => Self::ScsiCommandResp,
74 0x22 => Self::ScsiTaskMgmtResp,
75 0x23 => Self::LoginResp,
76 0x24 => Self::TextResp,
77 0x25 => Self::ScsiDataIn,
78 0x26 => Self::LogoutResp,
79 0x31 => Self::ReadyToTransfer,
80 0x3F => Self::Reject,
81 _ => return None,
82 })
83 }
84}
85
86#[derive(Debug, Error)]
88#[error("invalid opcode: 0x{0:02x}")]
89pub struct UnknownOpcode(pub u8);
90
91#[derive(Debug, PartialEq, Eq, Default)]
96#[repr(C)]
97pub struct BhsOpcode {
98 pub flags: bool,
99 pub opcode: Opcode,
100}
101
102impl TryFrom<u8> for BhsOpcode {
103 type Error = anyhow::Error;
104
105 fn try_from(byte: u8) -> Result<Self, Self::Error> {
106 let flags = (byte & I_MASK) != 0;
107 let code = byte & OPCODE_MASK;
108 let opcode = Opcode::from_u6(code).ok_or(UnknownOpcode(code))?;
109 Ok(Self { flags, opcode })
110 }
111}
112
113impl From<&BhsOpcode> for u8 {
114 fn from(b: &BhsOpcode) -> u8 {
115 let mut raw = b.opcode.clone() as u8;
116 if b.flags {
117 raw |= I_MASK;
118 }
119 raw
120 }
121}
122
123#[repr(transparent)]
126#[derive(Clone, Default, PartialEq, Eq, FromBytes, IntoBytes, KnownLayout, Immutable)]
127pub struct RawBhsOpcode(u8);
128
129impl RawBhsOpcode {
130 #[inline]
131 pub const fn raw(&self) -> u8 {
132 self.0
133 }
134
135 #[inline]
136 pub const fn from_raw(v: u8) -> Self {
137 Self(v)
138 }
139
140 #[inline]
142 pub const fn i(&self) -> bool {
143 (self.0 & I_MASK) != 0
144 }
145
146 #[inline]
147 pub fn set_i(&mut self) {
148 self.0 |= I_MASK
149 }
150
151 #[inline]
153 pub const fn opcode_raw(&self) -> u8 {
154 self.0 & OPCODE_MASK
155 }
156
157 #[inline]
158 pub fn set_opcode_raw(&mut self, v: u8) {
159 self.0 = (self.0 & !OPCODE_MASK) | (v & OPCODE_MASK)
160 }
161
162 #[inline]
163 pub fn opcode_known(&self) -> Option<Opcode> {
164 Opcode::from_u6(self.opcode_raw())
165 }
166
167 #[inline]
168 pub fn set_opcode_known(&mut self, k: Opcode) {
169 self.set_opcode_raw(k as u8);
170 }
171}
172
173impl fmt::Debug for RawBhsOpcode {
174 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
175 match BhsOpcode::try_from(self.0) {
176 Ok(bhs) => {
177 let mut tmp = f.debug_struct("RawBhsOpcode");
178 if bhs.flags {
179 tmp.field("I", &bhs.flags);
180 }
181 tmp.field("opcode", &bhs.opcode).finish()
182 },
183 Err(_) => {
184 let mut tmp = f.debug_struct("RawBhsOpcode");
185 if self.i() {
186 tmp.field("I", &self.i());
187 }
188 tmp.field("opcode_raw", &format_args!("0x{:02X}", self.opcode_raw()))
189 .finish()
190 },
191 }
192 }
193}