summaryrefslogtreecommitdiff
path: root/src/devices/pro.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/pro.rs')
-rw-r--r--src/devices/pro.rs99
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()
+ }
+}
+