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    pub fn header(&self) -> MessageHeader {
35        match *self {
36            Message::Short(header, _) => header,
37            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    pub fn extend_payload(&self) -> [u8; LONG_REPORT_LENGTH - 3] {
45        match *self {
46            Message::Short(_, payload) => {
47                let mut data = [0; LONG_REPORT_LENGTH - 3];
48                data[..SHORT_REPORT_LENGTH - 3].copy_from_slice(&payload);
49                data
50            }
51            Message::Long(_, payload) => payload,
52        }
53    }
54}
55
56impl From<HidppMessage> for Message {
57    fn from(msg: HidppMessage) -> Self {
58        match msg {
59            HidppMessage::Short(payload) => Message::Short(
60                MessageHeader {
61                    device_index: payload[0],
62                    sub_id: payload[1],
63                },
64                payload[2..].try_into().unwrap(),
65            ),
66            HidppMessage::Long(payload) => Message::Long(
67                MessageHeader {
68                    device_index: payload[0],
69                    sub_id: payload[1],
70                },
71                payload[2..].try_into().unwrap(),
72            ),
73        }
74    }
75}
76
77impl From<Message> for HidppMessage {
78    fn from(msg: Message) -> Self {
79        match msg {
80            Message::Short(header, payload) => {
81                let mut data = [0u8; SHORT_REPORT_LENGTH - 1];
82                data[0] = header.device_index;
83                data[1] = header.sub_id;
84                data[2..].copy_from_slice(&payload);
85
86                HidppMessage::Short(data)
87            }
88            Message::Long(header, payload) => {
89                let mut data = [0u8; LONG_REPORT_LENGTH - 1];
90                data[0] = header.device_index;
91                data[1] = header.sub_id;
92                data[2..].copy_from_slice(&payload);
93
94                HidppMessage::Long(data)
95            }
96        }
97    }
98}
99
100fn is_rap_response(device: u8, msg_type: MessageType, address: u8, msg: &HidppMessage) -> bool {
101    let raw: [u8; 4] = match msg {
102        HidppMessage::Short(d) => d[..4].try_into().unwrap(),
103        HidppMessage::Long(d) => d[..4].try_into().unwrap(),
104    };
105
106    raw[0] == device
107        && ((raw[1] == msg_type.into() && raw[2] == address)
108            || (raw[1] == MessageType::Error.into()
109                && raw[2] == msg_type.into()
110                && raw[3] == address))
111}
112
113impl HidppChannel {
114    /// Reads the data from a short 3-byte register using HID++1.0/RAP.
115    pub async fn read_register(
116        &self,
117        device: u8,
118        address: u8,
119        parameters: [u8; 3],
120    ) -> Result<[u8; 3], Hidpp10Error> {
121        let mut data = [address, 0x00, 0x00, 0x00];
122        data[1..].copy_from_slice(&parameters);
123
124        let response = Message::from(
125            self.send(
126                Message::Short(
127                    MessageHeader {
128                        device_index: device,
129                        sub_id: MessageType::GetRegister.into(),
130                    },
131                    data,
132                )
133                .into(),
134                move |raw| is_rap_response(device, MessageType::GetRegister, address, raw),
135            )
136            .await?,
137        );
138
139        let payload = response.extend_payload();
140
141        if response.header().sub_id == MessageType::Error.into() {
142            let err =
143                ErrorType::try_from(payload[2]).map_err(|_| Hidpp10Error::UnsupportedResponse)?;
144
145            return Err(Hidpp10Error::RegisterAccess(err));
146        }
147
148        Ok(payload[1..=3].try_into().unwrap())
149    }
150
151    /// Writes data to a short 3-byte register using HID++1.0/RAP.
152    pub async fn write_register(
153        &self,
154        device: u8,
155        address: u8,
156        payload: [u8; 3],
157    ) -> Result<(), Hidpp10Error> {
158        let mut data = [address, 0x00, 0x00, 0x00];
159        data[1..].copy_from_slice(&payload);
160
161        let response = Message::from(
162            self.send(
163                Message::Short(
164                    MessageHeader {
165                        device_index: device,
166                        sub_id: MessageType::SetRegister.into(),
167                    },
168                    data,
169                )
170                .into(),
171                move |raw| is_rap_response(device, MessageType::SetRegister, address, raw),
172            )
173            .await?,
174        );
175
176        if response.header().sub_id == MessageType::Error.into() {
177            let err = ErrorType::try_from(response.extend_payload()[2])
178                .map_err(|_| Hidpp10Error::UnsupportedResponse)?;
179
180            return Err(Hidpp10Error::RegisterAccess(err));
181        }
182
183        Ok(())
184    }
185
186    /// Reads the data from a long 16-byte register using HID++1.0/RAP.
187    pub async fn read_long_register(
188        &self,
189        device: u8,
190        address: u8,
191        parameters: [u8; 3],
192    ) -> Result<[u8; 16], Hidpp10Error> {
193        let mut data = [address, 0x00, 0x00, 0x00];
194        data[1..].copy_from_slice(&parameters);
195
196        let response = Message::from(
197            self.send(
198                Message::Short(
199                    MessageHeader {
200                        device_index: device,
201                        sub_id: MessageType::GetLongRegister.into(),
202                    },
203                    data,
204                )
205                .into(),
206                move |raw| is_rap_response(device, MessageType::GetLongRegister, address, raw),
207            )
208            .await?,
209        );
210
211        let payload = response.extend_payload();
212
213        if response.header().sub_id == MessageType::Error.into() {
214            let err =
215                ErrorType::try_from(payload[2]).map_err(|_| Hidpp10Error::UnsupportedResponse)?;
216
217            return Err(Hidpp10Error::RegisterAccess(err));
218        }
219
220        Ok(payload[1..=16].try_into().unwrap())
221    }
222
223    /// Writes data to a long 16-byte register using HID++1.0/RAP.
224    pub async fn write_long_register(
225        &self,
226        device: u8,
227        address: u8,
228        payload: [u8; 16],
229    ) -> Result<(), Hidpp10Error> {
230        let mut data = [0u8; 17];
231        data[0] = address;
232        data[1..].copy_from_slice(&payload);
233
234        let response = Message::from(
235            self.send(
236                Message::Long(
237                    MessageHeader {
238                        device_index: device,
239                        sub_id: MessageType::SetLongRegister.into(),
240                    },
241                    data,
242                )
243                .into(),
244                move |raw| is_rap_response(device, MessageType::SetLongRegister, address, raw),
245            )
246            .await?,
247        );
248
249        if response.header().sub_id == MessageType::Error.into() {
250            let err = ErrorType::try_from(response.extend_payload()[2])
251                .map_err(|_| Hidpp10Error::UnsupportedResponse)?;
252
253            return Err(Hidpp10Error::RegisterAccess(err));
254        }
255
256        Ok(())
257    }
258}
259
260/// Represents a globally defined sub ID of a HID++1.0 message.
261///
262/// This enum only includes sub IDs that are defined globally across all
263/// devices. Most devices (e.g. the Unifying Receiver) define additional sub IDs
264/// specific to their functionality.
265#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, IntoPrimitive, TryFromPrimitive)]
266#[cfg_attr(feature = "serde", derive(serde::Serialize))]
267#[non_exhaustive]
268#[repr(u8)]
269pub enum MessageType {
270    /// Used to set a 3-byte register value. A sent message of this type is
271    /// usually responded with a response message of the same type (or
272    /// [`Self::Error`]).
273    SetRegister = 0x80,
274
275    /// Used to retrieve a 3-byte register value. A sent message of this type is
276    /// usually responded with a response message of the same type (or
277    /// [`Self::Error`]).
278    GetRegister = 0x81,
279
280    /// Used to set a 16-byte register value. A sent message of this type is
281    /// usually responded with a response message of the same type (or
282    /// [`Self::Error`]).
283    SetLongRegister = 0x82,
284
285    /// Used to retrieve a 16-byte register value. A sent message of this type
286    /// is usually responded with a response message of the same type (or
287    /// [`Self::Error`]).
288    GetLongRegister = 0x83,
289
290    /// Used to indicate an error response. The error code usually included in
291    /// the message can be mapped using [`ErrorType::try_from`].
292    Error = 0x8f,
293}
294
295/// Represents the type of an error a HID++1.0 device returns as part of a
296/// message with the [`MessageType::Error`] type.
297#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, IntoPrimitive, TryFromPrimitive)]
298#[cfg_attr(feature = "serde", derive(serde::Serialize))]
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}