use nvme_cli_sys::{nvme_error_log_page, nvme_id_ctrl, nvme_smart_log};
use serde::Serialize;
use std::io;
use std::os::raw::c_char;
pub type Result<T> = io::Result<T>;
#[derive(Debug, Serialize, PartialEq)]
pub struct CtrlIdentity {
pub nvme_name: String,
pub vid: u16,
pub ssvid: u16,
pub serial_number: String,
pub model_number: String,
pub firmware_rev: String,
pub ieee_oui: [u8; 3],
pub cntlid: u16,
pub ver: u32,
pub subnqn: String,
pub fguid: [u8; 16],
pub cntrltype: u8,
}
impl CtrlIdentity {
pub fn new(nvme_name: String, raw: &nvme_id_ctrl) -> Self {
Self {
nvme_name,
vid: raw.vid,
ssvid: raw.ssvid,
serial_number: parse_ascii_field(&raw.sn),
model_number: parse_ascii_field(&raw.mn),
firmware_rev: parse_ascii_field(&raw.fr),
ieee_oui: raw.ieee,
cntlid: raw.cntlid,
ver: raw.ver,
subnqn: parse_ascii_field(&raw.subnqn),
fguid: convert_cchar_to_u8_array_16(&raw.fguid),
cntrltype: raw.cntrltype,
}
}
}
#[derive(Debug, Serialize, PartialEq)]
pub struct CtrlCapacity {
pub nvme_name: String,
pub total_nvm_bytes: u128,
pub unallocated_nvm_bytes: u128,
pub max_endurance_group_bytes: u128,
pub max_nvm_area: u32,
}
impl CtrlCapacity {
pub fn new(nvme_name: String, raw: &nvme_id_ctrl) -> Self {
Self {
nvme_name,
total_nvm_bytes: u128::from_le_bytes(raw.tnvmcap),
unallocated_nvm_bytes: u128::from_le_bytes(raw.unvmcap),
max_endurance_group_bytes: u128::from_le_bytes(raw.megcap),
max_nvm_area: raw.maxcna,
}
}
}
#[derive(Debug, Serialize, PartialEq)]
pub struct CtrlCapabilities {
pub nvme_name: String,
pub oacs: u16,
pub oncs: u16,
pub lpa: u8,
pub ctratt: u32,
pub oaes: u32,
pub sanicap: u32,
pub sgls: u32,
pub vwc: u8,
pub fna: u8,
pub anacap: u8,
pub anatt: u8,
pub anagrpmax: u32,
pub nanagrpid: u32,
pub fuses: u16,
pub ocfs: u16,
pub cmic: u8,
pub rpmbs: u32,
}
impl CtrlCapabilities {
pub fn new(nvme_name: String, raw: &nvme_id_ctrl) -> Self {
Self {
nvme_name,
oacs: raw.oacs,
oncs: raw.oncs,
lpa: raw.lpa,
ctratt: raw.ctratt,
oaes: raw.oaes,
sanicap: raw.sanicap,
sgls: raw.sgls,
vwc: raw.vwc,
fna: raw.fna,
anacap: raw.anacap,
anatt: raw.anatt,
anagrpmax: raw.anagrpmax,
nanagrpid: raw.nanagrpid,
fuses: raw.fuses,
ocfs: raw.ocfs,
cmic: raw.cmic,
rpmbs: raw.rpmbs,
}
}
}
#[derive(Debug, Serialize, PartialEq)]
pub struct CtrlLimits {
pub nvme_name: String,
pub mdts: u8,
pub sqes: u8,
pub cqes: u8,
pub maxcmd: u16,
pub nn: u32,
pub mnan: u32,
pub acl: u8,
pub aerl: u8,
pub awun: u16,
pub awupf: u16,
pub acwu: u16,
pub nsetidmax: u16,
pub endgidmax: u16,
}
impl CtrlLimits {
pub fn new(nvme_name: String, raw: &nvme_id_ctrl) -> Self {
Self {
nvme_name,
mdts: raw.mdts,
sqes: raw.sqes,
cqes: raw.cqes,
maxcmd: raw.maxcmd,
nn: raw.nn,
mnan: raw.mnan,
acl: raw.acl,
aerl: raw.aerl,
awun: raw.awun,
awupf: raw.awupf,
acwu: raw.acwu,
nsetidmax: raw.nsetidmax,
endgidmax: raw.endgidmax,
}
}
}
#[derive(Debug, Serialize, PartialEq)]
pub struct CtrlThermals {
pub nvme_name: String,
pub wctemp_k: u16,
pub cctemp_k: u16,
pub mntmt_k: u16,
pub mxtmt_k: u16,
pub hctma: u16,
}
impl CtrlThermals {
pub fn new(nvme_name: String, raw: &nvme_id_ctrl) -> Self {
Self {
nvme_name,
wctemp_k: raw.wctemp,
cctemp_k: raw.cctemp,
mntmt_k: raw.mntmt,
mxtmt_k: raw.mxtmt,
hctma: raw.hctma,
}
}
}
#[derive(Debug, Serialize, PartialEq)]
pub struct CtrlFirmware {
pub nvme_name: String,
pub frmw: u8,
pub fwug: u8,
pub mtfa: u16,
}
impl CtrlFirmware {
pub fn new(nvme_name: String, raw: &nvme_id_ctrl) -> Self {
Self {
nvme_name,
frmw: raw.frmw,
fwug: raw.fwug,
mtfa: raw.mtfa,
}
}
}
#[derive(Debug, Serialize, PartialEq)]
pub struct CtrlPowerStates {
pub nvme_name: String,
pub num_power_states: u8,
pub apsta: u8,
}
impl CtrlPowerStates {
pub fn new(nvme_name: String, raw: &nvme_id_ctrl) -> Self {
Self {
nvme_name,
num_power_states: raw.npss,
apsta: raw.apsta,
}
}
}
#[derive(Debug, Serialize, PartialEq)]
pub struct CtrlHostMemory {
pub nvme_name: String,
pub hmpre: u32,
pub hmmin: u32,
pub hmminds: u32,
pub hmmaxd: u16,
}
impl CtrlHostMemory {
pub fn new(nvme_name: String, raw: &nvme_id_ctrl) -> Self {
Self {
nvme_name,
hmpre: raw.hmpre,
hmmin: raw.hmmin,
hmminds: raw.hmminds,
hmmaxd: raw.hmmaxd,
}
}
}
#[derive(Debug, Serialize, PartialEq)]
pub struct CtrlArbitration {
pub nvme_name: String,
pub rab: u8,
}
impl CtrlArbitration {
pub fn new(nvme_name: String, raw: &nvme_id_ctrl) -> Self {
Self {
nvme_name,
rab: raw.rab,
}
}
}
#[derive(Debug, Serialize, PartialEq)]
pub struct CtrlDiagnostics {
pub nvme_name: String,
pub edstt: u16,
pub dsto: u8,
pub elpe: u8,
}
impl CtrlDiagnostics {
pub fn new(nvme_name: String, raw: &nvme_id_ctrl) -> Self {
Self {
nvme_name,
edstt: raw.edstt,
dsto: raw.dsto,
elpe: raw.elpe,
}
}
}
#[derive(Debug, Serialize, PartialEq)]
pub struct CtrlAdvanced {
pub nvme_name: String,
pub rtd3r_us: u32,
pub rtd3e_us: u32,
pub rrls: u16,
pub crdt1: u16,
pub crdt2: u16,
pub crdt3: u16,
pub nvmsr: u8,
pub vwci: u8,
pub mec: u8,
pub kas: u16,
pub pels: u32,
pub domainid: u16,
}
impl CtrlAdvanced {
pub fn new(nvme_name: String, raw: &nvme_id_ctrl) -> Self {
Self {
nvme_name,
rtd3r_us: raw.rtd3r,
rtd3e_us: raw.rtd3e,
rrls: raw.rrls,
crdt1: raw.crdt1,
crdt2: raw.crdt2,
crdt3: raw.crdt3,
nvmsr: raw.nvmsr,
vwci: raw.vwci,
mec: raw.mec,
kas: raw.kas,
pels: raw.pels,
domainid: raw.domainid,
}
}
}
#[derive(Debug, Serialize, PartialEq)]
pub struct CtrlCommandSets {
pub nvme_name: String,
pub avscc: u8,
pub icsvscc: u8,
pub nwpc: u8,
}
impl CtrlCommandSets {
pub fn new(nvme_name: String, raw: &nvme_id_ctrl) -> Self {
Self {
nvme_name,
avscc: raw.avscc,
icsvscc: raw.icsvscc,
nwpc: raw.nwpc,
}
}
}
#[derive(Debug, Serialize, PartialEq)]
pub struct CtrlFabric {
pub nvme_name: String,
pub ioccsz: u32,
pub iorcsz: u32,
pub icdoff: u16,
pub fcatt: u8,
pub msdbd: u8,
pub ofcs: u16,
}
impl CtrlFabric {
pub fn new(nvme_name: String, raw: &nvme_id_ctrl) -> Self {
Self {
nvme_name,
ioccsz: raw.ioccsz,
iorcsz: raw.iorcsz,
icdoff: raw.icdoff,
fcatt: raw.fcatt,
msdbd: raw.msdbd,
ofcs: raw.ofcs,
}
}
}
#[derive(Debug, Serialize, PartialEq)]
pub struct NvmeSmartLog {
pub nvme_name: String,
pub serial_number: String,
pub critical_warning: u8,
pub temperature: u16,
pub avail_spare: u8,
pub spare_thresh: u8,
pub percent_used: u8,
pub endurance_grp_critical_warning_summary: u8,
pub data_units_read: u128,
pub data_units_written: u128,
pub host_read_commands: u128,
pub host_write_commands: u128,
pub controller_busy_time: u128,
pub power_cycles: u128,
pub power_on_hours: u128,
pub unsafe_shutdowns: u128,
pub media_errors: u128,
pub num_err_log_entries: u128,
pub warning_temp_time: u32,
pub critical_comp_time: u32,
pub temperature_sensor_1: Option<u16>,
pub temperature_sensor_2: Option<u16>,
pub temperature_sensor_3: Option<u16>,
pub temperature_sensor_4: Option<u16>,
pub temperature_sensor_5: Option<u16>,
pub temperature_sensor_6: Option<u16>,
pub temperature_sensor_7: Option<u16>,
pub temperature_sensor_8: Option<u16>,
pub thm_temp1_trans_count: u32,
pub thm_temp2_trans_count: u32,
pub thm_temp1_total_time: u32,
pub thm_temp2_total_time: u32,
}
impl NvmeSmartLog {
pub fn new(nvme_name: String, serial_number: String, raw: &nvme_smart_log) -> Self {
Self {
nvme_name,
serial_number,
critical_warning: raw.critical_warning,
temperature: u16::from_le_bytes([raw.temperature[0], raw.temperature[1]]),
avail_spare: raw.avail_spare,
spare_thresh: raw.spare_thresh,
percent_used: raw.percent_used,
endurance_grp_critical_warning_summary: raw.endu_grp_crit_warn_sumry,
data_units_read: u128::from_le_bytes(raw.data_units_read),
data_units_written: u128::from_le_bytes(raw.data_units_written),
host_read_commands: u128::from_le_bytes(raw.host_reads),
host_write_commands: u128::from_le_bytes(raw.host_writes),
controller_busy_time: u128::from_le_bytes(raw.ctrl_busy_time),
power_cycles: u128::from_le_bytes(raw.power_cycles),
power_on_hours: u128::from_le_bytes(raw.power_on_hours),
unsafe_shutdowns: u128::from_le_bytes(raw.unsafe_shutdowns),
media_errors: u128::from_le_bytes(raw.media_errors),
num_err_log_entries: u128::from_le_bytes(raw.num_err_log_entries),
warning_temp_time: raw.warning_temp_time,
critical_comp_time: raw.critical_comp_time,
temperature_sensor_1: match raw.temp_sensor[0] {
0 => None,
v => Some(v),
},
temperature_sensor_2: match raw.temp_sensor[1] {
0 => None,
v => Some(v),
},
temperature_sensor_3: match raw.temp_sensor[2] {
0 => None,
v => Some(v),
},
temperature_sensor_4: match raw.temp_sensor[3] {
0 => None,
v => Some(v),
},
temperature_sensor_5: match raw.temp_sensor[4] {
0 => None,
v => Some(v),
},
temperature_sensor_6: match raw.temp_sensor[5] {
0 => None,
v => Some(v),
},
temperature_sensor_7: match raw.temp_sensor[6] {
0 => None,
v => Some(v),
},
temperature_sensor_8: match raw.temp_sensor[7] {
0 => None,
v => Some(v),
},
thm_temp1_trans_count: raw.thm_temp1_trans_count,
thm_temp2_trans_count: raw.thm_temp2_trans_count,
thm_temp1_total_time: raw.thm_temp1_total_time,
thm_temp2_total_time: raw.thm_temp2_total_time,
}
}
}
#[derive(Debug, Serialize, PartialEq)]
pub struct ErrorEntry {
pub error_count: u64,
pub submission_queue_id: u16,
pub command_id: u16,
pub status_field: u16,
pub parameter_error_location: u16,
pub lba: u64,
pub namespace_id: u32,
pub vendor_specific: u8,
pub transport_type: u8,
pub command_specific: u64,
pub transport_type_specific_info: u16,
}
impl ErrorEntry {
pub fn new(raw: &nvme_error_log_page) -> Self {
Self {
error_count: u64::from_le(raw.error_count),
submission_queue_id: u16::from_le(raw.sqid),
command_id: u16::from_le(raw.cmdid),
status_field: u16::from_le(raw.status_field),
parameter_error_location: u16::from_le(raw.parm_error_location),
lba: u64::from_le(raw.lba),
namespace_id: u32::from_le(raw.nsid),
vendor_specific: raw.vs,
transport_type: raw.trtype,
command_specific: u64::from_le(raw.cs),
transport_type_specific_info: u16::from_le(raw.trtype_spec_info),
}
}
}
#[derive(Debug, Serialize)]
pub struct NvmeErrorLog {
pub nvme_name: String,
pub serial_number: String,
pub entries: Vec<ErrorEntry>,
}
impl NvmeErrorLog {
pub fn new(
nvme_name: String,
serial_number: String,
raw_entries: Vec<nvme_error_log_page>,
) -> Self {
let entries = raw_entries
.iter()
.filter(|e| e.error_count != 0)
.map(ErrorEntry::new)
.collect();
Self {
nvme_name,
serial_number,
entries,
}
}
}
pub fn parse_ascii_field(bytes: &[c_char]) -> String {
let unsigned: Vec<u8> = bytes.iter().map(|&b| b as u8).collect();
String::from_utf8_lossy(&unsigned)
.trim_end_matches('\0')
.trim()
.to_string()
}
pub fn convert_cchar_to_u8_array_16(bytes: &[c_char; 16]) -> [u8; 16] {
let mut result = [0u8; 16];
for (i, &b) in bytes.iter().enumerate() {
result[i] = b as u8;
}
result
}