iscsi_client_rs/models/command/
request.rs1use anyhow::{Result, anyhow, bail};
9use zerocopy::{
10 BigEndian, FromBytes as ZFromBytes, Immutable, IntoBytes, KnownLayout, U32,
11};
12
13use crate::{
14 client::pdu_connection::FromBytes,
15 models::{
16 command::{common::TaskAttribute, zero_copy::RawScsiCmdReqFlags},
17 common::{
18 BasicHeaderSegment, HEADER_LEN, InitiatorTaskTag, LogicalUnitNumber,
19 SendingData,
20 },
21 data_fromat::ZeroCopyType,
22 opcode::{BhsOpcode, Opcode, RawBhsOpcode},
23 },
24};
25
26#[repr(C)]
32#[derive(Debug, Default, PartialEq, ZFromBytes, IntoBytes, KnownLayout, Immutable)]
33pub struct ScsiCommandRequest {
34 pub opcode: RawBhsOpcode,
36 pub flags: RawScsiCmdReqFlags,
38 reserved1: [u8; 2],
40 pub total_ahs_length: u8,
42 pub data_segment_length: [u8; 3],
44 pub lun: LogicalUnitNumber,
46 pub initiator_task_tag: InitiatorTaskTag,
48 pub expected_data_transfer_length: U32<BigEndian>,
50 pub cmd_sn: U32<BigEndian>,
52 pub exp_stat_sn: U32<BigEndian>,
54 pub scsi_descriptor_block: [u8; 16],
56}
57
58impl ScsiCommandRequest {
59 pub const DEFAULT_TAG: u32 = 0xffffffff_u32;
61
62 pub fn to_bhs_bytes(&self, buf: &mut [u8]) -> Result<()> {
64 buf.fill(0);
65 if buf.len() != HEADER_LEN {
66 bail!("buffer length must be {HEADER_LEN}, got {}", buf.len());
67 }
68 buf.copy_from_slice(self.as_bytes());
69 Ok(())
70 }
71
72 pub fn from_bhs_bytes(buf: &mut [u8]) -> Result<&mut Self> {
74 let hdr = <Self as zerocopy::FromBytes>::mut_from_bytes(buf)
75 .map_err(|e| anyhow!("failed convert buffer ScsiCommandRequest: {e}"))?;
76 if hdr.opcode.opcode_known() != Some(Opcode::ScsiCommandReq) {
77 anyhow::bail!(
78 "ScsiCommandRequest: invalid opcode 0x{:02x}",
79 hdr.opcode.opcode_raw()
80 );
81 }
82 Ok(hdr)
83 }
84}
85
86#[derive(Debug, Default, PartialEq)]
91pub struct ScsiCommandRequestBuilder {
92 pub header: ScsiCommandRequest,
94 enable_header_digest: bool,
96 enable_data_digest: bool,
98}
99
100impl ScsiCommandRequestBuilder {
101 pub fn new() -> Self {
103 ScsiCommandRequestBuilder {
104 header: ScsiCommandRequest {
105 opcode: {
106 let mut tmp = RawBhsOpcode::default();
107 tmp.set_opcode_known(Opcode::ScsiCommandReq);
108 tmp
109 },
110 ..Default::default()
111 },
112 enable_data_digest: false,
113 enable_header_digest: false,
114 }
115 }
116
117 pub fn immediate(mut self) -> Self {
119 self.header.opcode.set_i();
120 self
121 }
122
123 pub fn read(mut self) -> Self {
125 self.header.flags.set_read(true);
126 self
127 }
128
129 pub fn write(mut self) -> Self {
131 self.header.flags.set_write(true);
132 self
133 }
134
135 pub fn task_attribute(mut self, task: TaskAttribute) -> Self {
137 self.header.flags.set_task_attr(task);
138 self
139 }
140
141 pub fn with_header_digest(mut self) -> Self {
143 self.enable_header_digest = true;
144 self
145 }
146
147 pub fn with_data_digest(mut self) -> Self {
149 self.enable_data_digest = true;
150 self
151 }
152
153 pub fn initiator_task_tag(mut self, tag: u32) -> Self {
155 self.header.initiator_task_tag.set(tag);
156 self
157 }
158
159 pub fn expected_data_transfer_length(mut self, expected_data_length: u32) -> Self {
161 self.header
162 .expected_data_transfer_length
163 .set(expected_data_length);
164 self
165 }
166
167 pub fn cmd_sn(mut self, sn: u32) -> Self {
169 self.header.cmd_sn.set(sn);
170 self
171 }
172
173 pub fn exp_stat_sn(mut self, sn: u32) -> Self {
175 self.header.exp_stat_sn.set(sn);
176 self
177 }
178
179 pub fn lun(mut self, lun: u64) -> Self {
181 self.header.lun.set(lun);
182 self
183 }
184
185 pub fn scsi_descriptor_block(mut self, scsi_descriptor_block: &[u8; 16]) -> Self {
187 self.header
188 .scsi_descriptor_block
189 .clone_from_slice(scsi_descriptor_block);
190 self
191 }
192}
193
194impl SendingData for ScsiCommandRequest {
195 fn get_final_bit(&self) -> bool {
196 self.flags.fin()
197 }
198
199 fn set_final_bit(&mut self) {
200 self.flags.set_fin(true);
201 }
202
203 fn get_continue_bit(&self) -> bool {
204 !self.flags.fin()
205 }
206
207 fn set_continue_bit(&mut self) {
208 self.flags.set_fin(false);
209 }
210}
211
212impl FromBytes for ScsiCommandRequest {
213 fn from_bhs_bytes(bytes: &mut [u8]) -> Result<&mut Self> {
214 ScsiCommandRequest::from_bhs_bytes(bytes)
215 }
216}
217
218impl BasicHeaderSegment for ScsiCommandRequest {
219 #[inline]
220 fn to_bhs_bytes(&self, buf: &mut [u8]) -> Result<()> {
221 self.to_bhs_bytes(buf)
222 }
223
224 #[inline]
225 fn get_opcode(&self) -> Result<BhsOpcode> {
226 BhsOpcode::try_from(self.opcode.raw())
227 }
228
229 fn get_initiator_task_tag(&self) -> u32 {
230 self.initiator_task_tag.get()
231 }
232
233 #[inline]
234 fn get_ahs_length_bytes(&self) -> usize {
235 (self.total_ahs_length as usize) * 4
236 }
237
238 #[inline]
239 fn set_ahs_length_bytes(&mut self, len: u8) {
240 self.total_ahs_length = len >> 2;
241 }
242
243 #[inline]
244 fn get_data_length_bytes(&self) -> usize {
245 u32::from_be_bytes([
246 0,
247 self.data_segment_length[0],
248 self.data_segment_length[1],
249 self.data_segment_length[2],
250 ]) as usize
251 }
252
253 #[inline]
254 fn set_data_length_bytes(&mut self, len: u32) {
255 let be = len.to_be_bytes();
256 self.data_segment_length = [be[1], be[2], be[3]];
257 }
258}
259
260impl ZeroCopyType for ScsiCommandRequest {}