use anyhow::{Result, anyhow, bail};
use zerocopy::{
BigEndian, FromBytes as ZFromBytes, Immutable, IntoBytes, KnownLayout, U32, U64,
};
use crate::{
client::pdu_connection::FromBytes,
models::{
command::{common::TaskAttribute, zero_copy::RawScsiCmdReqFlags},
common::{BasicHeaderSegment, HEADER_LEN, SendingData},
data_fromat::ZeroCopyType,
opcode::{BhsOpcode, Opcode, RawBhsOpcode},
},
};
#[repr(C)]
#[derive(Debug, Default, PartialEq, ZFromBytes, IntoBytes, KnownLayout, Immutable)]
pub struct ScsiCommandRequest {
pub opcode: RawBhsOpcode, pub flags: RawScsiCmdReqFlags, reserved1: [u8; 2], pub total_ahs_length: u8, pub data_segment_length: [u8; 3], pub lun: U64<BigEndian>, pub initiator_task_tag: u32, pub expected_data_transfer_length: U32<BigEndian>, pub cmd_sn: U32<BigEndian>, pub exp_stat_sn: U32<BigEndian>, pub scsi_descriptor_block: [u8; 16], }
impl ScsiCommandRequest {
pub const DEFAULT_TAG: u32 = 0xffffffff_u32;
pub fn to_bhs_bytes(&self, buf: &mut [u8]) -> Result<()> {
buf.fill(0);
if buf.len() != HEADER_LEN {
bail!("buffer length must be {HEADER_LEN}, got {}", buf.len());
}
buf.copy_from_slice(self.as_bytes());
Ok(())
}
pub fn from_bhs_bytes(buf: &mut [u8]) -> Result<&mut Self> {
let hdr = <Self as zerocopy::FromBytes>::mut_from_bytes(buf)
.map_err(|e| anyhow!("failed convert buffer ScsiCommandRequest: {e}"))?;
if hdr.opcode.opcode_known() != Some(Opcode::ScsiCommandReq) {
anyhow::bail!(
"ScsiCommandRequest: invalid opcode 0x{:02x}",
hdr.opcode.opcode_raw()
);
}
Ok(hdr)
}
}
#[derive(Debug, Default, PartialEq)]
pub struct ScsiCommandRequestBuilder {
pub header: ScsiCommandRequest,
enable_header_digest: bool,
enable_data_digest: bool,
}
impl ScsiCommandRequestBuilder {
pub fn new() -> Self {
ScsiCommandRequestBuilder {
header: ScsiCommandRequest {
opcode: {
let mut tmp = RawBhsOpcode::default();
tmp.set_opcode_known(Opcode::ScsiCommandReq);
tmp
},
..Default::default()
},
enable_data_digest: false,
enable_header_digest: false,
}
}
pub fn immediate(mut self) -> Self {
self.header.opcode.set_i();
self
}
pub fn read(mut self) -> Self {
self.header.flags.set_read(true);
self
}
pub fn write(mut self) -> Self {
self.header.flags.set_write(true);
self
}
pub fn task_attribute(mut self, task: TaskAttribute) -> Self {
self.header.flags.set_task_attr(task);
self
}
pub fn with_header_digest(mut self) -> Self {
self.enable_header_digest = true;
self
}
pub fn with_data_digest(mut self) -> Self {
self.enable_data_digest = true;
self
}
pub fn initiator_task_tag(mut self, tag: u32) -> Self {
self.header.initiator_task_tag = tag;
self
}
pub fn expected_data_transfer_length(mut self, expected_data_length: u32) -> Self {
self.header
.expected_data_transfer_length
.set(expected_data_length);
self
}
pub fn cmd_sn(mut self, sn: u32) -> Self {
self.header.cmd_sn.set(sn);
self
}
pub fn exp_stat_sn(mut self, sn: u32) -> Self {
self.header.exp_stat_sn.set(sn);
self
}
pub fn lun(mut self, lun: u64) -> Self {
self.header.lun.set(lun);
self
}
pub fn scsi_descriptor_block(mut self, scsi_descriptor_block: &[u8; 16]) -> Self {
self.header
.scsi_descriptor_block
.clone_from_slice(scsi_descriptor_block);
self
}
}
impl SendingData for ScsiCommandRequest {
fn get_final_bit(&self) -> bool {
self.flags.fin()
}
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);
}
}
impl FromBytes for ScsiCommandRequest {
fn from_bhs_bytes(bytes: &mut [u8]) -> Result<&mut Self> {
ScsiCommandRequest::from_bhs_bytes(bytes)
}
}
impl BasicHeaderSegment for ScsiCommandRequest {
#[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())
}
fn get_initiator_task_tag(&self) -> u32 {
self.initiator_task_tag
}
#[inline]
fn get_ahs_length_bytes(&self) -> usize {
(self.total_ahs_length as usize) * 4
}
#[inline]
fn set_ahs_length_bytes(&mut self, len: u8) {
self.total_ahs_length = len >> 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 ScsiCommandRequest {}