Skip to main content

hidpp/feature/
root.rs

1//! Implements the `Root` feature (ID `0x0000`) that every device supports by
2//! default.
3
4use std::sync::Arc;
5
6use super::{CreatableFeature, Feature, FeatureType};
7use crate::{
8    channel::HidppChannel,
9    nibble::U4,
10    protocol::v20::{self, Hidpp20Error},
11};
12
13/// Implements the `Root` / `0x0000` feature that every HID++2.0 device
14/// supports by default.
15///
16/// This implementation is added automatically to any [`crate::device::Device`]
17/// created using [`crate::device::Device::new`].
18#[derive(Clone)]
19pub struct RootFeature {
20    /// The underlying HID++ channel.
21    chan: Arc<HidppChannel>,
22
23    /// The index of the device to implement the feature for.
24    device_index: u8,
25}
26
27impl CreatableFeature for RootFeature {
28    const ID: u16 = 0x0000;
29    const STARTING_VERSION: u8 = 0;
30
31    fn new(chan: Arc<HidppChannel>, device_index: u8, _: u8) -> Self {
32        Self { chan, device_index }
33    }
34}
35
36impl Feature for RootFeature {}
37
38impl RootFeature {
39    /// Retrieves information about a specific feature ID, including its index
40    /// in the feature table, its type and its version.
41    ///
42    /// If the feature is not supported by the device, [`None`] is returned.
43    ///
44    /// If the device only supports the root feature version 1, the
45    /// [`FeatureInformation::version`] field will be `0` for all features.
46    pub async fn get_feature(&self, id: u16) -> Result<Option<FeatureInformation>, Hidpp20Error> {
47        let response = self
48            .chan
49            .send_v20(v20::Message::Short(
50                v20::MessageHeader {
51                    device_index: self.device_index,
52                    feature_index: 0,
53                    function_id: U4::from_lo(0),
54                    software_id: self.chan.get_sw_id(),
55                },
56                [(id >> 8) as u8, id as u8, 0x00],
57            ))
58            .await?;
59
60        let payload = response.extend_payload();
61        if payload[0] == 0 {
62            return Ok(None);
63        }
64
65        Ok(Some(FeatureInformation {
66            index: payload[0],
67            typ: FeatureType::from(payload[1]),
68            version: payload[2],
69        }))
70    }
71
72    /// Pings the device with an arbitrary data byte. The device will respond
73    /// with the same data if communication succeeds.
74    ///
75    /// The underlying function, as described in the protocol specification,
76    /// will also look up the protocol version supported by the device.\
77    /// This is not implemented here, as the
78    /// [`crate::protocol::determine_version`] function does so in a more
79    /// general manner.
80    pub async fn ping(&self, data: u8) -> Result<u8, Hidpp20Error> {
81        let response = self
82            .chan
83            .send_v20(v20::Message::Short(
84                v20::MessageHeader {
85                    device_index: self.device_index,
86                    feature_index: 0,
87                    function_id: U4::from_lo(1),
88                    software_id: self.chan.get_sw_id(),
89                },
90                [0x00, 0x00, data],
91            ))
92            .await?;
93
94        let payload = response.extend_payload();
95        Ok(payload[2])
96    }
97}
98
99/// Represents information about a specific feature as returned by the
100/// [`RootFeature::get_feature`] function.
101#[derive(Clone, Copy, Hash, Debug)]
102#[cfg_attr(feature = "serde", derive(serde::Serialize))]
103#[non_exhaustive]
104pub struct FeatureInformation {
105    /// The index of the feature in the version table.
106    /// This is used for invocations of functions of that feature.
107    pub index: u8,
108
109    /// The type of the feature.
110    pub typ: FeatureType,
111
112    /// The latest supported version of the feature.
113    ///
114    /// Multi-version features are always backwards compatible as long as the
115    /// feature ID does not change, meaning functions implemented for an older
116    /// version of the same feature will behave as expected for every later
117    /// version.
118    ///
119    /// This field was added in feature version 1 and will be `0` for all older
120    /// versions.
121    pub version: u8,
122}