Skip to main content

async_hid/backend/hidraw/
descriptor.rs

1use std::fs::File;
2use std::io::{Cursor, Read, Seek, SeekFrom};
3use std::path::Path;
4
5use crate::HidResult;
6
7#[derive(Default)]
8pub struct HidrawReportDescriptor(Vec<u8>);
9
10impl HidrawReportDescriptor {
11    /// Open and parse given the "base" sysfs of the device
12    pub fn from_syspath(syspath: &Path) -> HidResult<Self> {
13        let path = syspath.join("device/report_descriptor");
14        let mut f = File::open(path)?;
15        let mut buf = Vec::new();
16        f.read_to_end(&mut buf)?;
17
18        Ok(HidrawReportDescriptor(buf))
19    }
20
21    /// Create a descriptor from a slice
22    ///
23    /// It returns an error if the value slice is too large for it to be a HID
24    /// descriptor
25    #[cfg_attr(not(test), allow(dead_code))]
26    pub fn from_slice(value: &[u8]) -> HidResult<Self> {
27        Ok(HidrawReportDescriptor(value.to_vec()))
28    }
29
30    pub fn usages(&self) -> impl Iterator<Item = (u16, u16)> + '_ {
31        UsageIterator {
32            usage_page: 0,
33            cursor: Cursor::new(&self.0)
34        }
35    }
36}
37
38/// Iterates over the values in a HidrawReportDescriptor
39struct UsageIterator<'a> {
40    usage_page: u16,
41    cursor: Cursor<&'a Vec<u8>>
42}
43
44impl Iterator for UsageIterator<'_> {
45    type Item = (u16, u16);
46
47    fn next(&mut self) -> Option<Self::Item> {
48        let (usage_page, page) = next_hid_usage(&mut self.cursor, self.usage_page)?;
49
50        self.usage_page = usage_page;
51        Some((usage_page, page))
52    }
53}
54
55// This comes from hidapi which apparently comes from Apple's implementation of
56// this
57fn next_hid_usage(cursor: &mut Cursor<&Vec<u8>>, mut usage_page: u16) -> Option<(u16, u16)> {
58    let mut usage = None;
59    let mut usage_pair = None;
60    let initial = cursor.position() == 0;
61
62    while let Some(Ok(key)) = cursor.bytes().next() {
63        // The amount to skip is calculated based off of the start of the
64        // iteration so we need to keep track of that.
65        let position = cursor.position() - 1;
66        let key_cmd = key & 0xfc;
67
68        let (data_len, key_size) = hid_item_size(key, cursor)?;
69
70        match key_cmd {
71            // Usage Page 6.2.2.7 (Global)
72            0x4 => {
73                usage_page = match hid_report_bytes(cursor, data_len) {
74                    Ok(v) => v as u16,
75                    Err(_) => break,
76                }
77            }
78            // Usage 6.2.2.8 (Local)
79            0x8 => {
80                usage = match hid_report_bytes(cursor, data_len) {
81                    Ok(v) => Some(v as u16),
82                    Err(_) => break,
83                }
84            }
85            // Collection 6.2.2.4 (Main)
86            0xa0 => {
87                // Usage is a Local Item, unset it
88                if let Some(u) = usage.take() {
89                    usage_pair = Some((usage_page, u))
90                }
91            }
92            // Input 6.2.2.4 (Main)
93            0x80 |
94            // Output 6.2.2.4 (Main)
95            0x90 |
96            // Feature 6.2.2.4 (Main)
97            0xb0 |
98            // End Collection 6.2.2.4 (Main)
99            0xc0  =>  {
100                // Usage is a Local Item, unset it
101                usage.take();
102            }
103            _ => {}
104        }
105
106        if cursor
107            .seek(SeekFrom::Start(position + (data_len + key_size) as u64))
108            .is_err()
109        {
110            return None;
111        }
112
113        if let Some((usage_page, usage)) = usage_pair {
114            return Some((usage_page, usage));
115        }
116    }
117
118    if let (true, Some(usage)) = (initial, usage) {
119        return Some((usage_page, usage));
120    }
121
122    None
123}
124
125/// Gets the size of the HID item at the given position
126///
127/// Returns data_len and key_size when successful
128fn hid_item_size(key: u8, cursor: &mut Cursor<&Vec<u8>>) -> Option<(usize, usize)> {
129    // Long Item. Next byte contains the length of the data section.
130    if (key & 0xf0) == 0xf0 {
131        if let Some(Ok(len)) = cursor.bytes().next() {
132            return Some((len.into(), 3));
133        }
134
135        // Malformed report
136        return None;
137    }
138
139    // Short Item. Bottom two bits contains the size code
140    match key & 0x03 {
141        v @ 0..=2 => Some((v.into(), 1)),
142        3 => Some((4, 1)),
143        _ => unreachable!() // & 0x03 means this can't happen
144    }
145}
146
147/// Get the bytes from a HID report descriptor
148///
149/// Must only be called with `num_bytes` 0, 1, 2 or 4.
150fn hid_report_bytes(cursor: &mut Cursor<&Vec<u8>>, num_bytes: usize) -> HidResult<u32> {
151    let mut bytes: [u8; 4] = [0; 4];
152    cursor.read_exact(&mut bytes[..num_bytes])?;
153
154    Ok(u32::from_le_bytes(bytes))
155}