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