Skip to main content

hidpp/feature/extended_report_rate/
mod.rs

1//! Implements `ExtendedAdjustableReportRate` (feature `0x8061`).
2
3use std::sync::Arc;
4
5use num_enum::{IntoPrimitive, TryFromPrimitive};
6
7use crate::{
8    channel::HidppChannel,
9    feature::{CreatableFeature, Feature, FeatureEndpoint},
10    protocol::v20::Hidpp20Error,
11};
12
13bitflags::bitflags! {
14    /// Report-rate values supported by a `0x8061` device.
15    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
16    #[cfg_attr(feature = "serde", derive(serde::Serialize))]
17    pub struct ExtendedReportRateList: u16 {
18        /// 125 Hz, equivalent to an 8 ms report interval.
19        const HZ_125 = 1 << 0;
20        /// 250 Hz, equivalent to a 4 ms report interval.
21        const HZ_250 = 1 << 1;
22        /// 500 Hz, equivalent to a 2 ms report interval.
23        const HZ_500 = 1 << 2;
24        /// 1000 Hz, equivalent to a 1 ms report interval.
25        const HZ_1000 = 1 << 3;
26        /// 2000 Hz, equivalent to a 500 µs report interval.
27        const HZ_2000 = 1 << 4;
28        /// 4000 Hz, equivalent to a 250 µs report interval.
29        const HZ_4000 = 1 << 5;
30        /// 8000 Hz, equivalent to a 125 µs report interval.
31        const HZ_8000 = 1 << 6;
32    }
33}
34
35/// A connection type used by `ExtendedAdjustableReportRate`.
36#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive)]
37#[cfg_attr(feature = "serde", derive(serde::Serialize))]
38#[non_exhaustive]
39#[repr(u8)]
40pub enum ConnectionType {
41    /// Wired USB connection.
42    Wired = 0,
43    /// Logitech gaming wireless connection.
44    GamingWireless = 1,
45}
46
47/// A concrete report-rate setting for `0x8061`.
48#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive)]
49#[cfg_attr(feature = "serde", derive(serde::Serialize))]
50#[non_exhaustive]
51#[repr(u8)]
52pub enum ExtendedReportRate {
53    /// 125 Hz, equivalent to an 8 ms report interval.
54    Hz125 = 0,
55    /// 250 Hz, equivalent to a 4 ms report interval.
56    Hz250 = 1,
57    /// 500 Hz, equivalent to a 2 ms report interval.
58    Hz500 = 2,
59    /// 1000 Hz, equivalent to a 1 ms report interval.
60    Hz1000 = 3,
61    /// 2000 Hz, equivalent to a 500 µs report interval.
62    Hz2000 = 4,
63    /// 4000 Hz, equivalent to a 250 µs report interval.
64    Hz4000 = 5,
65    /// 8000 Hz, equivalent to a 125 µs report interval.
66    Hz8000 = 6,
67}
68
69/// Implements the `ExtendedAdjustableReportRate` / `0x8061` feature.
70#[derive(Clone)]
71pub struct ExtendedReportRateFeature {
72    /// The endpoint this feature talks to.
73    endpoint: FeatureEndpoint,
74}
75
76impl CreatableFeature for ExtendedReportRateFeature {
77    const ID: u16 = 0x8061;
78    const STARTING_VERSION: u8 = 0;
79
80    fn new(chan: Arc<HidppChannel>, device_index: u8, feature_index: u8) -> Self {
81        Self {
82            endpoint: FeatureEndpoint::new(chan, device_index, feature_index),
83        }
84    }
85}
86
87impl Feature for ExtendedReportRateFeature {}
88
89impl ExtendedReportRateFeature {
90    /// Retrieves the report rates supported by `connection_type`.
91    pub async fn get_device_capabilities(
92        &self,
93        connection_type: ConnectionType,
94    ) -> Result<ExtendedReportRateList, Hidpp20Error> {
95        let payload = self
96            .endpoint
97            .call(0, [u8::from(connection_type), 0, 0])
98            .await?
99            .extend_payload();
100        Ok(report_rate_list_from_payload(payload))
101    }
102
103    /// Retrieves the report rates available for the device's current connection.
104    pub async fn get_actual_report_rate_list(
105        &self,
106    ) -> Result<ExtendedReportRateList, Hidpp20Error> {
107        let payload = self.endpoint.call(1, [0; 3]).await?.extend_payload();
108        Ok(report_rate_list_from_payload(payload))
109    }
110
111    /// Retrieves the active report rate for `connection_type`.
112    pub async fn get_report_rate(
113        &self,
114        connection_type: ConnectionType,
115    ) -> Result<ExtendedReportRate, Hidpp20Error> {
116        let payload = self
117            .endpoint
118            .call(2, [u8::from(connection_type), 0, 0])
119            .await?
120            .extend_payload();
121        ExtendedReportRate::try_from(payload[0]).map_err(|_| Hidpp20Error::UnsupportedResponse)
122    }
123
124    /// Sets the report rate for the current host-side connection.
125    pub async fn set_report_rate(
126        &self,
127        report_rate: ExtendedReportRate,
128    ) -> Result<(), Hidpp20Error> {
129        self.endpoint.call(3, [u8::from(report_rate), 0, 0]).await?;
130        Ok(())
131    }
132}
133
134fn report_rate_list_from_payload(payload: [u8; 16]) -> ExtendedReportRateList {
135    ExtendedReportRateList::from_bits_retain(u16::from_be_bytes([payload[0], payload[1]]))
136}
137
138#[cfg(test)]
139mod tests {
140    use super::{ExtendedReportRateList, report_rate_list_from_payload};
141
142    #[test]
143    fn parses_report_rate_mask() {
144        let mut payload = [0; 16];
145        payload[1] = 0b0100_1001;
146
147        let rates = report_rate_list_from_payload(payload);
148
149        assert!(rates.contains(ExtendedReportRateList::HZ_125));
150        assert!(rates.contains(ExtendedReportRateList::HZ_1000));
151        assert!(rates.contains(ExtendedReportRateList::HZ_8000));
152    }
153}