1 //! Bindings for libsgutils2
3 //! Incomplete, but we currently do not need more.
5 //! See: `/usr/include/scsi/sg_pt.h`
7 use std
::os
::unix
::io
::AsRawFd
;
9 use anyhow
::{bail, Error}
;
10 use libc
::{c_char, c_int}
;
12 // Opaque wrapper for sg_pt_base
14 struct SgPtBase { _private: [u8; 0] }
16 impl Drop
for SgPtBase
{
18 unsafe { destruct_scsi_pt_obj(self as *mut SgPtBase) }
;
22 pub const SCSI_PT_RESULT_GOOD
:c_int
= 0;
23 pub const SCSI_PT_RESULT_STATUS
:c_int
= 1;
24 pub const SCSI_PT_RESULT_SENSE
:c_int
= 2;
25 pub const SCSI_PT_RESULT_TRANSPORT_ERR
:c_int
= 3;
26 pub const SCSI_PT_RESULT_OS_ERR
:c_int
= 4;
28 #[link(name = "sgutils2")]
32 fn scsi_pt_open_device(
33 device_name
: * const c_char
,
43 fn construct_scsi_pt_obj() -> *mut SgPtBase
;
44 fn destruct_scsi_pt_obj(objp
: *mut SgPtBase
);
46 fn set_scsi_pt_data_in(
52 fn set_scsi_pt_data_out(
77 fn get_scsi_pt_resid(objp
: *const SgPtBase
) -> c_int
;
79 fn get_scsi_pt_sense_len(objp
: *const SgPtBase
) -> c_int
;
81 fn get_scsi_pt_status_response(objp
: *const SgPtBase
) -> c_int
;
84 fn get_scsi_pt_result_category(objp
: *const SgPtBase
) -> c_int
;
87 /// Creates a `Box<SgPtBase>`
89 /// Which get automatically dropped, so you do not need to call
90 /// destruct_scsi_pt_obj yourself.
91 fn boxed_scsi_pt_obj() -> Result
<Box
<SgPtBase
>, Error
> {
93 construct_scsi_pt_obj()
96 bail
!("construct_scsi_pt_ob failed");
99 Ok(unsafe { std::mem::transmute(objp)}
)
102 /// Safe interface to run RAW SCSI commands
103 pub struct SgRaw
<'a
, F
> {
106 sense_buffer
: [u8; 32],
109 /// Allocate a page aligned buffer
111 /// SG RAWIO commands needs page aligned transfer buffers.
112 pub fn alloc_page_aligned_buffer(buffer_size
: usize) -> Result
<Box
<[u8]> , Error
> {
113 let page_size
= unsafe { libc::sysconf(libc::_SC_PAGESIZE) }
as usize;
114 let layout
= std
::alloc
::Layout
::from_size_align(buffer_size
, page_size
)?
;
115 let dinp
= unsafe { std::alloc::alloc_zeroed(layout) }
;
117 bail
!("alloc SCSI output buffer failed");
120 let buffer_slice
= unsafe { std::slice::from_raw_parts_mut(dinp, buffer_size)}
;
121 Ok(unsafe { Box::from_raw(buffer_slice) }
)
124 impl <'a
, F
: AsRawFd
> SgRaw
<'a
, F
> {
126 /// Create a new instance to run commands
128 /// The file must be a handle to a SCSI device.
129 pub fn new(file
: &'a
mut F
, buffer_size
: usize) -> Result
<Self, Error
> {
134 buffer
= alloc_page_aligned_buffer(buffer_size
)?
;
136 buffer
= Box
::new([]);
139 let sense_buffer
= [0u8; 32];
141 Ok(Self { file, buffer, sense_buffer }
)
144 // create new object with initialized data_in and sense buffer
145 fn create_boxed_scsi_pt_obj(&mut self) -> Result
<Box
<SgPtBase
>, Error
> {
147 let mut ptvp
= boxed_scsi_pt_obj()?
;
149 if self.buffer
.len() > 0 {
153 self.buffer
.as_ptr(),
154 self.buffer
.len() as c_int
,
162 self.sense_buffer
.as_ptr(),
163 self.sense_buffer
.len() as c_int
,
170 /// Run the specified RAW SCSI command
171 pub fn do_command(&mut self, cmd
: &[u8]) -> Result
<&[u8], Error
> {
173 if !unsafe { sg_is_scsi_cdb(cmd.as_ptr(), cmd.len() as c_int) }
{
174 bail
!("no valid SCSI command");
177 if self.buffer
.len() < 16 {
178 bail
!("output buffer too small");
181 let mut ptvp
= self.create_boxed_scsi_pt_obj()?
;
191 let res
= unsafe { do_scsi_pt(&mut *ptvp, self.file.as_raw_fd(), 0, 0) }
;
193 let err
= nix
::Error
::last();
194 bail
!("do_scsi_pt failed - {}", err
);
197 bail
!("do_scsi_pt failed {}", res
);
200 // todo: what about sense data?
201 let _sense_len
= unsafe { get_scsi_pt_sense_len(&*ptvp) }
;
203 let status
= unsafe { get_scsi_pt_status_response(&*ptvp) }
;
205 // toto: improve error reporting
206 bail
!("unknown scsi error - status response {}", status
);
209 let data_len
= self.buffer
.len() -
210 (unsafe { get_scsi_pt_resid(&*ptvp) }
as usize);
212 bail
!("do_scsi_pt failed - no data received");
215 Ok(&self.buffer
[..data_len
])
218 /// Run dataout command
220 /// Note: use alloc_page_aligned_buffer to alloc data transfer buffer
221 pub fn do_out_command(&mut self, cmd
: &[u8], data
: &[u8]) -> Result
<(), Error
> {
223 if !unsafe { sg_is_scsi_cdb(cmd.as_ptr(), cmd.len() as c_int) }
{
224 bail
!("no valid SCSI command");
227 let page_size
= unsafe { libc::sysconf(libc::_SC_PAGESIZE) }
as usize;
228 if ((data
.as_ptr() as usize) & (page_size
-1)) != 0 {
229 bail
!("wrong transfer buffer alignment");
232 let mut ptvp
= self.create_boxed_scsi_pt_obj()?
;
235 set_scsi_pt_data_out(
248 let res
= unsafe { do_scsi_pt(&mut *ptvp, self.file.as_raw_fd(), 0, 0) }
;
250 let err
= nix
::Error
::last();
251 bail
!("do_scsi_pt failed - {}", err
);
254 bail
!("do_scsi_pt failed {}", res
);
257 // todo: what about sense data?
258 let _sense_len
= unsafe { get_scsi_pt_sense_len(&*ptvp) }
;
260 let status
= unsafe { get_scsi_pt_status_response(&*ptvp) }
;
262 // toto: improve error reporting
263 bail
!("unknown scsi error - status response {}", status
);