Skip to main content

hidpp/protocol/
v10.rs

1//! Implements functionality specific to HID++1.0.
2
3use num_enum::{IntoPrimitive, TryFromPrimitive};
4use thiserror::Error;
5
6use crate::channel::{
7    ChannelError, HidppChannel, HidppMessage, LONG_REPORT_LENGTH, SHORT_REPORT_LENGTH,
8};
9
10/// Represents the header that every [`HidppMessage`] of HID++1.0 starts with.
11#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
12#[cfg_attr(feature = "serde", derive(serde::Serialize))]
13pub struct MessageHeader {
14    /// The index of the device involved in the communication.
15    pub device_index: u8,
16
17    /// The sub ID of the message.
18    pub sub_id: u8,
19}
20
21/// Represents a HID++1.0 message.
22#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
23#[cfg_attr(feature = "serde", derive(serde::Serialize))]
24pub enum Message {
25    /// Represents a short HID++1.0 message with 4 bytes of payload.
26    Short(MessageHeader, [u8; SHORT_REPORT_LENGTH - 3]),
27
28    /// Represents a long HID++1.0 message with 17 bytes of payload.
29    Long(MessageHeader, [u8; LONG_REPORT_LENGTH - 3]),
30}
31
32impl Message {
33    /// Extracts the header of the message.
34    #[must_use]
35    pub fn header(&self) -> MessageHeader {
36        match *self {
37            Message::Short(header, _) | Message::Long(header, _) => header,
38        }
39    }
40
41    /// Extracts the payload of the message and fits it into an array capable of
42    /// containing the longest possible payload, filling the rest up with
43    /// zeroes.
44    #[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    /// Reads the data from a short 3-byte register using HID++1.0/RAP.
122    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(&parameters);
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    /// Writes data to a short 3-byte register using HID++1.0/RAP.
160    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    /// Reads the data from a long 16-byte register using HID++1.0/RAP.
195    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(&parameters);
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    /// Writes data to a long 16-byte register using HID++1.0/RAP.
233    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/// Represents a globally defined sub ID of a HID++1.0 message.
270///
271/// This enum only includes sub IDs that are defined globally across all
272/// devices. Most devices (e.g. the Unifying Receiver) define additional sub IDs
273/// specific to their functionality.
274#[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    /// Used to set a 3-byte register value. A sent message of this type is
280    /// usually responded with a response message of the same type (or
281    /// [`Self::Error`]).
282    SetRegister = 0x80,
283
284    /// Used to retrieve a 3-byte register value. A sent message of this type is
285    /// usually responded with a response message of the same type (or
286    /// [`Self::Error`]).
287    GetRegister = 0x81,
288
289    /// Used to set a 16-byte register value. A sent message of this type is
290    /// usually responded with a response message of the same type (or
291    /// [`Self::Error`]).
292    SetLongRegister = 0x82,
293
294    /// Used to retrieve a 16-byte register value. A sent message of this type
295    /// is usually responded with a response message of the same type (or
296    /// [`Self::Error`]).
297    GetLongRegister = 0x83,
298
299    /// Used to indicate an error response. The error code usually included in
300    /// the message can be mapped using [`ErrorType::try_from`].
301    Error = 0x8f,
302}
303
304/// Represents the type of an error a HID++1.0 device returns as part of a
305/// message with the [`MessageType::Error`] type.
306#[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    /// No error.
312    Success = 0x00,
313
314    /// The sub ID of a sent message is invalid.
315    InvalidSubId = 0x01,
316
317    /// The address included in a sent message is invalid.
318    InvalidAddress = 0x02,
319
320    /// The value included in a sent message is invalid.
321    InvalidValue = 0x03,
322
323    /// A connection request failed on the receiver's side.
324    ConnectFail = 0x04,
325
326    /// The receiver indicates that too many devices are connected to it.
327    TooManyDevices = 0x05,
328
329    /// The reciever indicates that something already exists. This error is not
330    /// further documented, please let me know what it means.
331    AlreadyExists = 0x06,
332
333    /// The receiver is currently handling a downstream (to device) message and
334    /// cannot process a second one.
335    Busy = 0x07,
336
337    /// Trying to send a message to a device (device index) where there is no
338    /// device paired.
339    UnknownDevice = 0x08,
340
341    /// This error is returned by the receiver when a HID++ command has been
342    /// sent to a device that is in disconnected mode. When a device is in
343    /// disconnected mode it cannot receive commands from the host until it
344    /// reconnects. A device reconnects when the user interacts with it. In most
345    /// cases, a device disconnects after several minutes of inactivity.
346    ResourceError = 0x09,
347
348    /// A sent request is not available in the current context.
349    RequestUnavailable = 0x0a,
350
351    /// A request parameter has an unsupported value.
352    InvalidParamValue = 0x0b,
353
354    /// The PIN code of a device was wrong.
355    WrongPinCode = 0x0c,
356}
357
358/// Represents an error that may occur when accessing registers using HID++1.0.
359#[derive(Debug, Error)]
360#[non_exhaustive]
361pub enum Hidpp10Error {
362    /// Indicates that an error occurred while communicating across the HID++
363    /// channel.
364    #[error("the HID++ channel returned an error")]
365    Channel(#[from] ChannelError),
366
367    /// Indicates that a register access failed.
368    #[error("a HID++1.0 register access resulted in an error")]
369    RegisterAccess(ErrorType),
370
371    /// Indicates that a received response is not fully supported.
372    #[error("the received response from the device is (partly) unsupported")]
373    UnsupportedResponse,
374}