diff options
Diffstat (limited to 'src/devices')
-rw-r--r-- | src/devices/pro.rs | 99 |
1 files changed, 99 insertions, 0 deletions
diff --git a/src/devices/pro.rs b/src/devices/pro.rs new file mode 100644 index 0000000..f986f6b --- /dev/null +++ b/src/devices/pro.rs @@ -0,0 +1,99 @@ +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<Command> 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<features::Status, Error> { + self.execute(Command::GetStatus, ()) + } + + fn get_user_retry_count(&self) -> Result<u8, Error> { + self.execute(Command::GetPasswordRetryCount, ()) + } + + fn get_admin_retry_count(&self) -> Result<u8, Error> { + self.execute(Command::GetUserPasswordRetryCount, ()) + } +} + +impl devices::RawDevice for Pro { + fn execute<T, U>(&self, command_id: impl Into<u8>, data: T) -> Result<U, Error> + 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() + } +} + |