Skip to main content

hidpp/feature/feature_set/
mod.rs

1//! Implements the `FeatureSet` feature (ID `0x0001`) that allows enumerating
2//! all the features supported by a device.
3
4use std::sync::Arc;
5
6use crate::{
7    channel::HidppChannel,
8    feature::{CreatableFeature, Feature, FeatureType},
9    nibble::U4,
10    protocol::v20::{self, Hidpp20Error},
11};
12
13/// Implements the `FeatureSet` / `0x0001` feature.
14///
15/// This feature is primarily used to collect all features supported by the
16/// device. To achieve this, call [`Self::count`] to retrieve the amount of
17/// supported features (excluding the root feature). Then call
18/// [`Self::get_feature`] for every `i in 1..=count` (1-based, as accessing the
19/// root feature is not allowed).
20#[derive(Clone)]
21pub struct FeatureSetFeature {
22    /// The underlying HID++ channel.
23    chan: Arc<HidppChannel>,
24
25    /// The index of the device to implement the feature for.
26    device_index: u8,
27
28    /// The index of the feature in the feature table.
29    feature_index: u8,
30}
31
32impl CreatableFeature for FeatureSetFeature {
33    const ID: u16 = 0x0001;
34    const STARTING_VERSION: u8 = 0;
35
36    fn new(chan: Arc<HidppChannel>, device_index: u8, feature_index: u8) -> Self {
37        Self {
38            chan,
39            device_index,
40            feature_index,
41        }
42    }
43}
44
45impl Feature for FeatureSetFeature {}
46
47impl FeatureSetFeature {
48    /// Retrieves the amount of features supported by the device, not including
49    /// the root feature.
50    pub async fn count(&self) -> Result<u8, Hidpp20Error> {
51        let response = self
52            .chan
53            .send_v20(v20::Message::Short(
54                v20::MessageHeader {
55                    device_index: self.device_index,
56                    feature_index: self.feature_index,
57                    function_id: U4::from_lo(0),
58                    software_id: self.chan.get_sw_id(),
59                },
60                [0x00, 0x00, 0x00],
61            ))
62            .await?;
63
64        Ok(response.extend_payload()[0])
65    }
66
67    /// Retrieves the information about a specific feature based on its index in
68    /// the feature table.
69    ///
70    /// Feature index `0` for the root feature is not allowed.
71    pub async fn get_feature(&self, index: u8) -> Result<FeatureInformation, Hidpp20Error> {
72        let response = self
73            .chan
74            .send_v20(v20::Message::Short(
75                v20::MessageHeader {
76                    device_index: self.device_index,
77                    feature_index: self.feature_index,
78                    function_id: U4::from_lo(1),
79                    software_id: self.chan.get_sw_id(),
80                },
81                [index, 0x00, 0x00],
82            ))
83            .await?;
84
85        let payload = response.extend_payload();
86
87        Ok(FeatureInformation {
88            id: (payload[0] as u16) << 8 | payload[1] as u16,
89            typ: FeatureType::from(payload[2]),
90            version: payload[3],
91        })
92    }
93}
94
95/// Represents information about a specific feature as returned by the
96/// [`FeatureSetFeature::get_feature`] function.
97#[derive(Clone, Copy, Hash, Debug)]
98#[cfg_attr(feature = "serde", derive(serde::Serialize))]
99#[non_exhaustive]
100pub struct FeatureInformation {
101    /// The protocol ID of the feature.
102    pub id: u16,
103
104    /// The type of the feature.
105    pub typ: FeatureType,
106
107    /// The latest supported version of the feature.
108    ///
109    /// Multi-version features are always backwards compatible as long as the
110    /// feature ID does not change, meaning functions implemented for an older
111    /// version of the same feature will behave as expected for every later
112    /// version.
113    ///
114    /// This field was added in feature version 1 and will be `0` for all older
115    /// versions.
116    pub version: u8,
117}