Skip to main content

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}