use std::time::Duration;
use crate::command::ata::{raw::RawSatCommand, AtaProtocol, FromDevice, SatResult, ScsiSat};
const OPCODE_IDENTIFY_DEVICE: u8 = 0xEC;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum SecurityState {
SEC1,
SEC2,
SEC4,
SEC5,
SEC6,
Unknown,
}
#[derive(Clone, Debug)]
pub struct IdentifySatCommand<'a> {
raw: RawSatCommand<'a, FromDevice>,
}
impl<'a> IdentifySatCommand<'a> {
fn new(sat: &'a ScsiSat<'a>) -> Self {
let mut raw = sat.raw_read();
raw.command(AtaProtocol::PioDataIn, OPCODE_IDENTIFY_DEVICE)
.features(0)
.lba_16_23(0)
.lba_8_15(0)
.lba_0_7(0)
.count(512);
Self { raw }
}
pub fn device(&mut self, device: u8) -> &mut Self {
self.raw.device(device);
self
}
pub fn issue_12(&mut self) -> SatResult<IdentifySatResponse> {
Ok(self.raw.issue_12()?.map(IdentifySatResponse::new))
}
pub fn issue_16(&mut self) -> SatResult<IdentifySatResponse> {
Ok(self.raw.issue_16()?.map(IdentifySatResponse::new))
}
}
pub struct IdentifySatResponse {
bfr: Vec<u8>,
}
macro_rules! define_bitflag_getter {
($name:ident, $word_idx:literal, $bit_idx:literal) => {
pub fn $name(&self) -> bool {
self.word($word_idx) & (1 << $bit_idx) != 0
}
};
}
impl IdentifySatResponse {
pub fn new(bfr: Vec<u8>) -> Self {
Self { bfr }
}
pub fn raw(&self) -> &Vec<u8> {
&self.bfr
}
fn word(&self, index: usize) -> u16 {
let offset = index * 2;
u16::from_le_bytes([self.bfr[offset], self.bfr[offset + 1]])
}
fn ata_string(&self, start_word: usize, end_word: usize) -> String {
let mut bytes = Vec::with_capacity((end_word - start_word) * 2);
for w in start_word..end_word {
let word = self.word(w);
bytes.push((word >> 8) as u8);
bytes.push((word & 0xFF) as u8);
}
String::from_utf8_lossy(&bytes).trim().to_string()
}
pub fn firmware_rev(&self) -> String {
self.ata_string(23, 27)
}
pub fn model_nr(&self) -> String {
self.ata_string(27, 47)
}
define_bitflag_getter!(cap_lba28, 49, 9);
define_bitflag_getter!(cap_cmd_sanitize_antiifreeze_lock_ext, 59, 10);
define_bitflag_getter!(cap_vers_sanitize_acs3, 59, 11);
define_bitflag_getter!(cap_feature_set_sanitize, 59, 12);
define_bitflag_getter!(cap_cmd_crypto_scramble_ext, 59, 13);
define_bitflag_getter!(cap_cmd_overwrite_ext, 59, 14);
define_bitflag_getter!(cap_cmd_block_erase_ext, 59, 15);
define_bitflag_getter!(cap_feature_set_smart, 82, 0);
define_bitflag_getter!(cap_feature_set_security, 82, 1);
define_bitflag_getter!(cap_volatile_write_cache, 82, 5);
define_bitflag_getter!(cap_read_look_ahead, 82, 6);
define_bitflag_getter!(cap_cmd_write_buffer, 82, 12);
define_bitflag_getter!(cap_cmd_read_buffer, 82, 13);
define_bitflag_getter!(cap_cmd_nop, 82, 14);
define_bitflag_getter!(cap_cmd_download_microcode, 83, 0);
define_bitflag_getter!(cap_feature_set_apm, 83, 3);
define_bitflag_getter!(cap_feature_set_puis, 83, 5);
define_bitflag_getter!(cap_set_features_required_for_spinup, 83, 6);
define_bitflag_getter!(cap_lba48, 83, 10);
define_bitflag_getter!(cap_cmd_flush_cache, 83, 12);
define_bitflag_getter!(cap_cmd_flush_cache_ext, 83, 13);
define_bitflag_getter!(cap_smart_error_logging, 84, 0);
define_bitflag_getter!(cap_smart_self_test, 84, 1);
define_bitflag_getter!(cap_feature_set_streaming, 84, 4);
define_bitflag_getter!(cap_feature_set_gpl, 84, 5);
define_bitflag_getter!(cap_cmd_write_dma_fua_ext, 84, 6);
define_bitflag_getter!(cap_cmd_idle_immediate_with_unload, 84, 13);
define_bitflag_getter!(enabled_feature_set_smart, 85, 0);
define_bitflag_getter!(enabled_feature_set_security, 85, 1);
define_bitflag_getter!(enabled_volatile_write_cache, 85, 5);
define_bitflag_getter!(enabled_read_look_ahead, 85, 6);
define_bitflag_getter!(enabled_cmd_write_buffer, 85, 12);
define_bitflag_getter!(enabled_cmd_read_buffer, 85, 13);
define_bitflag_getter!(enabled_cmd_nop, 85, 14);
define_bitflag_getter!(enabled_cmd_download_microcode, 86, 0);
define_bitflag_getter!(enabled_feature_set_apm, 86, 3);
define_bitflag_getter!(enabled_feature_set_puis, 86, 5);
define_bitflag_getter!(enabled_set_features_required_for_spinup, 86, 6);
define_bitflag_getter!(enabled_lba48, 86, 10);
define_bitflag_getter!(enabled_cmd_flush_cache, 86, 12);
define_bitflag_getter!(enabled_cmd_flush_cache_ext, 86, 13);
define_bitflag_getter!(enabled_smart_error_logging, 87, 0);
define_bitflag_getter!(enabled_smart_self_test, 87, 1);
define_bitflag_getter!(enabled_feature_set_streaming, 87, 4);
define_bitflag_getter!(enabled_feature_set_gpl, 87, 5);
define_bitflag_getter!(enabled_cmd_write_dma_fua_ext, 87, 6);
define_bitflag_getter!(enabled_cmd_idle_immediate_with_unload, 87, 13);
define_bitflag_getter!(cap_feature_set_write_read_verify, 119, 1);
define_bitflag_getter!(cap_cmd_write_uncorrectable_ext, 119, 2);
define_bitflag_getter!(cap_cmd_read_log_dma_ext, 119, 3);
define_bitflag_getter!(cap_feature_set_free_fall, 119, 5);
define_bitflag_getter!(cap_feature_set_sense_data_reporting, 119, 6);
define_bitflag_getter!(cap_feature_set_epc, 119, 7);
define_bitflag_getter!(cap_feature_set_accessible_max_addr_config, 119, 8);
define_bitflag_getter!(cap_feature_set_dsn, 119, 9);
define_bitflag_getter!(enabled_feature_set_write_read_verify, 120, 1);
define_bitflag_getter!(enabled_cmd_write_uncorrectable_ext, 120, 2);
define_bitflag_getter!(enabled_cmd_read_log_dma_ext, 120, 3);
define_bitflag_getter!(enabled_feature_set_free_fall, 120, 5);
define_bitflag_getter!(enabled_feature_set_sense_data_reporting, 120, 6);
define_bitflag_getter!(enabled_feature_set_epc, 120, 7);
define_bitflag_getter!(enabled_feature_set_accessible_max_addr_config, 120, 8);
define_bitflag_getter!(enabled_feature_set_dsn, 120, 9);
define_bitflag_getter!(security_supported, 128, 0);
define_bitflag_getter!(security_enabled, 128, 1);
define_bitflag_getter!(security_locked, 128, 2);
define_bitflag_getter!(security_frozen, 128, 3);
define_bitflag_getter!(security_count_expired, 128, 4);
define_bitflag_getter!(security_enhanced_security_erase_supported, 128, 5);
define_bitflag_getter!(security_master_password_capability, 128, 8);
pub fn security_state(&self) -> SecurityState {
let a = self.security_supported();
let b = self.security_enabled();
let c = self.security_locked();
let d = self.security_frozen();
let e = self.security_count_expired();
let f = self.security_master_password_capability();
match (a, b, c, d, e, f) {
(true, false, false, false, _, false) => SecurityState::SEC1,
(true, false, false, true, _, _) => SecurityState::SEC2,
(true, true, true, false, _, _) => SecurityState::SEC4,
(true, true, false, false, _, _) => SecurityState::SEC5,
(true, true, false, true, _, _) => SecurityState::SEC6,
_ => SecurityState::Unknown,
}
}
pub fn time_required_for_normal_erase(&self) -> Duration {
let w89 = self.word(89);
match (w89 >> 15) & 1 {
1 => Duration::from_mins((w89 & 0x7FFF) as u64 * 2),
_ => Duration::from_mins((w89 & 0x00FF) as u64 * 2),
}
}
pub fn time_required_for_enhanced_erase(&self) -> Duration {
let w90 = self.word(90);
match (w90 >> 15) & 1 {
1 => Duration::from_mins((w90 & 0x7FFF) as u64 * 2),
_ => Duration::from_mins((w90 & 0x00FF) as u64 * 2),
}
}
pub fn capacity_lba(&self) -> u64 {
let mut buf = [0u8; 8];
if self.cap_lba48() {
buf.copy_from_slice(&self.bfr[100 * 2..100 * 2 + 8]);
u64::from_le_bytes(buf)
} else if self.cap_lba28() {
buf[..4].copy_from_slice(&self.bfr[60 * 2..60 * 2 + 4]);
u64::from_le_bytes(buf)
} else {
0
}
}
pub fn capacity_bytes(&self) -> u64 {
self.capacity_lba() * self.logical_sector_size()
}
pub fn logical_sector_size(&self) -> u64 {
let w106 = self.word(106);
if w106 & (1 << 15) == 0 {
return 512;
}
let exponent = (w106 & 0x0FFF) as u32;
1u64 << (exponent + 9)
}
pub fn physical_sector_size(&self) -> u64 {
let w106 = self.word(106);
let logical = self.logical_sector_size();
if w106 & (1 << 14) == 0 {
return logical;
}
let exponent = (w106 & 0x0FFF) as u32;
logical * (1u64 << exponent)
}
}
impl ScsiSat<'_> {
pub fn identify(&self) -> IdentifySatCommand<'_> {
IdentifySatCommand::new(self)
}
}