iscsi_client_rs/models/command/
response.rs1use anyhow::{Result, bail};
8use tracing::warn;
9use zerocopy::{
10 BigEndian, FromBytes as ZFromBytes, Immutable, IntoBytes, KnownLayout, U32,
11};
12
13use crate::{
14 client::pdu_connection::FromBytes,
15 models::{
16 command::zero_copy::{RawResponseCode, RawScsiCmdRespFlags, RawScsiStatus},
17 common::{BasicHeaderSegment, HEADER_LEN, InitiatorTaskTag, SendingData},
18 data_fromat::ZeroCopyType,
19 opcode::{BhsOpcode, Opcode, RawBhsOpcode},
20 },
21};
22
23#[repr(C)]
30#[derive(Debug, PartialEq, ZFromBytes, IntoBytes, KnownLayout, Immutable)]
31pub struct ScsiCommandResponse {
32 pub opcode: RawBhsOpcode,
34 pub flags: RawScsiCmdRespFlags,
37 pub response: RawResponseCode,
39 pub status: RawScsiStatus,
41 pub total_ahs_length: u8,
43 pub data_segment_length: [u8; 3],
46 reserved: [u8; 8],
48 pub initiator_task_tag: InitiatorTaskTag,
50 pub snack_tag: U32<BigEndian>,
52 pub stat_sn: U32<BigEndian>,
54 pub exp_cmd_sn: U32<BigEndian>,
56 pub max_cmd_sn: U32<BigEndian>,
58 pub exp_data_sn: U32<BigEndian>,
60 pub bidirectional_read_residual_count: U32<BigEndian>,
63 pub residual_count: U32<BigEndian>,
66}
67
68impl ScsiCommandResponse {
69 pub fn to_bhs_bytes(&self, buf: &mut [u8]) -> Result<()> {
71 buf.fill(0);
72 if buf.len() != HEADER_LEN {
73 bail!("buffer length must be {HEADER_LEN}, got {}", buf.len());
74 }
75 buf.copy_from_slice(self.as_bytes());
76 Ok(())
77 }
78
79 pub fn from_bhs_bytes(buf: &mut [u8]) -> Result<&mut Self> {
81 let hdr = <Self as zerocopy::FromBytes>::mut_from_bytes(buf).map_err(|e| {
82 anyhow::anyhow!("failed convert buffer ScsiCommandResponse: {e}")
83 })?;
84 if hdr.opcode.opcode_known() != Some(Opcode::ScsiCommandResp) {
85 anyhow::bail!(
86 "ScsiCommandResponse: invalid opcode 0x{:02x}",
87 hdr.opcode.opcode_raw()
88 );
89 }
90 Ok(hdr)
91 }
92
93 #[inline]
95 pub fn residual_valid(&self) -> bool {
96 self.flags.u_big() || self.flags.o_big()
97 }
98
99 #[inline]
101 pub fn residual_effective(&self) -> u32 {
102 if self.residual_valid() {
103 self.residual_count.get()
104 } else {
105 0
106 }
107 }
108
109 #[inline]
111 pub fn bidi_read_residual_valid(&self) -> bool {
112 self.flags.u_small() || self.flags.o_small()
113 }
114
115 #[inline]
117 pub fn bidi_read_residual_effective(&self) -> u32 {
118 if self.bidi_read_residual_valid() {
119 self.bidirectional_read_residual_count.get()
120 } else {
121 0
122 }
123 }
124}
125
126impl SendingData for ScsiCommandResponse {
127 fn get_final_bit(&self) -> bool {
128 self.flags.fin()
129 }
130
131 fn set_final_bit(&mut self) {
132 warn!("ScsiCommand Response must contain Final");
133 self.flags.set_fin(true);
134 }
135
136 fn get_continue_bit(&self) -> bool {
137 false
138 }
139
140 fn set_continue_bit(&mut self) {
141 warn!("ScsiCommand Response don`t support Continue");
142 }
143}
144
145impl FromBytes for ScsiCommandResponse {
146 fn from_bhs_bytes(bytes: &mut [u8]) -> Result<&mut Self> {
147 ScsiCommandResponse::from_bhs_bytes(bytes)
148 }
149}
150
151impl BasicHeaderSegment for ScsiCommandResponse {
152 #[inline]
153 fn to_bhs_bytes(&self, buf: &mut [u8]) -> Result<()> {
154 self.to_bhs_bytes(buf)
155 }
156
157 #[inline]
158 fn get_opcode(&self) -> Result<BhsOpcode> {
159 BhsOpcode::try_from(self.opcode.raw())
160 }
161
162 #[inline]
163 fn get_initiator_task_tag(&self) -> u32 {
164 self.initiator_task_tag.get()
165 }
166
167 #[inline]
168 fn get_ahs_length_bytes(&self) -> usize {
169 (self.total_ahs_length as usize) * 4
170 }
171
172 #[inline]
173 fn set_ahs_length_bytes(&mut self, len: u8) {
174 self.total_ahs_length = len >> 2;
175 }
176
177 #[inline]
178 fn get_data_length_bytes(&self) -> usize {
179 u32::from_be_bytes([
180 0,
181 self.data_segment_length[0],
182 self.data_segment_length[1],
183 self.data_segment_length[2],
184 ]) as usize
185 }
186
187 #[inline]
188 fn set_data_length_bytes(&mut self, len: u32) {
189 let be = len.to_be_bytes();
190 self.data_segment_length = [be[1], be[2], be[3]];
191 }
192}
193
194impl ZeroCopyType for ScsiCommandResponse {}