1use std::error::Error;
2use std::fs::File;
3use std::os::unix::io::AsRawFd;
4
5use nix::ioctl_readwrite;
6
7const IDENTIFY_BUFFER_SIZE: usize = 4096;
9const MODEL_NUMBER_OFFSET: usize = 24;
10const MODEL_NUMBER_LEN: usize = 40;
11const FIRMWARE_REV_OFFSET: usize = 64;
12const FIRMWARE_REV_LEN: usize = 8;
13
14const NVME_ADMIN_IDENTIFY_OPCODE: u8 = 0x06;
16const NVME_IDENTIFY_CNS_CTRL: u32 = 0x01; #[repr(C)]
19#[derive(Debug, Default)]
20struct NvmeAdminCmd {
21 opcode: u8,
22 flags: u8,
23 rsvd1: u16,
24 nsid: u32,
25 cdw2: u32,
26 cdw3: u32,
27 metadata: u64,
28 addr: u64,
29 metadata_len: u32,
30 data_len: u32,
31 cdw10: u32,
32 cdw11: u32,
33 cdw12: u32,
34 cdw13: u32,
35 cdw14: u32,
36 cdw15: u32,
37 timeout_ms: u32,
38 result: u32,
39}
40
41ioctl_readwrite!(nvme_admin_cmd_ioctl, b'N', 0x41, NvmeAdminCmd);
42
43#[derive(Debug)]
45pub struct NvmeInfo {
46 pub model_number: String,
47 pub firmware_version: String,
48}
49
50fn parse_string(buffer: &[u8], offset: usize, len: usize) -> String {
51 let bytes = &buffer[offset..offset + len];
52 String::from_utf8_lossy(bytes)
53 .trim_end_matches('\0')
54 .trim()
55 .to_string()
56}
57
58pub fn get_nvme_firmware_version(device_path: &str) -> Result<NvmeInfo, Box<dyn Error>> {
60 let file = File::open(device_path)
61 .map_err(|e| format!("Failed to open NVMe device {}: {}", device_path, e))?;
62 let fd = file.as_raw_fd();
63
64 let mut buffer = vec![0u8; IDENTIFY_BUFFER_SIZE];
65 let mut cmd = NvmeAdminCmd {
66 opcode: NVME_ADMIN_IDENTIFY_OPCODE,
67 addr: buffer.as_mut_ptr() as u64,
68 data_len: buffer.len() as u32,
69 cdw10: NVME_IDENTIFY_CNS_CTRL,
70 ..Default::default()
71 };
72
73 let status = unsafe { nvme_admin_cmd_ioctl(fd, &mut cmd)? };
74 if status != 0 {
75 return Err(format!("NVMe command failed with status code: {:#x}", status).into());
76 }
77
78 let model_number = parse_string(&buffer, MODEL_NUMBER_OFFSET, MODEL_NUMBER_LEN);
79 let firmware_version = parse_string(&buffer, FIRMWARE_REV_OFFSET, FIRMWARE_REV_LEN);
80
81 Ok(NvmeInfo {
82 model_number,
83 firmware_version,
84 })
85}