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