#![warn(
missing_copy_implementations,
missing_debug_implementations,
missing_docs,
non_ascii_idents,
trivial_casts,
unused,
unused_qualifications
)]
#![deny(unsafe_code)]
pub mod command;
pub mod error;
mod hid;
mod message;
mod packet;
mod transaction;
use std::{fmt, ops};
use command::{Command, VendorCommand};
use error::{CommandError, Error};
pub struct Devices {
hidapi: hidapi::HidApi,
devices: Vec<DeviceInfo>,
}
impl Devices {
pub fn connect(&self, device_info: &DeviceInfo) -> Result<Device, Error> {
Device::connect(&self.hidapi, &device_info.hid_device_info)
}
}
impl<'a> IntoIterator for &'a Devices {
type Item = &'a DeviceInfo;
type IntoIter = std::slice::Iter<'a, DeviceInfo>;
fn into_iter(self) -> Self::IntoIter {
self.devices.iter()
}
}
impl fmt::Debug for Devices {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Devices")
.field("devices", &self.devices)
.finish()
}
}
impl ops::Deref for Devices {
type Target = [DeviceInfo];
fn deref(&self) -> &Self::Target {
&self.devices
}
}
#[derive(Clone)]
pub struct DeviceInfo {
hid_device_info: hidapi::DeviceInfo,
}
impl DeviceInfo {
pub fn vendor_id(&self) -> u16 {
self.hid_device_info.vendor_id()
}
pub fn product_id(&self) -> u16 {
self.hid_device_info.product_id()
}
}
impl fmt::Debug for DeviceInfo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Device")
.field("vendor_id", &self.vendor_id())
.field("product_id", &self.product_id())
.finish()
}
}
pub struct Device {
device_info: DeviceInfo,
device: hidapi::HidDevice,
channel: transaction::Channel,
}
impl Device {
pub fn connect(
hidapi: &hidapi::HidApi,
device_info: &hidapi::DeviceInfo,
) -> Result<Self, Error> {
let device = device_info.open_device(hidapi)?;
Device::new(device_info.to_owned(), device)
}
fn new(device_info: hidapi::DeviceInfo, device: hidapi::HidDevice) -> Result<Self, Error> {
let device_info = DeviceInfo {
hid_device_info: device_info,
};
let mut device = Self {
device_info,
device,
channel: transaction::Channel::BROADCAST,
};
device.channel = device.init([1, 2, 3, 4, 5, 6, 7, 8])?;
Ok(device)
}
pub fn ping(&self, data: &[u8]) -> Result<(), Error> {
let response = self.transaction(Command::Ping, data)?;
if data == response {
Ok(())
} else {
Err(Error::from(CommandError::InvalidPingData))
}
}
pub fn wink(&self) -> Result<(), Error> {
let response = self.transaction(Command::Wink, &[])?;
if response.is_empty() {
Ok(())
} else {
unimplemented!()
}
}
pub fn vendor_command(&self, command: VendorCommand, data: &[u8]) -> Result<Vec<u8>, Error> {
self.transaction(Command::Vendor(command), data)
}
fn init(&self, nonce: [u8; 8]) -> Result<transaction::Channel, Error> {
let response = self.transaction(Command::Init, &nonce)?;
if nonce == response[..8] {
Ok(transaction::Channel::from([
response[8],
response[9],
response[10],
response[11],
]))
} else {
Err(Error::from(CommandError::InvalidNonce))
}
}
fn transaction(&self, command: Command, data: &[u8]) -> Result<Vec<u8>, Error> {
transaction::exec(&self.device, self.channel, command, data)
}
}
impl fmt::Debug for Device {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Device")
.field("vendor_id", &self.device_info.vendor_id())
.field("product_id", &self.device_info.product_id())
.field("channel", &self.channel)
.finish()
}
}
impl ops::Deref for Device {
type Target = DeviceInfo;
fn deref(&self) -> &Self::Target {
&self.device_info
}
}
pub fn list() -> Result<Devices, Error> {
let hidapi = hidapi::HidApi::new()?;
let devices = hidapi
.device_list()
.filter(|device| device.vendor_id() == 0x20a0)
.filter(|device| device.product_id() == 0x42b1 || device.product_id() == 0x42b2)
.map(|device| DeviceInfo {
hid_device_info: device.to_owned(),
})
.collect();
Ok(Devices { hidapi, devices })
}