usbd_human_interface_device/device/
mouse.rs

1//!HID mice
2use crate::usb_class::prelude::*;
3use core::default::Default;
4use fugit::ExtU32;
5use packed_struct::prelude::*;
6use usb_device::bus::UsbBus;
7use usb_device::class_prelude::UsbBusAllocator;
8
9/// HID Mouse report descriptor conforming to the Boot specification
10///
11/// This aims to be compatible with BIOS and other reduced functionality USB hosts
12///
13/// This is defined in Appendix B.2 & E.10 of [Device Class Definition for Human
14/// Interface Devices (Hid) Version 1.11](<https://www.usb.org/sites/default/files/hid1_11.pdf>)
15#[rustfmt::skip]
16pub const BOOT_MOUSE_REPORT_DESCRIPTOR: &[u8] = &[
17    0x05, 0x01, // Usage Page (Generic Desktop),
18    0x09, 0x02, // Usage (Mouse),
19    0xA1, 0x01, // Collection (Application),
20    0x09, 0x01, //   Usage (Pointer),
21    0xA1, 0x00, //   Collection (Physical),
22    0x95, 0x03, //     Report Count (3),
23    0x75, 0x01, //     Report Size (1),
24    0x05, 0x09, //     Usage Page (Buttons),
25    0x19, 0x01, //     Usage Minimum (1),
26    0x29, 0x03, //     Usage Maximum (3),
27    0x15, 0x00, //     Logical Minimum (0),
28    0x25, 0x01, //     Logical Maximum (1),
29    0x81, 0x02, //     Input (Data, Variable, Absolute),
30    0x95, 0x01, //     Report Count (1),
31    0x75, 0x05, //     Report Size (5),
32    0x81, 0x01, //     Input (Constant),
33    0x75, 0x08, //     Report Size (8),
34    0x95, 0x02, //     Report Count (2),
35    0x05, 0x01, //     Usage Page (Generic Desktop),
36    0x09, 0x30, //     Usage (X),
37    0x09, 0x31, //     Usage (Y),
38    0x15, 0x81, //     Logical Minimum (-127),
39    0x25, 0x7F, //     Logical Maximum (127),
40    0x81, 0x06, //     Input (Data, Variable, Relative),
41    0xC0, //   End Collection,
42    0xC0, // End Collection
43];
44
45#[cfg_attr(feature = "defmt", derive(defmt::Format))]
46#[derive(Clone, Copy, Debug, Eq, PartialEq, Default, PackedStruct)]
47#[packed_struct(endian = "lsb", size_bytes = "3")]
48pub struct BootMouseReport {
49    #[packed_field]
50    pub buttons: u8,
51    #[packed_field]
52    pub x: i8,
53    #[packed_field]
54    pub y: i8,
55}
56
57/// Boot compatible mouse with wheel, pan and eight buttons
58///
59/// Reference: <https://docs.microsoft.com/en-us/previous-versions/windows/hardware/design/dn613912(v=vs.85)>
60#[rustfmt::skip]
61pub const WHEEL_MOUSE_REPORT_DESCRIPTOR: &[u8] = &[
62    0x05, 0x01,        // Usage Page (Generic Desktop),
63    0x09, 0x02,        // Usage (Mouse),
64    0xA1, 0x01,        // Collection (Application),
65    0x09, 0x01,        //   Usage (Pointer),
66    0xA1, 0x00,        //   Collection (Physical),
67    0x95, 0x08,        //     Report Count (8),
68    0x75, 0x01,        //     Report Size (1),
69    0x05, 0x09,        //     Usage Page (Buttons),
70    0x19, 0x01,        //     Usage Minimum (1),
71    0x29, 0x08,        //     Usage Maximum (8),
72    0x15, 0x00,        //     Logical Minimum (0),
73    0x25, 0x01,        //     Logical Maximum (1),
74    0x81, 0x02,        //     Input (Data, Variable, Absolute),
75
76    0x75, 0x08,        //     Report Size (8),
77    0x95, 0x02,        //     Report Count (2),
78    0x05, 0x01,        //     Usage Page (Generic Desktop),
79    0x09, 0x30,        //     Usage (X),
80    0x09, 0x31,        //     Usage (Y),
81    0x15, 0x81,        //     Logical Minimum (-127),
82    0x25, 0x7F,        //     Logical Maximum (127),
83    0x81, 0x06,        //     Input (Data, Variable, Relative),
84
85    0x15, 0x81,        //     Logical Minimum (-127)
86    0x25, 0x7F,        //     Logical Maximum (127)
87    0x09, 0x38,        //     Usage (Wheel)
88    0x75, 0x08,        //     Report Size (8)
89    0x95, 0x01,        //     Report Count (1)
90    0x81, 0x06,        //     Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
91    0x05, 0x0C,        //     Usage Page (Consumer)
92    0x0A, 0x38, 0x02,  //     Usage (AC Pan)
93    0x95, 0x01,        //     Report Count (1)
94    0x81, 0x06,        //     Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
95    0xC0,              //   End Collection
96    0xC0,              // End Collection
97];
98
99#[derive(Clone, Copy, Debug, Eq, PartialEq, Default, PackedStruct)]
100#[packed_struct(endian = "lsb")]
101pub struct WheelMouseReport {
102    #[packed_field]
103    pub buttons: u8,
104    #[packed_field]
105    pub x: i8,
106    #[packed_field]
107    pub y: i8,
108    #[packed_field]
109    pub vertical_wheel: i8,
110    #[packed_field]
111    pub horizontal_wheel: i8,
112}
113
114/// Absolute mouse with wheel and eight buttons
115///
116/// Note - absolute pointer support is relatively uncommon. This has been tested on Windows 11
117/// Other operating systems may not natively support this device.
118/// 
119/// Windows only natively supports absolute pointer devices on the primary display.
120/// 
121/// Reference: <https://docs.microsoft.com/en-us/previous-versions/windows/hardware/design/dn613912(v=vs.85)>
122#[rustfmt::skip]
123pub const ABSOLUTE_WHEEL_MOUSE_REPORT_DESCRIPTOR: &[u8] = &[
124    0x05, 0x01,        // Usage Page (Generic Desktop),
125    0x09, 0x02,        // Usage (Mouse),
126    0xA1, 0x01,        // Collection (Application),
127    0x09, 0x01,        //   Usage (Pointer),
128    0xA1, 0x00,        //   Collection (Physical),
129
130    0x05, 0x09,        //     Usage Page (Buttons),
131    0x19, 0x01,        //     Usage Minimum (1),
132    0x29, 0x08,        //     Usage Maximum (8),
133    0x15, 0x00,        //     Logical Minimum (0),
134    0x25, 0x01,        //     Logical Maximum (1),
135    0x95, 0x08,        //     Report Count (8),
136    0x75, 0x01,        //     Report Size (1),
137    0x81, 0x02,        //     Input (Data, Variable, Absolute),
138
139    0x05, 0x01,        //     Usage Page (Generic Desktop),
140    0x09, 0x30,        //     Usage (X),
141    0x09, 0x31,        //     Usage (Y),
142    0x15, 0x00,        //     Logical Minimum (0),
143    0x26, 0xFF, 0x7F,  //     Logical Maximum (32767),
144    0x35, 0x00,        //     Physical Minimum (0),
145    0x46, 0xFF, 0x7F,  //     Physical Maximum (32767),
146    0x95, 0x02,        //     Report Count (2),
147    0x75, 0x10,        //     Report Size (16),
148    0x81, 0x02,        //     Input (Data, Variable, Absolute),
149
150    0x09, 0x38,        //     Usage (Wheel)
151    0x15, 0x81,        //     Logical Minimum (-127)
152    0x25, 0x7F,        //     Logical Maximum (127)
153    0x35, 0x81,        //     Physical Minimum (-127),
154    0x45, 0x7F,        //     Physical Maximum (127),
155    0x75, 0x08,        //     Report Size (8)
156    0x95, 0x01,        //     Report Count (1)
157    0x81, 0x06,        //     Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
158
159    0xC0,              //   End Collection
160    0xC0,              // End Collection
161];
162
163#[derive(Clone, Copy, Debug, Eq, PartialEq, Default, PackedStruct)]
164#[packed_struct(endian = "lsb")]
165pub struct AbsoluteWheelMouseReport {
166    #[packed_field]
167    pub buttons: u8,
168    #[packed_field]
169    pub x: u16,
170    #[packed_field]
171    pub y: u16,
172    #[packed_field]
173    pub wheel: i8,
174}
175
176pub struct BootMouse<'a, B: UsbBus> {
177    interface: Interface<'a, B, InBytes8, OutNone, ReportSingle>,
178}
179
180impl<B: UsbBus> BootMouse<'_, B> {
181    pub fn write_report(&mut self, report: &BootMouseReport) -> Result<(), UsbHidError> {
182        let data = report.pack().map_err(|_| {
183            error!("Error packing BootMouseReport");
184            UsbHidError::SerializationError
185        })?;
186        self.interface
187            .write_report(&data)
188            .map(|_| ())
189            .map_err(UsbHidError::from)
190    }
191}
192
193pub struct BootMouseConfig<'a> {
194    interface: InterfaceConfig<'a, InBytes8, OutNone, ReportSingle>,
195}
196
197impl<'a> BootMouseConfig<'a> {
198    #[must_use]
199    pub fn new(interface: InterfaceConfig<'a, InBytes8, OutNone, ReportSingle>) -> Self {
200        Self { interface }
201    }
202}
203
204impl Default for BootMouseConfig<'_> {
205    fn default() -> Self {
206        Self::new(
207            unwrap!(unwrap!(InterfaceBuilder::new(BOOT_MOUSE_REPORT_DESCRIPTOR))
208                .boot_device(InterfaceProtocol::Mouse)
209                .description("Mouse")
210                .in_endpoint(10.millis()))
211            .without_out_endpoint()
212            .build(),
213        )
214    }
215}
216
217impl<'a, B: UsbBus + 'a> UsbAllocatable<'a, B> for BootMouseConfig<'a> {
218    type Allocated = BootMouse<'a, B>;
219
220    fn allocate(self, usb_alloc: &'a UsbBusAllocator<B>) -> Self::Allocated {
221        BootMouse {
222            interface: self.interface.allocate(usb_alloc),
223        }
224    }
225}
226
227impl<'a, B: UsbBus> DeviceClass<'a> for BootMouse<'a, B> {
228    type I = Interface<'a, B, InBytes8, OutNone, ReportSingle>;
229
230    fn interface(&mut self) -> &mut Self::I {
231        &mut self.interface
232    }
233
234    fn reset(&mut self) {}
235
236    fn tick(&mut self) -> Result<(), UsbHidError> {
237        Ok(())
238    }
239}
240
241pub struct WheelMouse<'a, B: UsbBus> {
242    interface: Interface<'a, B, InBytes8, OutNone, ReportSingle>,
243}
244
245impl<B: UsbBus> WheelMouse<'_, B> {
246    pub fn write_report(&mut self, report: &WheelMouseReport) -> Result<(), UsbHidError> {
247        let data = report.pack().map_err(|_| {
248            error!("Error packing WheelMouseReport");
249            UsbHidError::SerializationError
250        })?;
251        self.interface
252            .write_report(&data)
253            .map(|_| ())
254            .map_err(UsbHidError::from)
255    }
256}
257pub struct WheelMouseConfig<'a> {
258    interface: InterfaceConfig<'a, InBytes8, OutNone, ReportSingle>,
259}
260
261impl<'a> WheelMouseConfig<'a> {
262    #[must_use]
263    pub fn new(interface: InterfaceConfig<'a, InBytes8, OutNone, ReportSingle>) -> Self {
264        Self { interface }
265    }
266}
267
268impl Default for WheelMouseConfig<'_> {
269    fn default() -> Self {
270        WheelMouseConfig::new(
271            unwrap!(
272                unwrap!(InterfaceBuilder::new(WHEEL_MOUSE_REPORT_DESCRIPTOR))
273                    .boot_device(InterfaceProtocol::Mouse)
274                    .description("Wheel Mouse")
275                    .in_endpoint(10.millis())
276            )
277            .without_out_endpoint()
278            .build(),
279        )
280    }
281}
282
283impl<'a, B: UsbBus + 'a> UsbAllocatable<'a, B> for WheelMouseConfig<'a> {
284    type Allocated = WheelMouse<'a, B>;
285
286    fn allocate(self, usb_alloc: &'a UsbBusAllocator<B>) -> Self::Allocated {
287        WheelMouse {
288            interface: self.interface.allocate(usb_alloc),
289        }
290    }
291}
292
293impl<'a, B: UsbBus> DeviceClass<'a> for WheelMouse<'a, B> {
294    type I = Interface<'a, B, InBytes8, OutNone, ReportSingle>;
295
296    fn interface(&mut self) -> &mut Self::I {
297        &mut self.interface
298    }
299
300    fn reset(&mut self) {}
301
302    fn tick(&mut self) -> Result<(), UsbHidError> {
303        Ok(())
304    }
305}
306
307pub struct AbsoluteWheelMouse<'a, B: UsbBus> {
308    interface: Interface<'a, B, InBytes8, OutNone, ReportSingle>,
309}
310
311impl<B: UsbBus> AbsoluteWheelMouse<'_, B> {
312    pub fn write_report(&mut self, report: &AbsoluteWheelMouseReport) -> Result<(), UsbHidError> {
313        let data = report.pack().map_err(|_| {
314            error!("Error packing WheelMouseReport");
315            UsbHidError::SerializationError
316        })?;
317        self.interface
318            .write_report(&data)
319            .map(|_| ())
320            .map_err(UsbHidError::from)
321    }
322}
323
324pub struct AbsoluteWheelMouseConfig<'a> {
325    interface: InterfaceConfig<'a, InBytes8, OutNone, ReportSingle>,
326}
327
328impl<'a> AbsoluteWheelMouseConfig<'a> {
329    #[must_use]
330    pub fn new(interface: InterfaceConfig<'a, InBytes8, OutNone, ReportSingle>) -> Self {
331        Self { interface }
332    }
333}
334
335impl Default for AbsoluteWheelMouseConfig<'_> {
336    fn default() -> Self {
337        AbsoluteWheelMouseConfig::new(
338            unwrap!(unwrap!(InterfaceBuilder::new(
339                ABSOLUTE_WHEEL_MOUSE_REPORT_DESCRIPTOR
340            ))
341            .description("Absolute Wheel Mouse")
342            .in_endpoint(10.millis()))
343            .without_out_endpoint()
344            .build(),
345        )
346    }
347}
348
349impl<'a, B: UsbBus + 'a> UsbAllocatable<'a, B> for AbsoluteWheelMouseConfig<'a> {
350    type Allocated = AbsoluteWheelMouse<'a, B>;
351
352    fn allocate(self, usb_alloc: &'a UsbBusAllocator<B>) -> Self::Allocated {
353        AbsoluteWheelMouse {
354            interface: self.interface.allocate(usb_alloc),
355        }
356    }
357}
358
359impl<'a, B: UsbBus> DeviceClass<'a> for AbsoluteWheelMouse<'a, B> {
360    type I = Interface<'a, B, InBytes8, OutNone, ReportSingle>;
361
362    fn interface(&mut self) -> &mut Self::I {
363        &mut self.interface
364    }
365
366    fn reset(&mut self) {}
367
368    fn tick(&mut self) -> Result<(), UsbHidError> {
369        Ok(())
370    }
371}