Skip to main content

hidpp/feature/
extended_report_rate.rs

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