Skip to main content

hid_decode/
lib.rs

1//! # HID report descriptor decoding utilities
2//!
3//! This library can perform text decoding of USB HID report descriptors.
4//!
5//! It does not perform stateful functional decoding, so it can't (yet)
6//! determine the size and layout of HID reports.
7//!
8//! See [Device Class Definition for Human Interface Devices](https://www.usb.org/document-library/device-class-definition-hid-111)
9//! for a detailed description of HID report descriptors.
10//!
11//! # Example
12//! ```rust
13//! # // This descriptor was generated by an example in the `hid-descriptor` crate sources.
14//! # static GAMEPAD_DESCRIPTOR: &[u8] = include_bytes!("../tests/gamepad.bin");
15//! let mut output = Vec::new();
16//! hid_decode::decode(&mut output, GAMEPAD_DESCRIPTOR.iter().copied()).expect("write error");
17//! let output = String::try_from(output).expect("non-UTF8 output");
18//! assert_eq!(
19//!     output.lines().collect::<Vec<_>>(),
20//!     [
21//!         "Usage Page: GenericDesktop",
22//!         "Usage: Gamepad",
23//!         "Collection: Application",
24//!         "Usage Page: Button",
25//!         "Usage Minimum: 1",
26//!         "Usage Maximum: 8",
27//!         "Logical Minimum: 0",
28//!         "Logical Maximum: 1",
29//!         "Report Size: 1",
30//!         "Report Count: 8",
31//!         "Input: Data Variable Absolute No-Wrap Linear Preferred-State No-Null-Position Bit-Field",
32//!         "EndCollection",
33//!     ]
34//! );
35//! ```
36
37#![warn(clippy::print_stderr, clippy::print_stdout, clippy::dbg_macro)]
38#![warn(clippy::todo)]
39#![warn(missing_docs)]
40
41use std::io::Write;
42
43use hid_types::encoding::{Size, TagTypeSize, TypeBits};
44use hid_types::hid::{CollectionType, InputFlags, IoFlags, OutputFeatureFlags, Unit};
45use hid_types::id::tag::{GlobalItem, LocalItem, MainItem};
46use hid_types::id::usage::UsagePage;
47use hid_types::item::usage::{ExtendedUsage, Usage};
48use hid_types::item::{Global, Item, Local, LongItem, Main};
49
50/// Decode HID descriptor bytes as text.
51///
52/// The output text will be written to the `writer`.
53pub fn decode<W, I>(writer: W, bytes: I) -> std::io::Result<()>
54where
55    W: Write,
56    I: IntoIterator<Item = u8>,
57{
58    decode_with_options(writer, bytes, Verbosity::default())
59}
60
61/// Decode HID descriptor bytes as text.
62///
63/// The output text will be written to the `writer`.
64/// Use `verbosity` to select the level of detail.
65pub fn decode_with_options<W, I>(
66    mut writer: W,
67    bytes: I,
68    verbosity: Verbosity,
69) -> std::io::Result<()>
70where
71    W: Write,
72    I: IntoIterator<Item = u8>,
73{
74    let mut iter = bytes.into_iter();
75    let mut decoder = DecoderContext::new(verbosity);
76    loop {
77        match decoder.decode_one(&mut iter) {
78            Ok(Some(item)) => {
79                if decoder.verbosity.display_tag_type {
80                    write!(writer, "{:9}", item.type_note())?;
81                }
82                writeln!(writer, "{item}")?;
83            }
84            Ok(None) => break,
85            Err(_e) => return Err(std::io::Error::other("decoding failed")),
86        }
87    }
88    Ok(())
89}
90
91/// Configuration for decoding descriptors to text.
92#[derive(Clone, Default)]
93pub struct Verbosity {
94    /// Display the item tag types, i.e. Main, Global, Local.
95    pub display_tag_type: bool,
96}
97
98/// HID Decoder state.
99#[derive(Default)]
100pub struct DecoderContext {
101    /// Decoder verbosity.
102    verbosity: Verbosity,
103    /// The current usage page.
104    usage_page: Option<UsagePage>,
105}
106
107impl DecoderContext {
108    fn new(verbosity: Verbosity) -> Self {
109        Self {
110            verbosity,
111            ..Default::default()
112        }
113    }
114
115    /// Decode one item from its encoded bytes.
116    ///
117    /// This will consume a variable number of bytes from the input iterator.
118    pub fn decode_one<Iter>(&mut self, iter: &mut Iter) -> Result<Option<Item>, LengthError>
119    where
120        Iter: Iterator<Item = u8>,
121    {
122        let Some(tag) = iter.next() else {
123            // End of input iterator
124            return Ok(None);
125        };
126        let tag = TagTypeSize::from_bits(tag);
127
128        let more = match tag.size() {
129            Size::Short(size_bytes) => {
130                let iter_remaining = iter.take(size_bytes);
131                iter_remaining.collect::<Vec<_>>()
132            }
133            Size::Long => {
134                let (Some(size), Some(tag)) = (iter.next(), iter.next()) else {
135                    return Err(LengthError::Truncated);
136                };
137                let iter_payload = iter.take(usize::from(size));
138                let data = iter_payload.collect::<Vec<_>>();
139                let item = Item::Long(LongItem { tag, data });
140                return Ok(Some(item));
141            }
142        };
143
144        match tag.ty() {
145            TypeBits::Main => {
146                let item = MainItem::from(tag.tag());
147                let decoded = match item {
148                    MainItem::Input => {
149                        let data = slice_to_u32(&more)?;
150                        let flags = InputFlags(IoFlags::from(data));
151                        Main::Input(flags)
152                    }
153                    MainItem::Output => {
154                        let data = slice_to_u32(&more)?;
155                        let flags = OutputFeatureFlags(IoFlags::from(data));
156                        Main::Output(flags)
157                    }
158                    MainItem::Feature => {
159                        let data = slice_to_u32(&more)?;
160                        let flags = OutputFeatureFlags(IoFlags::from(data));
161                        Main::Feature(flags)
162                    }
163                    MainItem::Collection => {
164                        assert_eq!(more.len(), 1);
165                        let coll_type = CollectionType::from_integer(more[0]);
166                        Main::Collection(coll_type)
167                    }
168                    MainItem::EndCollection => {
169                        assert!(more.is_empty());
170                        Main::EndCollection
171                    }
172                    MainItem::Reserved => Main::Reserved(tag.tag()),
173                };
174                Ok(Some(Item::Main(decoded)))
175            }
176            TypeBits::Global => {
177                let item = GlobalItem::from(tag.tag());
178                let decoded = match item {
179                    GlobalItem::UsagePage => {
180                        let page_number = slice_to_u16(&more)?;
181                        let usage_page = UsagePage::from_integer(page_number);
182                        self.usage_page = Some(usage_page);
183                        Global::UsagePage(usage_page)
184                    }
185                    GlobalItem::LogicalMinimum => {
186                        let num = slice_to_i32(&more)?;
187                        Global::LogicalMinimum(num)
188                    }
189                    GlobalItem::LogicalMaximum => {
190                        let num = slice_to_i32(&more)?;
191                        Global::LogicalMaximum(num)
192                    }
193                    GlobalItem::PhysicalMinimum => {
194                        let num = slice_to_i32(&more)?;
195                        Global::PhysicalMinimum(num)
196                    }
197                    GlobalItem::PhysicalMaximum => {
198                        let num = slice_to_i32(&more)?;
199                        Global::PhysicalMaximum(num)
200                    }
201                    GlobalItem::UnitExponent => {
202                        let num = slice_to_i32(&more)?;
203                        Global::UnitExponent(num)
204                    }
205                    GlobalItem::ReportSize => {
206                        let num = slice_to_u32(&more)?;
207                        Global::ReportSize(num)
208                    }
209                    GlobalItem::ReportId => {
210                        let num = slice_to_u32(&more)?;
211                        Global::ReportId(num)
212                    }
213                    GlobalItem::ReportCount => {
214                        let num = slice_to_u32(&more)?;
215                        Global::ReportCount(num)
216                    }
217                    GlobalItem::Unit => {
218                        let num = slice_to_u32(&more)?;
219                        let unit = Unit::from_integer(num);
220                        Global::Unit(unit)
221                    }
222                    GlobalItem::Push => Global::Push,
223                    GlobalItem::Pop => Global::Pop,
224                    GlobalItem::Reserved => Global::Reserved(tag.tag()),
225                };
226                Ok(Some(Item::Global(decoded)))
227            }
228            TypeBits::Local => {
229                let item = LocalItem::from(tag.tag());
230                let decoded = match item {
231                    LocalItem::Usage => {
232                        // If the payload is 1 or 2 bytes, we use the
233                        // existing page number. If the payload is 4 bytes, it
234                        // contains both the page number and page id.
235                        match slice_to_u16(&more) {
236                            Ok(page_id) => match self.usage_page {
237                                Some(page) => Local::Usage(Usage::new(page, page_id)),
238
239                                None => Local::Usage(Usage::without_page(page_id)),
240                            },
241                            Err(LengthError::DataTooBig) => {
242                                let value = slice_to_u32(&more)?;
243                                Local::ExtendedUsage(ExtendedUsage::from_u32(value))
244                            }
245                            Err(e) => return Err(e),
246                        }
247                    }
248                    LocalItem::UsageMinimum => {
249                        let num = slice_to_u32(&more)?;
250                        Local::UsageMinimum(num)
251                    }
252                    LocalItem::UsageMaximum => {
253                        let num = slice_to_u32(&more)?;
254                        Local::UsageMaximum(num)
255                    }
256                    LocalItem::DesignatorIndex => {
257                        let num = slice_to_u32(&more)?;
258                        Local::DesignatorIndex(num)
259                    }
260                    LocalItem::DesignatorMinimum => {
261                        let num = slice_to_u32(&more)?;
262                        Local::DesignatorMinimum(num)
263                    }
264                    LocalItem::DesignatorMaximum => {
265                        let num = slice_to_u32(&more)?;
266                        Local::DesignatorMaximum(num)
267                    }
268                    LocalItem::StringIndex => {
269                        let num = slice_to_u32(&more)?;
270                        Local::StringIndex(num)
271                    }
272                    LocalItem::StringMinimum => {
273                        let num = slice_to_u32(&more)?;
274                        Local::StringMinimum(num)
275                    }
276                    LocalItem::StringMaximum => {
277                        let num = slice_to_u32(&more)?;
278                        Local::StringMaximum(num)
279                    }
280                    LocalItem::Delimiter => {
281                        let num = slice_to_u8(&more)?;
282                        let param = match num {
283                            0 => false,
284                            1 => true,
285                            _ => return Err(LengthError::DataTooBig),
286                        };
287                        Local::Delimiter(param)
288                    }
289                    LocalItem::Reserved => Local::Reserved(tag.tag()),
290                };
291                Ok(Some(Item::Local(decoded)))
292            }
293            TypeBits::Reserved => {
294                let decoded = Item::Reserved(tag.ty().into_bits());
295                Ok(Some(decoded))
296            }
297        }
298    }
299}
300
301/// An error that resulted from improper item encoding.
302#[derive(Clone, Debug)]
303pub enum LengthError {
304    /// The data length was too big for this item.
305    DataTooBig,
306    /// A data length of zero is not allowed in this item.
307    ZeroLength,
308    /// Ran out of bytes trying to decode this item.
309    Truncated,
310}
311
312fn slice_to_u8(slice: &[u8]) -> Result<u8, LengthError> {
313    match slice.len() {
314        0 => Err(LengthError::ZeroLength),
315        1 => Ok(slice[0]),
316        _ => Err(LengthError::DataTooBig),
317    }
318}
319
320fn slice_to_u16(slice: &[u8]) -> Result<u16, LengthError> {
321    match slice.len() {
322        0 => Err(LengthError::ZeroLength),
323        1 => Ok(slice[0] as u16),
324        2 => {
325            let ar: &[u8; 2] = slice.as_array().unwrap();
326            Ok(u16::from_le_bytes(*ar))
327        }
328        _ => Err(LengthError::DataTooBig),
329    }
330}
331
332fn slice_to_u32(slice: &[u8]) -> Result<u32, LengthError> {
333    match slice.len() {
334        0 => Err(LengthError::ZeroLength),
335        1 => Ok(slice[0] as u32),
336        2 => {
337            let ar: &[u8; 2] = slice.as_array().unwrap();
338            let value = u16::from_le_bytes(*ar);
339            Ok(value as u32)
340        }
341        3 => Err(LengthError::Truncated),
342        4 => {
343            let ar: &[u8; 4] = slice.as_array().unwrap();
344            Ok(u32::from_le_bytes(*ar))
345        }
346        _ => Err(LengthError::DataTooBig),
347    }
348}
349
350fn slice_to_i32(slice: &[u8]) -> Result<i32, LengthError> {
351    match slice.len() {
352        0 => Err(LengthError::ZeroLength),
353        1 => Ok(slice[0] as i8 as i32),
354        2 => {
355            let ar: &[u8; 2] = slice.as_array().unwrap();
356            let value = i16::from_le_bytes(*ar);
357            Ok(value as i32)
358        }
359        3 => Err(LengthError::Truncated),
360        4 => {
361            let ar: &[u8; 4] = slice.as_array().unwrap();
362            Ok(i32::from_le_bytes(*ar))
363        }
364        _ => Err(LengthError::DataTooBig),
365    }
366}
367
368#[cfg(test)]
369mod tests {
370    use super::*;
371
372    #[test]
373    fn test_integer_decoding() {
374        assert_eq!(slice_to_u8(&[0xFF]).unwrap(), 0xFF);
375        slice_to_u8(&[]).unwrap_err();
376        slice_to_u8(&[0xFF, 0xFF]).unwrap_err();
377
378        assert_eq!(slice_to_u16(&[0xFF]).unwrap(), 0xFF);
379        assert_eq!(slice_to_u16(&[0xFF, 0xFF]).unwrap(), 0xFFFF);
380        assert_eq!(slice_to_u16(&[1, 2]).unwrap(), 0x0201);
381        slice_to_u16(&[]).unwrap_err();
382        slice_to_u16(&[0xFF; 3]).unwrap_err();
383        slice_to_u16(&[0xFF; 4]).unwrap_err();
384
385        assert_eq!(slice_to_u32(&[0xFF]).unwrap(), 0xFF);
386        assert_eq!(slice_to_u32(&[0xFF, 0xFF]).unwrap(), 0xFFFF);
387        assert_eq!(slice_to_u32(&[1, 2]).unwrap(), 0x0201);
388        assert_eq!(slice_to_u32(&[0xFF; 4]).unwrap(), 0xFFFFFFFF);
389        assert_eq!(slice_to_u32(&[1, 2, 3, 4]).unwrap(), 0x04030201);
390        slice_to_u32(&[]).unwrap_err();
391        slice_to_u32(&[0xFF; 3]).unwrap_err();
392        slice_to_u32(&[0xFF; 5]).unwrap_err();
393
394        assert_eq!(slice_to_i32(&[0]).unwrap(), 0);
395        assert_eq!(slice_to_i32(&[0, 0]).unwrap(), 0);
396        assert_eq!(slice_to_i32(&[1, 2]).unwrap(), 0x0201);
397        assert_eq!(slice_to_i32(&[0; 4]).unwrap(), 0);
398        assert_eq!(slice_to_i32(&[1, 2, 3, 4]).unwrap(), 0x04030201);
399        assert_eq!(slice_to_i32(&[0xFF]).unwrap(), -1);
400        assert_eq!(slice_to_i32(&[0xFF, 0xFF]).unwrap(), -1);
401        assert_eq!(slice_to_i32(&[0xFF; 4]).unwrap(), -1);
402        slice_to_i32(&[]).unwrap_err();
403        slice_to_i32(&[0xFF; 3]).unwrap_err();
404        slice_to_i32(&[0xFF; 5]).unwrap_err();
405    }
406}