hidpp/device.rs
1//! Implements peripheral devices connected to HID++ channels.
2
3use std::{any::TypeId, collections::HashMap, sync::Arc};
4
5use thiserror::Error;
6
7use crate::{
8 channel::{ChannelError, HidppChannel},
9 feature::{
10 self, CreatableFeature, Feature,
11 feature_set::{FeatureInformation, FeatureSetFeature},
12 root::RootFeature,
13 },
14 protocol::{self, ProtocolVersion, v20::Hidpp20Error},
15};
16
17/// Represents a single HID++ device connected to a [`HidppChannel`].
18///
19/// This is used only for peripheral devices and not receivers.
20#[derive(Clone)]
21pub struct Device {
22 /// The underlying HID++ channel.
23 chan: Arc<HidppChannel>,
24
25 /// The initialized implementation of features the device supports.
26 features: HashMap<TypeId, Arc<dyn Feature>>,
27
28 /// The index of the device on the HID++ channel.
29 pub device_index: u8,
30
31 /// The supported protocol version reported by the device.
32 pub protocol_version: ProtocolVersion,
33}
34
35impl Device {
36 /// Tries to initialize a device on a HID++ channel.
37 ///
38 /// This will automatically ping the device to determine the protocol
39 /// version it supports via [`protocol::determine_version`].
40 ///
41 /// Returns [`DeviceError::DeviceNotFound`] if there is no device with the
42 /// specified index connected to the channel.
43 ///
44 /// Returns [`DeviceError::UnsupportedProtocolVersion`] if the device only
45 /// supports [`ProtocolVersion::V10`].
46 pub async fn new(chan: Arc<HidppChannel>, device_index: u8) -> Result<Self, DeviceError> {
47 let protocol_version = protocol::determine_version(&chan, device_index).await?;
48
49 if protocol_version.is_none() {
50 return Err(DeviceError::DeviceNotFound);
51 }
52 let version = protocol_version.unwrap();
53
54 if version == ProtocolVersion::V10 {
55 return Err(DeviceError::UnsupportedProtocolVersion);
56 }
57
58 let mut device = Self {
59 chan,
60 features: HashMap::new(),
61 device_index,
62 protocol_version: version,
63 };
64
65 // Every HID++2.0 device supports the root feature.
66 // We implicitly verified that using [`protocol::determine_version`].
67 device.add_feature::<RootFeature>(0);
68
69 Ok(device)
70 }
71
72 /// A convenience wrapper around [`Self::get_feature`] to obtain the root
73 /// feature.
74 pub fn root(&self) -> Arc<RootFeature> {
75 self.get_feature::<RootFeature>().unwrap()
76 }
77
78 /// Adds a new feature implementation to the list of available features.
79 /// This will override an existing implementation of the same type.
80 /// The caller is responsible for making sure the device actually supports
81 /// the feature.
82 pub fn add_feature_instance<F: Feature>(&mut self, feature: F) -> Arc<F> {
83 let feat_rc: Arc<dyn Feature> = Arc::new(feature);
84
85 self.features
86 .insert(TypeId::of::<F>(), Arc::clone(&feat_rc));
87
88 Arc::downcast::<F>(feat_rc).unwrap()
89 }
90
91 /// Adds a new feature implementation to the list of available features.
92 /// This will override an existing implementation of the same type.
93 /// The caller is responsible for making sure the device actually supports
94 /// the feature.
95 ///
96 /// This method uses [`CreatableFeature`] to automatically create an
97 /// instance of the feature implementation and adds it using
98 /// [`Self::add_feature_instance`].
99 pub fn add_feature<F: CreatableFeature>(&mut self, feature_index: u8) -> Arc<F> {
100 self.add_feature_instance(F::new(
101 Arc::clone(&self.chan),
102 self.device_index,
103 feature_index,
104 ))
105 }
106
107 /// Checks whether a specific feature implementation is provided by the
108 /// device.
109 pub fn provides_feature<F: Feature>(&self) -> bool {
110 self.features.contains_key(&TypeId::of::<F>())
111 }
112
113 /// Tries to retrieve a feature implementation from the device.
114 ///
115 /// Returns [`None`] if the requested feature implementation is not
116 /// provided.
117 pub fn get_feature<F: Feature>(&self) -> Option<Arc<F>> {
118 self.features
119 .get(&TypeId::of::<F>())
120 .cloned()
121 .and_then(|feat| Arc::downcast::<F>(feat).ok())
122 }
123
124 /// Tries to detect all features supported by the device and add
125 /// implementations for them using [`feature::registry::lookup_version`].
126 ///
127 /// Returns a vector containing all feature IDs supported by the device.
128 ///
129 /// Returns `Ok(None)` if the [`FeatureSetFeature`] feature, which is
130 /// required for feature enumeration, is not supported by the device.
131 pub async fn enumerate_features(
132 &mut self,
133 ) -> Result<Option<Vec<FeatureInformation>>, Hidpp20Error> {
134 let Some(feature_set_info) = self.root().get_feature(FeatureSetFeature::ID).await? else {
135 return Ok(None);
136 };
137
138 let feature_set_feature = self.add_feature::<FeatureSetFeature>(feature_set_info.index);
139
140 let count = feature_set_feature.count().await?;
141 let mut features = Vec::with_capacity(count as usize);
142 for i in 1..=count {
143 let info = feature_set_feature.get_feature(i).await?;
144 features.push(info);
145
146 if i == feature_set_info.index {
147 continue;
148 }
149
150 let Some(impls) = feature::registry::lookup_version(info.id, info.version) else {
151 continue;
152 };
153
154 for feat_impl in impls {
155 let (type_id, instance) =
156 (feat_impl.producer)(Arc::clone(&self.chan), self.device_index, i);
157
158 self.features.insert(type_id, instance);
159 }
160 }
161
162 Ok(Some(features))
163 }
164}
165
166/// Represents a device-specific error.
167#[derive(Debug, Error)]
168#[non_exhaustive]
169pub enum DeviceError {
170 /// Indicates that the underlying [`HidppChannel`] returned an error.
171 #[error("the HID++ channel returned an error")]
172 Channel(#[from] ChannelError),
173
174 /// Indicates that the specified device index points to no device.
175 #[error("there is no device with the specified device index")]
176 DeviceNotFound,
177
178 /// Indicates that the addressed device does only support HID++1.0.
179 #[error("the device does not support HID++2.0 or newer")]
180 UnsupportedProtocolVersion,
181}