1use num_enum::TryFromPrimitive;
4use openlogi_hidpp_derive::Feature;
5
6use crate::{feature::FeatureEndpoint, protocol::v20::Hidpp20Error};
7
8bitflags::bitflags! {
9 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
11 #[cfg_attr(feature = "serde", derive(serde::Serialize))]
12 pub struct HostsInfoCapabilities: u8 {
13 const GET_NAME = 1 << 0;
15 const SET_NAME = 1 << 1;
17 const MOVE_HOST = 1 << 2;
19 const DELETE_HOST = 1 << 3;
21 const SET_OS_VERSION = 1 << 4;
23 }
24}
25
26bitflags::bitflags! {
27 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
29 #[cfg_attr(feature = "serde", derive(serde::Serialize))]
30 pub struct HostDescriptorCapabilities: u8 {
31 const EQUAD = 1 << 0;
33 const USB = 1 << 1;
35 const BT = 1 << 2;
37 const BLE = 1 << 3;
39 }
40}
41
42#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
44#[cfg_attr(feature = "serde", derive(serde::Serialize))]
45#[non_exhaustive]
46pub enum HostIndex {
47 Current,
49 Slot(u8),
51}
52
53impl From<HostIndex> for u8 {
54 fn from(value: HostIndex) -> Self {
55 match value {
56 HostIndex::Current => 0xff,
57 HostIndex::Slot(index) => index,
58 }
59 }
60}
61
62impl From<u8> for HostIndex {
63 fn from(value: u8) -> Self {
64 if value == 0xff {
65 Self::Current
66 } else {
67 Self::Slot(value)
68 }
69 }
70}
71
72#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, TryFromPrimitive)]
74#[cfg_attr(feature = "serde", derive(serde::Serialize))]
75#[non_exhaustive]
76#[repr(u8)]
77pub enum HostSlotStatus {
78 Empty = 0,
80 Paired = 1,
82}
83
84#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, TryFromPrimitive)]
86#[cfg_attr(feature = "serde", derive(serde::Serialize))]
87#[non_exhaustive]
88#[repr(u8)]
89pub enum HostBusType {
90 Undefined = 0,
92 Equad = 1,
94 Usb = 2,
96 Bt = 3,
98 Ble = 4,
100 BlePro = 5,
102}
103
104#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
106#[cfg_attr(feature = "serde", derive(serde::Serialize))]
107#[non_exhaustive]
108pub struct HostsInfoFeatureInfo {
109 pub capabilities: HostsInfoCapabilities,
111 pub descriptor_capabilities: HostDescriptorCapabilities,
113 pub host_count: u8,
115 pub current_host: HostIndex,
117}
118
119#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
121#[cfg_attr(feature = "serde", derive(serde::Serialize))]
122#[non_exhaustive]
123pub struct HostInfo {
124 pub host_index: HostIndex,
126 pub status: HostSlotStatus,
128 pub bus_type: HostBusType,
130 pub page_count: u8,
132 pub name_len: u8,
134 pub name_max_len: u8,
136}
137
138#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
140#[cfg_attr(feature = "serde", derive(serde::Serialize))]
141#[non_exhaustive]
142pub struct HostDescriptorPage {
143 pub host_index: HostIndex,
145 pub bus_type: HostBusType,
147 pub page_index: u8,
149 pub body: [u8; 14],
151}
152
153#[derive(Clone, Feature)]
155#[creatable(id = 0x1815, version = 2)]
156pub struct HostsInfoFeature {
157 endpoint: FeatureEndpoint,
159}
160
161impl HostsInfoFeature {
162 pub async fn get_feature_info(&self) -> Result<HostsInfoFeatureInfo, Hidpp20Error> {
164 let payload = self.endpoint.call(0, [0; 3]).await?.extend_payload();
165 Ok(HostsInfoFeatureInfo {
166 capabilities: HostsInfoCapabilities::from_bits_retain(payload[0]),
167 descriptor_capabilities: HostDescriptorCapabilities::from_bits_retain(payload[1]),
168 host_count: payload[2],
169 current_host: HostIndex::from(payload[3]),
170 })
171 }
172
173 pub async fn get_host_info(&self, host: HostIndex) -> Result<HostInfo, Hidpp20Error> {
175 let payload = self
176 .endpoint
177 .call(1, [u8::from(host), 0, 0])
178 .await?
179 .extend_payload();
180 Ok(HostInfo {
181 host_index: HostIndex::from(payload[0]),
182 status: HostSlotStatus::try_from(payload[1])
183 .map_err(|_| Hidpp20Error::UnsupportedResponse)?,
184 bus_type: HostBusType::try_from(payload[2])
185 .map_err(|_| Hidpp20Error::UnsupportedResponse)?,
186 page_count: payload[3],
187 name_len: payload[4],
188 name_max_len: payload[5],
189 })
190 }
191
192 pub async fn get_host_descriptor(
194 &self,
195 host: HostIndex,
196 page: u8,
197 ) -> Result<HostDescriptorPage, Hidpp20Error> {
198 let payload = self
199 .endpoint
200 .call(2, [u8::from(host), page, 0])
201 .await?
202 .extend_payload();
203 let mut body = [0; 14];
204 body.copy_from_slice(&payload[2..16]);
205 Ok(HostDescriptorPage {
206 host_index: HostIndex::from(payload[0]),
207 bus_type: HostBusType::try_from(payload[1] >> 4)
208 .map_err(|_| Hidpp20Error::UnsupportedResponse)?,
209 page_index: payload[1] & 0x0f,
210 body,
211 })
212 }
213}