pub mod data;
pub mod pages;
#[cfg(target_os = "linux")]
mod linux;
#[cfg(target_os = "freebsd")]
mod freebsd;
use std::io;
use ata;
use byteorder::{ReadBytesExt, BigEndian};
use self::data::sense;
use Direction;
use Device;
use utils::hexdump_8;
quick_error! {
#[derive(Debug)]
pub enum Error {
IO(err: io::Error) {
from()
display("IO error: {}", err)
description(err.description())
cause(err)
}
Sense(key: sense::key::SenseKey, asc: u8, ascq: u8) {
description("SCSI error")
display("SCSI error: {:?} ({})",
key,
sense::key::decode_asc(*asc, *ascq)
.map(|x| x.to_string())
.unwrap_or_else(|| format!("unknown additional sense code: {:02x} {:02x}", asc, ascq)))
}
Nonsense {}
}
}
quick_error! {
#[derive(Debug)]
pub enum ATAError {
SCSI(err: Error) {
from()
from(err: io::Error) -> (Error::IO(err))
display("{}", err)
}
NotSupported {}
NoRegisters {}
}
}
#[derive(Debug)]
pub struct SCSIDevice {
device: Device,
}
impl SCSIDevice {
pub fn new(device: Device) -> Self {
Self { device }
}
pub fn do_cmd(&self, cmd: &[u8], dir: Direction, sense_len: usize, data_len: usize) -> Result<(Vec<u8>, Vec<u8>), io::Error> {
info!("SCSI cmd: dir={:?} cmd={:?}", dir, cmd);
let ret = Self::do_platform_cmd(self, cmd, dir, sense_len, data_len);
match ret {
Ok((ref sense, ref data)) => {
debug!("SCSI autosense: {}", hexdump_8(sense));
debug!("SCSI data: {}", hexdump_8(data));
},
ref err => {
debug!("SCSI err: {:?}", err);
}
}
ret
}
}
pub trait SCSICommon {
fn do_cmd(&self, cmd: &[u8], dir: Direction, sense_len: usize, data_len: usize) -> Result<(Vec<u8>, Vec<u8>), io::Error>;
fn scsi_inquiry(&self, vital: bool, code: u8) -> Result<(Vec<u8>, Vec<u8>), Error> {
info!("issuing INQUIRY: code={:?} vital={:?}", code, vital);
const alloc: usize = 4096;
let cmd: [u8; 6] = [
0x12,
if vital {1} else {0},
code,
(alloc >> 8) as u8,
(alloc & 0xff) as u8,
0,
];
Ok(self.do_cmd(&cmd, Direction::From, 32, alloc)?)
}
fn read_capacity_10(&self, lba: Option<u32>) -> Result<(Vec<u8>, u32, u32), Error> {
info!("issuing READ CAPACITY(10): lba={:?}", lba);
let (pmi, lba) = match lba {
Some(lba) => (true, lba),
None => (false, 0),
};
let cmd: [u8; 10] = [
0x25,
0,
((lba >> 24) & 0xff) as u8,
((lba >> 16) & 0xff) as u8,
((lba >> 8) & 0xff) as u8,
((lba) & 0xff) as u8,
0,
0,
if pmi { 1 } else { 0 },
0,
];
let (sense, data) = self.do_cmd(&cmd, Direction::From, 32, 8)?;
Ok((
sense,
(&data[0..4]).read_u32::<BigEndian>().unwrap(),
(&data[4..8]).read_u32::<BigEndian>().unwrap(),
))
}
fn log_sense(&self, changed: bool, save_params: bool, default: bool, threshold: bool, page: u8, subpage: u8, param_ptr: u16) -> Result<(Vec<u8>, Vec<u8>), Error> {
info!("issuing LOG SENSE: page={page:?} subpage={subpage:?} param_ptr={param_ptr:?} changed={changed:?} save_params={save_params:?} default={default:?} threshold={threshold:?}",
changed = changed,
save_params = save_params,
default = default,
threshold = threshold,
page = page,
subpage = subpage,
param_ptr = param_ptr,
);
const alloc: usize = 4096;
let pc = match (default, threshold) {
(false, true) => 0b00,
(false, false) => 0b01,
(true, true) => 0b10,
(true, false) => 0b11,
};
let cmd: [u8; 10] = [
0x4d,
if changed {0b10} else {0} + if save_params {0b1} else {0},
(pc << 6) + page,
subpage,
0,
(param_ptr >> 8) as u8,
(param_ptr & 0xff) as u8,
(alloc >> 8) as u8,
(alloc & 0xff) as u8,
0,
];
Ok(self.do_cmd(&cmd, Direction::From, 32, alloc)?)
}
fn ata_pass_through_16(&self, dir: Direction, regs: &ata::RegistersWrite) -> Result<(ata::RegistersRead, Vec<u8>), ATAError> {
info!("issuing ATA PASS-THROUGH (16): dir={:?} regs={:?}", dir, regs);
let extend = 0;
let protocol = match dir {
Direction::None => 3,
Direction::From => 4,
Direction::To => unimplemented!(),
_ => unimplemented!(),
};
let multiple_count = 0;
let ata_cmd: [u8; 16] = [
0x85,
(multiple_count << 5) + (protocol << 1) + extend,
0b0010_1101,
0, regs.features,
0, regs.sector_count,
0, regs.sector,
0, regs.cyl_low,
0, regs.cyl_high,
regs.device,
regs.command,
0,
];
let (sense, data) = self.do_cmd(&ata_cmd, Direction::From, 32, 512)?;
let descriptors = match sense::parse(&sense) {
Some((true, sense::Sense::Descriptor(sense::DescriptorData {
descriptors,
key: 0x01, asc: 0x00, ascq: 0x1D,
..
}))) => {
descriptors
},
Some((true, sense::Sense::Descriptor(sense::DescriptorData {
descriptors,
key: 0x00, asc: 0x00, ascq: 0x00,
..
}))) => {
descriptors
},
Some((true, sense::Sense::Fixed(sense::FixedData::Valid {
key: 0x05, asc: 0x20, ascq: 0x00, ..
}))) => {
return Err(ATAError::NotSupported);
},
Some((true, sense::Sense::Fixed(sense::FixedData::Valid {
key, asc, ascq, ..
})))
| Some((true, sense::Sense::Descriptor(sense::DescriptorData {
key, asc, ascq, ..
})))
=> {
return Err(Error::Sense(sense::key::SenseKey::from(key), asc, ascq))?;
},
Some((true, sense::Sense::Fixed(sense::FixedData::Invalid(_)))) => {
return Err(Error::Nonsense)?;
},
Some((false, _)) | None => {
return Err(ATAError::NoRegisters);
},
};
for desc in descriptors {
if desc.code != 0x09 { continue; }
if desc.data.len() != 12 { continue; }
let d = desc.data;
return Ok((ata::RegistersRead {
error: d[1],
sector_count: d[3],
sector: d[5],
cyl_low: d[7],
cyl_high: d[9],
device: d[10],
status: d[11],
}, data))
}
return Err(ATAError::NoRegisters);
}
}
impl SCSICommon for SCSIDevice {
fn do_cmd(&self, cmd: &[u8], dir: Direction, sense_len: usize, data_len: usize) -> Result<(Vec<u8>, Vec<u8>), io::Error> {
Self::do_cmd(self, cmd, dir, sense_len, data_len)
}
}