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],
110 /// Allocate a page aligned buffer
112 /// SG RAWIO commands needs page aligned transfer buffers.
113 pub fn alloc_page_aligned_buffer(buffer_size
: usize) -> Result
<Box
<[u8]> , Error
> {
114 let page_size
= unsafe { libc::sysconf(libc::_SC_PAGESIZE) }
as usize;
115 let layout
= std
::alloc
::Layout
::from_size_align(buffer_size
, page_size
)?
;
116 let dinp
= unsafe { std::alloc::alloc_zeroed(layout) }
;
118 bail
!("alloc SCSI output buffer failed");
121 let buffer_slice
= unsafe { std::slice::from_raw_parts_mut(dinp, buffer_size)}
;
122 Ok(unsafe { Box::from_raw(buffer_slice) }
)
125 impl <'a
, F
: AsRawFd
> SgRaw
<'a
, F
> {
127 /// Create a new instance to run commands
129 /// The file must be a handle to a SCSI device.
130 pub fn new(file
: &'a
mut F
, buffer_size
: usize) -> Result
<Self, Error
> {
135 buffer
= alloc_page_aligned_buffer(buffer_size
)?
;
137 buffer
= Box
::new([]);
140 let sense_buffer
= [0u8; 32];
142 Ok(Self { file, buffer, sense_buffer, timeout: 0 }
)
145 /// Set the command timeout in seconds (0 means default (60 seconds))
146 pub fn set_timeout(&mut self, seconds
: usize) {
147 if seconds
> (i32::MAX
as usize) {
148 self.timeout
= i32::MAX
; // don't care about larger values
150 self.timeout
= seconds
as i32;
154 // create new object with initialized data_in and sense buffer
155 fn create_boxed_scsi_pt_obj(&mut self) -> Result
<Box
<SgPtBase
>, Error
> {
157 let mut ptvp
= boxed_scsi_pt_obj()?
;
159 if self.buffer
.len() > 0 {
163 self.buffer
.as_ptr(),
164 self.buffer
.len() as c_int
,
172 self.sense_buffer
.as_ptr(),
173 self.sense_buffer
.len() as c_int
,
180 /// Run the specified RAW SCSI command
181 pub fn do_command(&mut self, cmd
: &[u8]) -> Result
<&[u8], Error
> {
183 if !unsafe { sg_is_scsi_cdb(cmd.as_ptr(), cmd.len() as c_int) }
{
184 bail
!("no valid SCSI command");
187 if self.buffer
.len() < 16 {
188 bail
!("output buffer too small");
191 let mut ptvp
= self.create_boxed_scsi_pt_obj()?
;
201 let res
= unsafe { do_scsi_pt(&mut *ptvp, self.file.as_raw_fd(), self.timeout, 0) }
;
203 let err
= nix
::Error
::last();
204 bail
!("do_scsi_pt failed - {}", err
);
207 bail
!("do_scsi_pt failed {}", res
);
210 // todo: what about sense data?
211 let _sense_len
= unsafe { get_scsi_pt_sense_len(&*ptvp) }
;
213 let status
= unsafe { get_scsi_pt_status_response(&*ptvp) }
;
215 // toto: improve error reporting
216 bail
!("unknown scsi error - status response {}", status
);
219 let data_len
= self.buffer
.len() -
220 (unsafe { get_scsi_pt_resid(&*ptvp) }
as usize);
222 bail
!("do_scsi_pt failed - no data received");
225 Ok(&self.buffer
[..data_len
])
228 /// Run dataout command
230 /// Note: use alloc_page_aligned_buffer to alloc data transfer buffer
231 pub fn do_out_command(&mut self, cmd
: &[u8], data
: &[u8]) -> Result
<(), Error
> {
233 if !unsafe { sg_is_scsi_cdb(cmd.as_ptr(), cmd.len() as c_int) }
{
234 bail
!("no valid SCSI command");
237 let page_size
= unsafe { libc::sysconf(libc::_SC_PAGESIZE) }
as usize;
238 if ((data
.as_ptr() as usize) & (page_size
-1)) != 0 {
239 bail
!("wrong transfer buffer alignment");
242 let mut ptvp
= self.create_boxed_scsi_pt_obj()?
;
245 set_scsi_pt_data_out(
258 let res
= unsafe { do_scsi_pt(&mut *ptvp, self.file.as_raw_fd(), self.timeout, 0) }
;
260 let err
= nix
::Error
::last();
261 bail
!("do_scsi_pt failed - {}", err
);
264 bail
!("do_scsi_pt failed {}", res
);
267 // todo: what about sense data?
268 let _sense_len
= unsafe { get_scsi_pt_sense_len(&*ptvp) }
;
270 let status
= unsafe { get_scsi_pt_status_response(&*ptvp) }
;
272 // toto: improve error reporting
273 bail
!("unknown scsi error - status response {}", status
);