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(
119 device: u8,
120 msg_type: MessageType,
121 address: u8,
122 echo: Option<u8>,
123 msg: &HidppMessage,
124) -> bool {
125 let raw: [u8; 4] = match msg {
126 HidppMessage::Short(d) => [d[0], d[1], d[2], d[3]],
127 HidppMessage::Long(d) => [d[0], d[1], d[2], d[3]],
128 };
129
130 raw[0] == device
131 && ((raw[1] == msg_type.into()
132 && raw[2] == address
133 && echo.is_none_or(|echo| raw[3] == echo))
134 || (raw[1] == MessageType::Error.into()
135 && raw[2] == msg_type.into()
136 && raw[3] == address))
137}
138
139impl HidppChannel {
140 pub async fn read_register(
142 &self,
143 device: u8,
144 address: u8,
145 parameters: [u8; 3],
146 ) -> Result<[u8; 3], Hidpp10Error> {
147 let mut data = [address, 0x00, 0x00, 0x00];
148 data[1..].copy_from_slice(¶meters);
149
150 let response = Message::from(
151 self.send(
152 Message::Short(
153 MessageHeader {
154 device_index: device,
155 sub_id: MessageType::GetRegister.into(),
156 },
157 data,
158 )
159 .into(),
160 move |raw| is_rap_response(device, MessageType::GetRegister, address, None, raw),
161 )
162 .await?,
163 );
164
165 let payload = response.extend_payload();
166
167 if response.header().sub_id == MessageType::Error.into() {
168 let err =
169 ErrorType::try_from(payload[2]).map_err(|_| Hidpp10Error::UnsupportedResponse)?;
170
171 return Err(Hidpp10Error::RegisterAccess(err));
172 }
173
174 let [_, p1, p2, p3, ..] = payload;
175 Ok([p1, p2, p3])
176 }
177
178 pub async fn write_register(
180 &self,
181 device: u8,
182 address: u8,
183 payload: [u8; 3],
184 ) -> Result<(), Hidpp10Error> {
185 let mut data = [address, 0x00, 0x00, 0x00];
186 data[1..].copy_from_slice(&payload);
187
188 let response = Message::from(
189 self.send(
190 Message::Short(
191 MessageHeader {
192 device_index: device,
193 sub_id: MessageType::SetRegister.into(),
194 },
195 data,
196 )
197 .into(),
198 move |raw| is_rap_response(device, MessageType::SetRegister, address, None, raw),
199 )
200 .await?,
201 );
202
203 if response.header().sub_id == MessageType::Error.into() {
204 let err = ErrorType::try_from(response.extend_payload()[2])
205 .map_err(|_| Hidpp10Error::UnsupportedResponse)?;
206
207 return Err(Hidpp10Error::RegisterAccess(err));
208 }
209
210 Ok(())
211 }
212
213 pub async fn read_long_register(
215 &self,
216 device: u8,
217 address: u8,
218 parameters: [u8; 3],
219 ) -> Result<[u8; 16], Hidpp10Error> {
220 self.read_long_register_matching(device, address, parameters, None)
221 .await
222 }
223
224 pub async fn read_long_sub_register(
233 &self,
234 device: u8,
235 address: u8,
236 sub_register: u8,
237 parameters: [u8; 2],
238 ) -> Result<[u8; 16], Hidpp10Error> {
239 self.read_long_register_matching(
240 device,
241 address,
242 [sub_register, parameters[0], parameters[1]],
243 Some(sub_register),
244 )
245 .await
246 }
247
248 async fn read_long_register_matching(
249 &self,
250 device: u8,
251 address: u8,
252 parameters: [u8; 3],
253 echo: Option<u8>,
254 ) -> Result<[u8; 16], Hidpp10Error> {
255 let mut data = [address, 0x00, 0x00, 0x00];
256 data[1..].copy_from_slice(¶meters);
257
258 let response = Message::from(
259 self.send(
260 Message::Short(
261 MessageHeader {
262 device_index: device,
263 sub_id: MessageType::GetLongRegister.into(),
264 },
265 data,
266 )
267 .into(),
268 move |raw| {
269 is_rap_response(device, MessageType::GetLongRegister, address, echo, raw)
270 },
271 )
272 .await?,
273 );
274
275 let payload = response.extend_payload();
276
277 if response.header().sub_id == MessageType::Error.into() {
278 let err =
279 ErrorType::try_from(payload[2]).map_err(|_| Hidpp10Error::UnsupportedResponse)?;
280
281 return Err(Hidpp10Error::RegisterAccess(err));
282 }
283
284 let [_, rest @ ..] = payload;
285 Ok(rest)
286 }
287
288 pub async fn write_long_register(
290 &self,
291 device: u8,
292 address: u8,
293 payload: [u8; 16],
294 ) -> Result<(), Hidpp10Error> {
295 let mut data = [0u8; 17];
296 data[0] = address;
297 data[1..].copy_from_slice(&payload);
298
299 let response = Message::from(
300 self.send(
301 Message::Long(
302 MessageHeader {
303 device_index: device,
304 sub_id: MessageType::SetLongRegister.into(),
305 },
306 data,
307 )
308 .into(),
309 move |raw| {
310 is_rap_response(device, MessageType::SetLongRegister, address, None, raw)
311 },
312 )
313 .await?,
314 );
315
316 if response.header().sub_id == MessageType::Error.into() {
317 let err = ErrorType::try_from(response.extend_payload()[2])
318 .map_err(|_| Hidpp10Error::UnsupportedResponse)?;
319
320 return Err(Hidpp10Error::RegisterAccess(err));
321 }
322
323 Ok(())
324 }
325}
326
327#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, IntoPrimitive, TryFromPrimitive)]
333#[cfg_attr(feature = "serde", derive(serde::Serialize))]
334#[non_exhaustive]
335#[repr(u8)]
336pub enum MessageType {
337 SetRegister = 0x80,
341
342 GetRegister = 0x81,
346
347 SetLongRegister = 0x82,
351
352 GetLongRegister = 0x83,
356
357 Error = 0x8f,
360}
361
362#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, IntoPrimitive, TryFromPrimitive)]
365#[cfg_attr(feature = "serde", derive(serde::Serialize))]
366#[non_exhaustive]
367#[repr(u8)]
368pub enum ErrorType {
369 Success = 0x00,
371
372 InvalidSubId = 0x01,
374
375 InvalidAddress = 0x02,
377
378 InvalidValue = 0x03,
380
381 ConnectFail = 0x04,
383
384 TooManyDevices = 0x05,
386
387 AlreadyExists = 0x06,
390
391 Busy = 0x07,
394
395 UnknownDevice = 0x08,
398
399 ResourceError = 0x09,
405
406 RequestUnavailable = 0x0a,
408
409 InvalidParamValue = 0x0b,
411
412 WrongPinCode = 0x0c,
414}
415
416#[derive(Debug, Error)]
418#[non_exhaustive]
419pub enum Hidpp10Error {
420 #[error("the HID++ channel returned an error")]
423 Channel(#[from] ChannelError),
424
425 #[error("a HID++1.0 register access resulted in an error")]
427 RegisterAccess(ErrorType),
428
429 #[error("the received response from the device is (partly) unsupported")]
431 UnsupportedResponse,
432}
433
434#[cfg(test)]
435mod tests {
436 use std::sync::Arc;
437
438 use futures::join;
439
440 use super::MessageType;
441 use crate::channel::{
442 HidppMessage,
443 mock::{MockRawHidChannel, channel_with_reader},
444 };
445
446 fn long_register_reply(address: u8, first: u8) -> HidppMessage {
449 let mut data = [0u8; 19];
450 data[0] = 0xff;
451 data[1] = MessageType::GetLongRegister.into();
452 data[2] = address;
453 data[3] = first;
454 data[4] = 0xaa;
455 HidppMessage::Long(data)
456 }
457
458 #[test]
459 fn sub_register_read_ignores_another_sub_registers_reply() {
460 futures::executor::block_on(async {
461 let (raw, handle) = MockRawHidChannel::new();
462 let channel = Arc::new(channel_with_reader(raw).await);
463 handle.queue_response(long_register_reply(0xb5, 0x53));
467
468 let read = channel.read_long_sub_register(0xff, 0xb5, 0x52, [0, 0]);
469 let inject = handle.send_incoming(long_register_reply(0xb5, 0x52));
470 let (result, ()) = join!(read, inject);
471
472 let data = result.expect("the reply echoing the sub-register answers the read");
473 assert_eq!(data[0], 0x52, "slot 2's read must not take slot 3's reply");
474 });
475 }
476
477 #[test]
478 fn plain_register_read_matches_on_the_header_alone() {
479 futures::executor::block_on(async {
480 let (raw, handle) = MockRawHidChannel::new();
481 let channel = Arc::new(channel_with_reader(raw).await);
482 handle.queue_response(long_register_reply(0x02, 0x00));
485
486 let data = channel
487 .read_long_register(0xff, 0x02, [0, 0, 0])
488 .await
489 .expect("a header match answers a plain register read");
490 assert_eq!(data[0], 0x00);
491 });
492 }
493}