libcdio_rs/mmc/
inquiry.rs1use displaydoc::Display;
22use thiserror::Error;
23use tracing::debug;
24
25use crate::{
26 Mmc,
27 mmc::{Cdb, MmcCommand, MmcDirection, MmcError},
28};
29
30impl Mmc {
32 pub fn hardware_identifiers(&self) -> Result<HardwareIdentifiers, GetHardwareIdentifiersError> {
34 let buf = self.inquiry(InquiryKind::Standard)?;
35 let response_data_format = buf[RESPONSE_DATA_FMT_POS] & 0b1111;
36 if response_data_format != RESPONSE_DATA_FMT_MMC {
37 return Err(MmcInquiryError::InvalidResponse(format!(
38 "unsupported non-standard INQUIRY response format, got response data format of {}",
39 response_data_format,
40 ))
41 .into());
42 }
43 let vendor = String::from_utf8_lossy(&buf[VENDOR_ID_START_POS..=VENDOR_ID_END_POS])
44 .trim()
45 .to_string();
46 let product = String::from_utf8_lossy(&buf[PRODUCT_ID_START_POS..=PRODUCT_ID_END_POS])
47 .trim()
48 .to_string();
49 let revision = String::from_utf8_lossy(&buf[REVISION_START_POS..=REVISION_END_POS])
50 .trim()
51 .to_string();
52
53 return Ok(HardwareIdentifiers {
54 product,
55 vendor,
56 revision,
57 });
58
59 const RESPONSE_DATA_FMT_POS: usize = 3;
60 const RESPONSE_DATA_FMT_MMC: u8 = 2;
61 const VENDOR_ID_START_POS: usize = 8;
62 const VENDOR_ID_END_POS: usize = 15;
63 const PRODUCT_ID_START_POS: usize = 16;
64 const PRODUCT_ID_END_POS: usize = 31;
65 const REVISION_START_POS: usize = 32;
66 const REVISION_END_POS: usize = 35;
67 }
68
69 fn inquiry(&self, kind: InquiryKind) -> Result<Vec<u8>, MmcInquiryError> {
70 let mut buf = vec![0; ALLOC_LEN];
71 let mut cdb = Cdb::default();
72 cdb[0] = MmcCommand::Inquiry as u8;
73 if let InquiryKind::VitalProductData { page_code } = kind {
74 cdb[1] = EVPD_SET;
75 cdb[2] = page_code;
76 }
77 cdb[3..=4].copy_from_slice(&(buf.len() as u16).to_be_bytes());
78
79 self.run_command(Some(MmcDirection::Read), buf.as_mut_slice(), cdb)?;
80 debug!(?buf, len = buf.len());
81
82 return Ok(buf);
83
84 const ALLOC_LEN: usize = 255;
85 const EVPD_SET: u8 = 1;
86 }
87
88 pub(crate) fn is_mmc_device(&self) -> Result<bool, MmcInquiryError> {
94 let response = self.inquiry(InquiryKind::Standard)?;
95 let peripheral_device_type = response[0] & PERIPHERAL_DEVICE_TYPE_BITMASK;
96 return Ok(peripheral_device_type == PERIPHERAL_DEVICE_TYPE_CD_DVD);
97
98 const PERIPHERAL_DEVICE_TYPE_BITMASK: u8 = 0b11111;
99 const PERIPHERAL_DEVICE_TYPE_CD_DVD: u8 = 0x05;
100 }
101}
102
103#[derive(Debug, Display, Error)]
105pub struct GetHardwareIdentifiersError {
106 #[from]
107 pub source: MmcInquiryError,
108}
109
110#[derive(Debug, Display, Error)]
112pub enum MmcInquiryError {
113 Os(#[from] MmcError),
115
116 InvalidResponse(String),
118}
119
120#[derive(Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
121pub struct HardwareIdentifiers {
123 pub product: String,
124 pub vendor: String,
125 pub revision: String,
126}
127
128#[allow(unused)]
129#[derive(Clone, Copy, Debug)]
130enum InquiryKind {
131 Standard,
132 VitalProductData { page_code: u8 },
133}
134
135#[cfg(test)]
136mod tests {
137 use tracing::info;
138
139 use super::*;
140
141 #[test_log::test(test)]
142 #[ignore = "requires a drive with mmc"]
143 fn hardware_identifiers() {
144 let hardware_identifiers = Mmc::new().unwrap().hardware_identifiers().unwrap();
145 info!(?hardware_identifiers);
146 }
147}