Skip to main content

hidpp/feature/multi_platform/
mod.rs

1//! Implements `MultiPlatform` (feature `0x4531`).
2
3use std::sync::Arc;
4
5use num_enum::TryFromPrimitive;
6
7use crate::{
8    channel::HidppChannel,
9    feature::{CreatableFeature, Feature, FeatureEndpoint, hosts_info::HostIndex},
10    protocol::v20::Hidpp20Error,
11};
12
13bitflags::bitflags! {
14    /// Capabilities reported by `MultiPlatform`.
15    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
16    #[cfg_attr(feature = "serde", derive(serde::Serialize))]
17    pub struct MultiPlatformCapabilities: u16 {
18        /// The device can detect the host OS automatically.
19        const OS_DETECTION = 1 << 0;
20        /// Software can set the host platform.
21        const SET_HOST_PLATFORM = 1 << 1;
22    }
23}
24
25bitflags::bitflags! {
26    /// Operating systems covered by a platform descriptor.
27    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
28    #[cfg_attr(feature = "serde", derive(serde::Serialize))]
29    pub struct OsMask: u16 {
30        /// Microsoft Windows.
31        const WINDOWS = 1 << 0;
32        /// Windows Embedded.
33        const WINDOWS_EMBEDDED = 1 << 1;
34        /// Linux.
35        const LINUX = 1 << 2;
36        /// ChromeOS.
37        const CHROME = 1 << 3;
38        /// Android.
39        const ANDROID = 1 << 4;
40        /// macOS.
41        const MACOS = 1 << 5;
42        /// iOS.
43        const IOS = 1 << 6;
44        /// webOS.
45        const WEBOS = 1 << 7;
46        /// Tizen.
47        const TIZEN = 1 << 8;
48    }
49}
50
51/// Source of a host-platform selection.
52#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, TryFromPrimitive)]
53#[cfg_attr(feature = "serde", derive(serde::Serialize))]
54#[non_exhaustive]
55#[repr(u8)]
56pub enum PlatformSource {
57    /// Device default.
58    Default = 0,
59    /// Automatically detected by the device.
60    Auto = 1,
61    /// Manually selected on the device.
62    Manual = 2,
63    /// Set by host software.
64    Software = 3,
65}
66
67/// Static `MultiPlatform` feature information.
68#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
69#[cfg_attr(feature = "serde", derive(serde::Serialize))]
70#[non_exhaustive]
71pub struct MultiPlatformInfo {
72    /// Feature capabilities.
73    pub capabilities: MultiPlatformCapabilities,
74    /// Number of platform IDs.
75    pub platform_count: u8,
76    /// Number of platform descriptor rows.
77    pub descriptor_count: u8,
78    /// Number of host slots.
79    pub host_count: u8,
80    /// Current host slot.
81    pub current_host: HostIndex,
82    /// Platform index selected for the current host.
83    pub current_host_platform: Option<u8>,
84}
85
86/// A platform descriptor row.
87#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
88#[cfg_attr(feature = "serde", derive(serde::Serialize))]
89#[non_exhaustive]
90pub struct PlatformDescriptor {
91    /// Platform index this descriptor belongs to.
92    pub platform_index: u8,
93    /// Descriptor row index.
94    pub descriptor_index: u8,
95    /// Covered operating systems.
96    pub os_mask: OsMask,
97    /// First supported OS major version.
98    pub from_version: u8,
99    /// First supported OS revision.
100    pub from_revision: u8,
101    /// Last supported OS major version.
102    pub to_version: u8,
103    /// Last supported OS revision.
104    pub to_revision: u8,
105}
106
107/// Platform selection for a host slot.
108#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
109#[cfg_attr(feature = "serde", derive(serde::Serialize))]
110#[non_exhaustive]
111pub struct HostPlatform {
112    /// Host slot index returned by the device.
113    pub host_index: HostIndex,
114    /// Raw host status byte.
115    pub status: u8,
116    /// Selected platform, or `None` when undefined.
117    pub platform_index: Option<u8>,
118    /// Source of the platform selection.
119    pub source: PlatformSource,
120    /// Automatically detected platform, if available.
121    pub auto_platform_index: Option<u8>,
122    /// Automatically matched platform descriptor, if available.
123    pub auto_descriptor_index: Option<u8>,
124}
125
126/// Implements the `MultiPlatform` / `0x4531` feature.
127#[derive(Clone)]
128pub struct MultiPlatformFeature {
129    /// The endpoint this feature talks to.
130    endpoint: FeatureEndpoint,
131}
132
133impl CreatableFeature for MultiPlatformFeature {
134    const ID: u16 = 0x4531;
135    const STARTING_VERSION: u8 = 1;
136
137    fn new(chan: Arc<HidppChannel>, device_index: u8, feature_index: u8) -> Self {
138        Self {
139            endpoint: FeatureEndpoint::new(chan, device_index, feature_index),
140        }
141    }
142}
143
144impl Feature for MultiPlatformFeature {}
145
146impl MultiPlatformFeature {
147    /// Retrieves feature capabilities and platform counts.
148    pub async fn get_feature_infos(&self) -> Result<MultiPlatformInfo, Hidpp20Error> {
149        let payload = self.endpoint.call(0, [0; 3]).await?.extend_payload();
150        Ok(MultiPlatformInfo {
151            capabilities: MultiPlatformCapabilities::from_bits_retain(u16::from_be_bytes([
152                payload[0], payload[1],
153            ])),
154            platform_count: payload[2],
155            descriptor_count: payload[3],
156            host_count: payload[4],
157            current_host: HostIndex::from(payload[5]),
158            current_host_platform: optional_index(payload[6]),
159        })
160    }
161
162    /// Retrieves a platform descriptor row.
163    pub async fn get_platform_descriptor(
164        &self,
165        descriptor_index: u8,
166    ) -> Result<PlatformDescriptor, Hidpp20Error> {
167        let payload = self
168            .endpoint
169            .call(1, [descriptor_index, 0, 0])
170            .await?
171            .extend_payload();
172        Ok(PlatformDescriptor {
173            platform_index: payload[0],
174            descriptor_index: payload[1],
175            os_mask: OsMask::from_bits_retain(u16::from_be_bytes([payload[2], payload[3]])),
176            from_version: payload[4],
177            from_revision: payload[5],
178            to_version: payload[6],
179            to_revision: payload[7],
180        })
181    }
182
183    /// Retrieves the platform selected for `host`.
184    pub async fn get_host_platform(&self, host: HostIndex) -> Result<HostPlatform, Hidpp20Error> {
185        let payload = self
186            .endpoint
187            .call(2, [u8::from(host), 0, 0])
188            .await?
189            .extend_payload();
190        Ok(HostPlatform {
191            host_index: HostIndex::from(payload[0]),
192            status: payload[1],
193            platform_index: optional_index(payload[2]),
194            source: PlatformSource::try_from(payload[3])
195                .map_err(|_| Hidpp20Error::UnsupportedResponse)?,
196            auto_platform_index: optional_index(payload[4]),
197            auto_descriptor_index: optional_index(payload[5]),
198        })
199    }
200}
201
202fn optional_index(value: u8) -> Option<u8> {
203    (value != 0xff).then_some(value)
204}