use anyhow::{Result, anyhow, bail};
use tracing::debug;
use zerocopy::{
BigEndian, FromBytes as ZFromBytes, Immutable, IntoBytes, KnownLayout, U32,
};
use crate::{
client::pdu_connection::FromBytes,
models::{
command::{common::ScsiStatus, zero_copy::RawScsiStatus},
common::{
BasicHeaderSegment, HEADER_LEN, InitiatorTaskTag, LogicalUnitNumber,
SendingData,
},
data::common::RawDataInFlags,
data_fromat::ZeroCopyType,
opcode::{BhsOpcode, Opcode, RawBhsOpcode},
},
};
#[repr(C)]
#[derive(Debug, Default, PartialEq, ZFromBytes, IntoBytes, KnownLayout, Immutable)]
pub struct ScsiDataIn {
pub opcode: RawBhsOpcode, pub flags: RawDataInFlags, pub reserved2: u8, pub status_or_rsvd: RawScsiStatus, pub total_ahs_length: u8, pub data_segment_length: [u8; 3], pub lun: LogicalUnitNumber,
pub initiator_task_tag: InitiatorTaskTag, pub target_transfer_tag: U32<BigEndian>, pub stat_sn_or_rsvd: U32<BigEndian>, pub exp_cmd_sn: U32<BigEndian>, pub max_cmd_sn: U32<BigEndian>, pub data_sn: U32<BigEndian>, pub buffer_offset: U32<BigEndian>, pub residual_count: U32<BigEndian>, }
impl ScsiDataIn {
#[inline]
pub fn scsi_status(&self) -> Option<ScsiStatus> {
if self.flags.s() {
self.status_or_rsvd.decode().ok()
} else {
None
}
}
#[inline]
pub fn residual_valid(&self) -> bool {
self.flags.u() || self.flags.o()
}
#[inline]
pub fn residual_effective(&self) -> u32 {
if self.residual_valid() {
self.residual_count.get()
} else {
0
}
}
#[inline]
pub fn set_scsi_status(&mut self, st: Option<ScsiStatus>) {
match st {
Some(s) => {
self.flags.set_s(true); self.flags.set_fin(true); self.status_or_rsvd.encode(s);
},
None => {
self.flags.set_s(false); self.status_or_rsvd.encode(ScsiStatus::Good);
self.stat_sn_or_rsvd.set(0);
self.residual_count.set(0);
},
}
}
#[inline]
pub fn to_bhs_bytes(&self, buf: &mut [u8]) -> Result<()> {
if buf.len() != HEADER_LEN {
bail!("buffer length must be {HEADER_LEN}, got {}", buf.len());
}
buf.copy_from_slice(self.as_bytes());
if !self.flags.s() {
buf[3] = 0; buf[24..28].fill(0); buf[44..48].fill(0); }
Ok(())
}
#[inline]
pub fn from_bhs_bytes(buf: &mut [u8]) -> Result<&mut Self> {
if buf.len() < HEADER_LEN {
return Err(anyhow!(
"buffer too small for SCSI Data-In BHS: {}",
buf.len()
));
}
let hdr = Self::mut_from_bytes(buf)
.map_err(|_| anyhow!("SCSI Data-In: zerocopy prefix error"))?;
if hdr.opcode.opcode_known() != Some(Opcode::ScsiDataIn) {
bail!(
"ScsiDataIn invalid opcode 0x{:02x}",
hdr.opcode.opcode_raw()
);
}
hdr.flags.validate()?;
Ok(hdr)
}
#[inline]
pub fn get_real_final_bit(&self) -> bool {
self.flags.fin()
}
#[inline]
pub fn get_status_bit(&self) -> bool {
self.flags.s()
}
}
impl SendingData for ScsiDataIn {
fn get_final_bit(&self) -> bool {
let f = self.flags.fin();
let s = self.flags.s();
let is_final = f && s;
debug!(
"DataIn get_final_bit (channel): F={} S={} status={:?} => {}",
f,
s,
self.scsi_status(),
is_final
);
is_final
}
fn set_final_bit(&mut self) {
self.flags.set_fin(true);
}
fn get_continue_bit(&self) -> bool {
!self.flags.fin()
}
fn set_continue_bit(&mut self) {
self.flags.set_fin(false);
self.flags.set_s(false);
}
}
impl FromBytes for ScsiDataIn {
#[inline]
fn from_bhs_bytes(bytes: &mut [u8]) -> Result<&mut Self> {
ScsiDataIn::from_bhs_bytes(bytes)
}
}
impl BasicHeaderSegment for ScsiDataIn {
#[inline]
fn to_bhs_bytes(&self, buf: &mut [u8]) -> Result<()> {
self.to_bhs_bytes(buf)
}
#[inline]
fn get_opcode(&self) -> Result<BhsOpcode> {
BhsOpcode::try_from(self.opcode.raw())
}
#[inline]
fn get_initiator_task_tag(&self) -> u32 {
self.initiator_task_tag.get()
}
#[inline]
fn get_ahs_length_bytes(&self) -> usize {
(self.total_ahs_length as usize) * 4
}
#[inline]
fn set_ahs_length_bytes(&mut self, len_bytes: u8) {
self.total_ahs_length = len_bytes >> 2;
}
#[inline]
fn get_data_length_bytes(&self) -> usize {
u32::from_be_bytes([
0,
self.data_segment_length[0],
self.data_segment_length[1],
self.data_segment_length[2],
]) as usize
}
#[inline]
fn set_data_length_bytes(&mut self, len: u32) {
let be = len.to_be_bytes();
self.data_segment_length = [be[1], be[2], be[3]];
}
}
impl ZeroCopyType for ScsiDataIn {}