tape: sgutils2.rs - add do_out_command()
Make it possible to run commands that writes data.
This commit is contained in:
		@ -16,6 +16,12 @@ impl Drop for SgPtBase  {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub const SCSI_PT_RESULT_GOOD:c_int = 0;
 | 
			
		||||
pub const SCSI_PT_RESULT_STATUS:c_int = 1;
 | 
			
		||||
pub const SCSI_PT_RESULT_SENSE:c_int = 2;
 | 
			
		||||
pub const SCSI_PT_RESULT_TRANSPORT_ERR:c_int = 3;
 | 
			
		||||
pub const SCSI_PT_RESULT_OS_ERR:c_int = 4;
 | 
			
		||||
 | 
			
		||||
#[link(name = "sgutils2")]
 | 
			
		||||
extern {
 | 
			
		||||
 | 
			
		||||
@ -39,6 +45,12 @@ extern {
 | 
			
		||||
        dxfer_ilen: c_int,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    pub fn set_scsi_pt_data_out(
 | 
			
		||||
        objp: *mut SgPtBase,
 | 
			
		||||
        dxferp: *const u8,
 | 
			
		||||
        dxfer_olen: c_int,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    pub fn set_scsi_pt_cdb(
 | 
			
		||||
        objp: *mut SgPtBase,
 | 
			
		||||
        cdb: *const u8,
 | 
			
		||||
@ -63,6 +75,8 @@ extern {
 | 
			
		||||
    pub fn get_scsi_pt_sense_len(objp: *const SgPtBase) -> c_int;
 | 
			
		||||
 | 
			
		||||
    pub fn get_scsi_pt_status_response(objp: *const SgPtBase) -> c_int;
 | 
			
		||||
 | 
			
		||||
    pub fn get_scsi_pt_result_category(objp: *const SgPtBase) -> c_int;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Creates a Box<SgPtBase>
 | 
			
		||||
@ -87,6 +101,19 @@ pub struct SgRaw<'a, F> {
 | 
			
		||||
    sense_buffer: [u8; 32],
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// alloc page aligned buffer
 | 
			
		||||
pub fn alloc_page_aligned_buffer(buffer_size: usize) -> Result<Box<[u8]> , Error> {
 | 
			
		||||
    let page_size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) } as usize;
 | 
			
		||||
    let layout = std::alloc::Layout::from_size_align(buffer_size, page_size)?;
 | 
			
		||||
    let dinp = unsafe { std::alloc::alloc_zeroed(layout) };
 | 
			
		||||
    if dinp.is_null() {
 | 
			
		||||
        bail!("alloc SCSI output buffer failed");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let buffer_slice = unsafe { std::slice::from_raw_parts_mut(dinp, buffer_size)};
 | 
			
		||||
    Ok(unsafe { Box::from_raw(buffer_slice) })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl <'a, F: AsRawFd> SgRaw<'a, F> {
 | 
			
		||||
 | 
			
		||||
    /// Create a new instance to run commands
 | 
			
		||||
@ -94,15 +121,13 @@ impl <'a, F: AsRawFd> SgRaw<'a, F> {
 | 
			
		||||
    /// The file must be a handle to a SCSI device.
 | 
			
		||||
    pub fn new(file: &'a mut F, buffer_size: usize) -> Result<Self, Error> {
 | 
			
		||||
 | 
			
		||||
        let page_size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) } as usize;
 | 
			
		||||
        let layout = std::alloc::Layout::from_size_align(buffer_size, page_size)?;
 | 
			
		||||
        let dinp = unsafe { std::alloc::alloc(layout) };
 | 
			
		||||
        if dinp.is_null() {
 | 
			
		||||
            bail!("alloc SCSI output buffer failed");
 | 
			
		||||
        }
 | 
			
		||||
        let buffer;
 | 
			
		||||
 | 
			
		||||
        let buffer = unsafe { std::slice::from_raw_parts_mut(dinp, buffer_size)};
 | 
			
		||||
        let buffer = unsafe { Box::from_raw(buffer) };
 | 
			
		||||
        if buffer_size > 0 {
 | 
			
		||||
            buffer = alloc_page_aligned_buffer(buffer_size)?;
 | 
			
		||||
        } else {
 | 
			
		||||
            buffer =  Box::new([]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let sense_buffer = [0u8; 32];
 | 
			
		||||
 | 
			
		||||
@ -114,13 +139,15 @@ impl <'a, F: AsRawFd> SgRaw<'a, F> {
 | 
			
		||||
 | 
			
		||||
        let mut ptvp = boxed_scsi_pt_obj()?;
 | 
			
		||||
 | 
			
		||||
        unsafe {
 | 
			
		||||
            set_scsi_pt_data_in(
 | 
			
		||||
                &mut *ptvp,
 | 
			
		||||
                self.buffer.as_ptr(),
 | 
			
		||||
                self.buffer.len() as c_int,
 | 
			
		||||
            )
 | 
			
		||||
        };
 | 
			
		||||
        if self.buffer.len() > 0 {
 | 
			
		||||
            unsafe {
 | 
			
		||||
                set_scsi_pt_data_in(
 | 
			
		||||
                    &mut *ptvp,
 | 
			
		||||
                    self.buffer.as_ptr(),
 | 
			
		||||
                    self.buffer.len() as c_int,
 | 
			
		||||
                )
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        unsafe {
 | 
			
		||||
            set_scsi_pt_sense(
 | 
			
		||||
@ -140,6 +167,10 @@ impl <'a, F: AsRawFd> SgRaw<'a, F> {
 | 
			
		||||
            bail!("no valid SCSI command");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if self.buffer.len() < 16 {
 | 
			
		||||
            bail!("output buffer too small");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let mut ptvp = self.create_boxed_scsi_pt_obj()?;
 | 
			
		||||
 | 
			
		||||
        unsafe {
 | 
			
		||||
@ -176,4 +207,55 @@ impl <'a, F: AsRawFd> SgRaw<'a, F> {
 | 
			
		||||
 | 
			
		||||
        Ok(&self.buffer[..data_len])
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Run dataout command
 | 
			
		||||
    ///
 | 
			
		||||
    /// Note: use alloc_page_aligned_buffer to alloc data transfer buffer
 | 
			
		||||
    pub fn do_out_command(&mut self, cmd: &[u8], data: &[u8]) -> Result<(), Error> {
 | 
			
		||||
 | 
			
		||||
        if !unsafe { sg_is_scsi_cdb(cmd.as_ptr(), cmd.len() as c_int) } {
 | 
			
		||||
            bail!("no valid SCSI command");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let page_size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) } as usize;
 | 
			
		||||
        if ((data.as_ptr() as usize) & (page_size -1)) != 0 {
 | 
			
		||||
            bail!("wrong transfer buffer alignment");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let mut ptvp = self.create_boxed_scsi_pt_obj()?;
 | 
			
		||||
 | 
			
		||||
        unsafe {
 | 
			
		||||
            set_scsi_pt_data_out(
 | 
			
		||||
                &mut *ptvp,
 | 
			
		||||
                data.as_ptr(),
 | 
			
		||||
                data.len() as c_int,
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            set_scsi_pt_cdb(
 | 
			
		||||
                &mut *ptvp,
 | 
			
		||||
                cmd.as_ptr(),
 | 
			
		||||
                cmd.len() as c_int,
 | 
			
		||||
            );
 | 
			
		||||
         };
 | 
			
		||||
 | 
			
		||||
        let res = unsafe { do_scsi_pt(&mut *ptvp, self.file.as_raw_fd(), 0, 0) };
 | 
			
		||||
        if res < 0 {
 | 
			
		||||
            let err = nix::Error::last();
 | 
			
		||||
            bail!("do_scsi_pt failed  - {}", err);
 | 
			
		||||
        }
 | 
			
		||||
        if res != 0 {
 | 
			
		||||
            bail!("do_scsi_pt failed {}", res);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // todo: what about sense data?
 | 
			
		||||
        let _sense_len = unsafe { get_scsi_pt_sense_len(&mut *ptvp) };
 | 
			
		||||
 | 
			
		||||
        let status = unsafe { get_scsi_pt_status_response(&mut *ptvp) };
 | 
			
		||||
        if status != 0 {
 | 
			
		||||
            // toto: improve error reporting
 | 
			
		||||
            bail!("unknown scsi error - status response {}", status);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user