use std::fmt; use crate::{devices, hid, features, DeviceInfo, Error, Model}; use crate::devices::RawDevice as _; #[derive(Clone, Copy, Debug, Eq, PartialEq)] #[repr(u8)] enum Command { GetStatus = 0x00, GetPasswordRetryCount = 0x09, GetUserPasswordRetryCount = 0x0F, } impl From for u8 { fn from(command: Command) -> u8 { command as u8 } } pub struct Pro { hid_device: hidapi::HidDevice, path: std::ffi::CString, } impl Pro { pub(crate) fn new(hid_device: hidapi::HidDevice, device_info: &DeviceInfo<'_>) -> Self { Self { hid_device, path: device_info.device_info.path().to_owned(), } } } impl features::Basic for Pro { fn get_model(&self) -> Model { Model::Pro } fn get_status(&self) -> Result { self.execute(Command::GetStatus, ()) } fn get_user_retry_count(&self) -> Result { self.execute(Command::GetPasswordRetryCount, ()) } fn get_admin_retry_count(&self) -> Result { self.execute(Command::GetUserPasswordRetryCount, ()) } } impl devices::RawDevice for Pro { fn execute(&self, command_id: impl Into, data: T) -> Result where T: serde::Serialize, U: serde::de::DeserializeOwned, { use hid::HidDeviceExt as _; let command_id = command_id.into(); let crc = self.hid_device.send(&hid::Request::new(command_id, data))?; let mut response = self.hid_device.receive()?; let mut retry_count = 0; while response.device_status == hid::DeviceStatus::Busy && retry_count < 3 { std::thread::sleep(std::time::Duration::from_millis(100)); response = self.hid_device.receive()?; retry_count += 1; } if response.device_status != hid::DeviceStatus::Ok { return Err(format!("Got device status {:?}", response.device_status).into()); } if response.command_id != command_id { return Err(format!( "Expected command ID {}, got {}", command_id, response.command_id ) .into()); } if response.command_status != hid::CommandStatus::Ok { return Err(format!("Got command status {:?}", response.command_status).into()); } if response.last_crc() != crc { return Err(format!("Expected last_crc {}, got {}", crc, response.last_crc()).into()); } Ok(response.data) } } impl fmt::Debug for Pro { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Pro") .field("path", &self.path) .finish() } }