1use num_enum::{IntoPrimitive, TryFromPrimitive};
4use thiserror::Error;
5
6use crate::channel::{
7 ChannelError, HidppChannel, HidppMessage, LONG_REPORT_LENGTH, SHORT_REPORT_LENGTH,
8};
9
10#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
12#[cfg_attr(feature = "serde", derive(serde::Serialize))]
13pub struct MessageHeader {
14 pub device_index: u8,
16
17 pub sub_id: u8,
19}
20
21#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
23#[cfg_attr(feature = "serde", derive(serde::Serialize))]
24pub enum Message {
25 Short(MessageHeader, [u8; SHORT_REPORT_LENGTH - 3]),
27
28 Long(MessageHeader, [u8; LONG_REPORT_LENGTH - 3]),
30}
31
32impl Message {
33 #[must_use]
35 pub fn header(&self) -> MessageHeader {
36 match *self {
37 Message::Short(header, _) | Message::Long(header, _) => header,
38 }
39 }
40
41 #[must_use]
45 pub fn extend_payload(&self) -> [u8; LONG_REPORT_LENGTH - 3] {
46 match *self {
47 Message::Short(_, payload) => {
48 let mut data = [0; LONG_REPORT_LENGTH - 3];
49 data[..SHORT_REPORT_LENGTH - 3].copy_from_slice(&payload);
50 data
51 }
52 Message::Long(_, payload) => payload,
53 }
54 }
55}
56
57impl From<HidppMessage> for Message {
58 fn from(msg: HidppMessage) -> Self {
59 match msg {
60 HidppMessage::Short(payload) => {
61 let [_, _, rest @ ..] = payload;
62 Message::Short(
63 MessageHeader {
64 device_index: payload[0],
65 sub_id: payload[1],
66 },
67 rest,
68 )
69 }
70 HidppMessage::Long(payload) => {
71 let [_, _, rest @ ..] = payload;
72 Message::Long(
73 MessageHeader {
74 device_index: payload[0],
75 sub_id: payload[1],
76 },
77 rest,
78 )
79 }
80 }
81 }
82}
83
84impl From<Message> for HidppMessage {
85 fn from(msg: Message) -> Self {
86 match msg {
87 Message::Short(header, payload) => {
88 let mut data = [0u8; SHORT_REPORT_LENGTH - 1];
89 data[0] = header.device_index;
90 data[1] = header.sub_id;
91 data[2..].copy_from_slice(&payload);
92
93 HidppMessage::Short(data)
94 }
95 Message::Long(header, payload) => {
96 let mut data = [0u8; LONG_REPORT_LENGTH - 1];
97 data[0] = header.device_index;
98 data[1] = header.sub_id;
99 data[2..].copy_from_slice(&payload);
100
101 HidppMessage::Long(data)
102 }
103 }
104 }
105}
106
107fn is_rap_response(device: u8, msg_type: MessageType, address: u8, msg: &HidppMessage) -> bool {
108 let raw: [u8; 4] = match msg {
109 HidppMessage::Short(d) => [d[0], d[1], d[2], d[3]],
110 HidppMessage::Long(d) => [d[0], d[1], d[2], d[3]],
111 };
112
113 raw[0] == device
114 && ((raw[1] == msg_type.into() && raw[2] == address)
115 || (raw[1] == MessageType::Error.into()
116 && raw[2] == msg_type.into()
117 && raw[3] == address))
118}
119
120impl HidppChannel {
121 pub async fn read_register(
123 &self,
124 device: u8,
125 address: u8,
126 parameters: [u8; 3],
127 ) -> Result<[u8; 3], Hidpp10Error> {
128 let mut data = [address, 0x00, 0x00, 0x00];
129 data[1..].copy_from_slice(¶meters);
130
131 let response = Message::from(
132 self.send(
133 Message::Short(
134 MessageHeader {
135 device_index: device,
136 sub_id: MessageType::GetRegister.into(),
137 },
138 data,
139 )
140 .into(),
141 move |raw| is_rap_response(device, MessageType::GetRegister, address, raw),
142 )
143 .await?,
144 );
145
146 let payload = response.extend_payload();
147
148 if response.header().sub_id == MessageType::Error.into() {
149 let err =
150 ErrorType::try_from(payload[2]).map_err(|_| Hidpp10Error::UnsupportedResponse)?;
151
152 return Err(Hidpp10Error::RegisterAccess(err));
153 }
154
155 let [_, p1, p2, p3, ..] = payload;
156 Ok([p1, p2, p3])
157 }
158
159 pub async fn write_register(
161 &self,
162 device: u8,
163 address: u8,
164 payload: [u8; 3],
165 ) -> Result<(), Hidpp10Error> {
166 let mut data = [address, 0x00, 0x00, 0x00];
167 data[1..].copy_from_slice(&payload);
168
169 let response = Message::from(
170 self.send(
171 Message::Short(
172 MessageHeader {
173 device_index: device,
174 sub_id: MessageType::SetRegister.into(),
175 },
176 data,
177 )
178 .into(),
179 move |raw| is_rap_response(device, MessageType::SetRegister, address, raw),
180 )
181 .await?,
182 );
183
184 if response.header().sub_id == MessageType::Error.into() {
185 let err = ErrorType::try_from(response.extend_payload()[2])
186 .map_err(|_| Hidpp10Error::UnsupportedResponse)?;
187
188 return Err(Hidpp10Error::RegisterAccess(err));
189 }
190
191 Ok(())
192 }
193
194 pub async fn read_long_register(
196 &self,
197 device: u8,
198 address: u8,
199 parameters: [u8; 3],
200 ) -> Result<[u8; 16], Hidpp10Error> {
201 let mut data = [address, 0x00, 0x00, 0x00];
202 data[1..].copy_from_slice(¶meters);
203
204 let response = Message::from(
205 self.send(
206 Message::Short(
207 MessageHeader {
208 device_index: device,
209 sub_id: MessageType::GetLongRegister.into(),
210 },
211 data,
212 )
213 .into(),
214 move |raw| is_rap_response(device, MessageType::GetLongRegister, address, raw),
215 )
216 .await?,
217 );
218
219 let payload = response.extend_payload();
220
221 if response.header().sub_id == MessageType::Error.into() {
222 let err =
223 ErrorType::try_from(payload[2]).map_err(|_| Hidpp10Error::UnsupportedResponse)?;
224
225 return Err(Hidpp10Error::RegisterAccess(err));
226 }
227
228 let [_, rest @ ..] = payload;
229 Ok(rest)
230 }
231
232 pub async fn write_long_register(
234 &self,
235 device: u8,
236 address: u8,
237 payload: [u8; 16],
238 ) -> Result<(), Hidpp10Error> {
239 let mut data = [0u8; 17];
240 data[0] = address;
241 data[1..].copy_from_slice(&payload);
242
243 let response = Message::from(
244 self.send(
245 Message::Long(
246 MessageHeader {
247 device_index: device,
248 sub_id: MessageType::SetLongRegister.into(),
249 },
250 data,
251 )
252 .into(),
253 move |raw| is_rap_response(device, MessageType::SetLongRegister, address, raw),
254 )
255 .await?,
256 );
257
258 if response.header().sub_id == MessageType::Error.into() {
259 let err = ErrorType::try_from(response.extend_payload()[2])
260 .map_err(|_| Hidpp10Error::UnsupportedResponse)?;
261
262 return Err(Hidpp10Error::RegisterAccess(err));
263 }
264
265 Ok(())
266 }
267}
268
269#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, IntoPrimitive, TryFromPrimitive)]
275#[cfg_attr(feature = "serde", derive(serde::Serialize))]
276#[non_exhaustive]
277#[repr(u8)]
278pub enum MessageType {
279 SetRegister = 0x80,
283
284 GetRegister = 0x81,
288
289 SetLongRegister = 0x82,
293
294 GetLongRegister = 0x83,
298
299 Error = 0x8f,
302}
303
304#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, IntoPrimitive, TryFromPrimitive)]
307#[cfg_attr(feature = "serde", derive(serde::Serialize))]
308#[non_exhaustive]
309#[repr(u8)]
310pub enum ErrorType {
311 Success = 0x00,
313
314 InvalidSubId = 0x01,
316
317 InvalidAddress = 0x02,
319
320 InvalidValue = 0x03,
322
323 ConnectFail = 0x04,
325
326 TooManyDevices = 0x05,
328
329 AlreadyExists = 0x06,
332
333 Busy = 0x07,
336
337 UnknownDevice = 0x08,
340
341 ResourceError = 0x09,
347
348 RequestUnavailable = 0x0a,
350
351 InvalidParamValue = 0x0b,
353
354 WrongPinCode = 0x0c,
356}
357
358#[derive(Debug, Error)]
360#[non_exhaustive]
361pub enum Hidpp10Error {
362 #[error("the HID++ channel returned an error")]
365 Channel(#[from] ChannelError),
366
367 #[error("a HID++1.0 register access resulted in an error")]
369 RegisterAccess(ErrorType),
370
371 #[error("the received response from the device is (partly) unsupported")]
373 UnsupportedResponse,
374}