iscsi_client_rs/models/command/
common.rs1use std::{fmt, ptr};
8
9use anyhow::bail;
10use thiserror::Error;
11
12bitflags::bitflags! {
13 #[derive(Default, Clone, PartialEq)]
14 pub struct ScsiCommandRequestFlags: u8 {
16 const FINAL = 0x80;
17 const READ = 0x40;
18 const WRITE = 0x20;
19 const ATTR_MASK = 0b0000_0111;
21 }
22}
23
24impl TryFrom<u8> for ScsiCommandRequestFlags {
25 type Error = anyhow::Error;
26
27 fn try_from(value: u8) -> Result<Self, Self::Error> {
28 ScsiCommandRequestFlags::from_bits(value)
29 .ok_or_else(|| anyhow::anyhow!("invalid ScsiCommandFlags: {:#08b}", value))
30 }
31}
32
33impl fmt::Debug for ScsiCommandRequestFlags {
34 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35 use ScsiCommandRequestFlags as F;
36
37 write!(f, "ScsiCommandRequestFlags(")?;
38
39 let mut sep = "";
40 if self.contains(F::FINAL) {
41 write!(f, "FINAL")?;
42 sep = "|";
43 }
44 if self.contains(F::READ) {
45 write!(f, "{sep}READ")?;
46 sep = "|";
47 }
48 if self.contains(F::WRITE) {
49 write!(f, "{sep}WRITE")?;
50 sep = "|";
51 }
52
53 let attr_bits = self.bits() & F::ATTR_MASK.bits();
54 let attr = TaskAttribute::from(attr_bits);
55 write!(f, "{sep}ATTR={attr:?}({attr_bits:#04x})")?;
56
57 write!(f, ")")
58 }
59}
60
61#[derive(Clone, Copy, PartialEq)]
67pub enum TaskAttribute {
68 Untagged,
70 Simple,
72 Ordered,
74 HeadOfQueue,
76 ACA,
78 Reserved(u8),
80}
81
82impl From<u8> for TaskAttribute {
83 fn from(value: u8) -> Self {
84 match value {
85 0 => TaskAttribute::Untagged,
86 1 => TaskAttribute::Simple,
87 2 => TaskAttribute::Ordered,
88 3 => TaskAttribute::HeadOfQueue,
89 4 => TaskAttribute::ACA,
90 r @ 5..=7 => TaskAttribute::Reserved(r),
91 other => TaskAttribute::Reserved(other),
92 }
93 }
94}
95
96impl From<TaskAttribute> for u8 {
97 fn from(value: TaskAttribute) -> Self {
98 match value {
99 TaskAttribute::Untagged => 0,
100 TaskAttribute::Simple => 1,
101 TaskAttribute::Ordered => 2,
102 TaskAttribute::HeadOfQueue => 3,
103 TaskAttribute::ACA => 4,
104 TaskAttribute::Reserved(v) => v & ScsiCommandRequestFlags::ATTR_MASK.bits(),
105 }
106 }
107}
108
109impl fmt::Debug for TaskAttribute {
110 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
111 match *self {
112 TaskAttribute::Untagged => write!(f, "Untagged"),
113 TaskAttribute::Simple => write!(f, "Simple"),
114 TaskAttribute::Ordered => write!(f, "Ordered"),
115 TaskAttribute::HeadOfQueue => write!(f, "HeadOfQueue"),
116 TaskAttribute::ACA => write!(f, "ACA"),
117 TaskAttribute::Reserved(val) => write!(f, "Reserved({val})"),
118 }
119 }
120}
121
122bitflags::bitflags! {
123 #[derive(Debug, Default, Clone, PartialEq)]
124 pub struct ScsiCommandResponseFlags: u8 {
126 const FINAL = 0b1000_0000;
127 const O_SMALL = 0b0001_0000;
129 const U_SMALL = 0b0000_1000;
131 const O_BIG = 0b0000_0100;
133 const U_BIG = 0b0000_0010;
135 }
136}
137
138impl TryFrom<u8> for ScsiCommandResponseFlags {
139 type Error = anyhow::Error;
140
141 fn try_from(value: u8) -> Result<Self, Self::Error> {
142 let tmp = ScsiCommandResponseFlags::from_bits(value)
143 .ok_or_else(|| anyhow::anyhow!("invalid ScsiCommandFlags: {:#08b}", value))?;
144
145 if (tmp.contains(ScsiCommandResponseFlags::U_BIG)
146 && tmp.contains(ScsiCommandResponseFlags::O_BIG))
147 || (tmp.contains(ScsiCommandResponseFlags::U_SMALL)
148 && tmp.contains(ScsiCommandResponseFlags::O_SMALL))
149 {
150 bail!("Protocol error cause U && O both presented")
151 }
152
153 Ok(tmp)
154 }
155}
156
157#[repr(u8)]
159#[derive(Debug, Clone, PartialEq, Eq)]
160pub enum ResponseCode {
161 CommandCompleted = 0x00,
163 TargetFailure = 0x01,
165 VendorSpecific(u8),
167 Reserved(u8),
169}
170
171#[derive(Debug, Error)]
176#[error("invalid response code: 0x{0:02x}")]
177pub struct UnknownResponseCode(pub u8);
178
179impl From<&ResponseCode> for u8 {
180 fn from(value: &ResponseCode) -> Self {
181 unsafe { ptr::read_unaligned(value as *const ResponseCode as *const u8) }
182 }
183}
184
185impl TryFrom<u8> for ResponseCode {
186 type Error = UnknownResponseCode;
187
188 fn try_from(b: u8) -> Result<Self, Self::Error> {
189 match b {
190 0x00 => Ok(ResponseCode::CommandCompleted),
191 0x01 => Ok(ResponseCode::TargetFailure),
192 0x80..=0xFF => Ok(ResponseCode::VendorSpecific(b)),
193 r @ 0x02..=0x7F => Ok(ResponseCode::Reserved(r)),
194 }
195 }
196}
197
198#[repr(u8)]
204#[derive(Debug, Clone, Default, PartialEq, Eq)]
205pub enum ScsiStatus {
206 #[default]
208 Good = 0x00,
209 CheckCondition = 0x02,
211 Busy = 0x08,
213 ReservationConflict = 0x18,
215 TaskSetFull = 0x28,
217 AcaActive = 0x30,
219 TaskAborted = 0x40,
221 Other(u8),
223}
224
225#[derive(Debug, Error)]
230#[error("invalid SCSI status: 0x{0:02x}")]
231pub struct UnknownScsiStatus(pub u8);
232
233impl From<&ScsiStatus> for u8 {
234 fn from(value: &ScsiStatus) -> Self {
235 unsafe { ptr::read_unaligned(value as *const ScsiStatus as *const u8) }
236 }
237}
238
239impl TryFrom<u8> for ScsiStatus {
240 type Error = UnknownScsiStatus;
241
242 fn try_from(b: u8) -> Result<Self, Self::Error> {
243 let s = match b {
244 0x00 => ScsiStatus::Good,
245 0x02 => ScsiStatus::CheckCondition,
246 0x08 => ScsiStatus::Busy,
247 0x18 => ScsiStatus::ReservationConflict,
248 0x28 => ScsiStatus::TaskSetFull,
249 0x30 => ScsiStatus::AcaActive,
250 0x40 => ScsiStatus::TaskAborted,
251 other => ScsiStatus::Other(other),
252 };
253 Ok(s)
254 }
255}