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