async_hid/
lib.rs

1#![doc = include_str!("../README.md")]
2
3mod backend;
4mod error;
5
6use std::fmt::{Debug, Formatter};
7use std::future::Future;
8use std::hash::{Hash, Hasher};
9
10use futures_core::Stream;
11use static_assertions::assert_impl_all;
12use crate::backend::{BackendDevice, BackendDeviceId, BackendPrivateData};
13pub use crate::error::{ErrorSource, HidError, HidResult};
14
15/// A struct containing basic information about a device
16///
17/// This struct can be obtained by calling [DeviceInfo::enumerate] and upgraded into a usable [Device] by calling [DeviceInfo::open].
18#[derive(Debug, Clone)]
19pub struct DeviceInfo {
20    /// OS specific identifier
21    pub id: DeviceId,
22    /// The human readable name
23    pub name: String,
24    /// The HID product id assigned to this device
25    pub product_id: u16,
26    /// The HID vendor id of the device's manufacturer (i.e Logitech = 0x46D)
27    pub vendor_id: u16,
28    /// The HID usage id
29    pub usage_id: u16,
30    /// The HID usage page
31    pub usage_page: u16,
32
33    pub(crate) private_data: BackendPrivateData,
34}
35
36impl DeviceInfo {
37    /// Enumerates all **accessible** HID devices
38    ///
39    /// If this library fails to retrieve the [DeviceInfo] of a device it will be automatically excluded.
40    /// Register a `log` compatible logger at `trace` level for more information about the discarded devices.
41    pub fn enumerate() -> impl Future<Output = HidResult<impl Stream<Item = DeviceInfo> + Unpin + Send>> {
42        backend::enumerate()
43    }
44
45    /// Opens the associated device in the requested [AccessMode]
46    pub async fn open(&self, mode: AccessMode) -> HidResult<Device> {
47        let dev = backend::open(&self.id.0, mode).await?;
48        Ok(Device {
49            inner: dev,
50            info: self.clone(),
51            mode
52        })
53    }
54
55    /// Convenience method for easily finding a specific device
56    pub fn matches(&self, usage_page: u16, usage_id: u16, vendor_id: u16, product_id: u16) -> bool {
57        self.usage_page == usage_page && self.usage_id == usage_id && self.vendor_id == vendor_id && self.product_id == product_id
58    }
59}
60
61impl Hash for DeviceInfo {
62    fn hash<H: Hasher>(&self, state: &mut H) {
63        self.id.hash(state);
64        self.name.hash(state);
65        self.product_id.hash(state);
66        self.vendor_id.hash(state);
67        self.usage_id.hash(state);
68        self.usage_page.hash(state);
69    }
70}
71
72impl PartialEq for DeviceInfo {
73    fn eq(&self, other: &Self) -> bool {
74        self.id == other.id
75            && self.name == other.name
76            && self.product_id == other.product_id
77            && self.vendor_id == other.vendor_id
78            && self.usage_id == other.usage_id
79            && self.usage_page == other.usage_page
80    }
81}
82
83impl Eq for DeviceInfo {}
84
85pub trait SerialNumberExt {
86    fn serial_number(&self) -> Option<&str>;
87}
88
89/// A struct representing an opened device
90///
91/// Dropping this struct will close the associated device
92pub struct Device {
93    inner: BackendDevice,
94    info: DeviceInfo,
95    mode: AccessMode
96}
97
98impl Device {
99    /// Read a input report from this device
100    pub fn read_input_report<'a>(&'a self, buf: &'a mut [u8]) -> impl Future<Output = HidResult<usize>> + Send + 'a {
101        debug_assert!(self.mode.readable());
102        self.inner.read_input_report(buf)
103    }
104
105    /// Write an output report to this device
106    pub fn write_output_report<'a>(&'a self, buf: &'a [u8]) -> impl Future<Output = HidResult<()>> + Send + 'a {
107        debug_assert!(self.mode.writeable());
108        self.inner.write_output_report(buf)
109    }
110
111    /// Retrieves the [DeviceInfo] associated with this device
112    pub fn info(&self) -> &DeviceInfo {
113        &self.info
114    }
115}
116
117/// An opaque struct that wraps the OS specific identifier of a device
118#[derive(Hash, Clone, Eq, PartialEq)]
119#[repr(transparent)]
120pub struct DeviceId(BackendDeviceId);
121
122impl From<BackendDeviceId> for DeviceId {
123    fn from(value: BackendDeviceId) -> Self {
124        Self(value)
125    }
126}
127
128impl Debug for DeviceId {
129    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
130        write!(f, "{:?}", self.0)
131    }
132}
133
134/// An enum that controls how a device will be opened
135///
136/// This mainly influences the flags passed to the underlying OS api,
137/// but is also used to avoid initializing read specific data structures for write-only devices.
138///
139/// In general `Read` means shared access and `Write` or `ReadWrite` means exclusive access
140#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
141pub enum AccessMode {
142    Read,
143    Write,
144    #[default]
145    ReadWrite
146}
147
148impl AccessMode {
149    pub fn readable(self) -> bool {
150        matches!(self, Self::Read | Self::ReadWrite)
151    }
152    pub fn writeable(self) -> bool {
153        matches!(self, Self::Write | Self::ReadWrite)
154    }
155}
156
157assert_impl_all!(Device: Send, Sync);
158assert_impl_all!(DeviceInfo: Send, Sync);