Skip to main content

netget/server/usb/mouse/
handler.rs

1//! USB HID mouse handler
2//!
3//! The `usbip` crate ships `hid::UsbHidKeyboardHandler` but has no mouse equivalent, so this
4//! implements `UsbInterfaceHandler` directly. It is the same shape as the crate's keyboard
5//! handler: a queue of pending reports, drained one per interrupt-IN URB, with an automatic
6//! all-zero "release" report after any report that holds a button down, so a click does not
7//! stick.
8
9#[cfg(all(unix, feature = "libusb"))]
10use crate::server::usb::descriptors::{build_hid_mouse_report_descriptor, MouseReport};
11#[cfg(all(unix, feature = "libusb"))]
12use std::collections::VecDeque;
13#[cfg(all(unix, feature = "libusb"))]
14use tracing::{debug, trace, warn};
15
16/// HID descriptor type codes (HID 1.11 section 7.1).
17#[cfg(all(unix, feature = "libusb"))]
18mod hid_descriptor_type {
19    pub const HID: u8 = 0x21;
20    pub const REPORT: u8 = 0x22;
21}
22
23/// A virtual USB HID mouse.
24#[cfg(all(unix, feature = "libusb"))]
25#[derive(Debug)]
26pub struct UsbHidMouseHandler {
27    /// HID report descriptor handed to the host on GET_DESCRIPTOR(Report)
28    report_descriptor: Vec<u8>,
29    /// Reports waiting to be delivered on the next interrupt-IN URBs
30    pending_reports: VecDeque<[u8; 4]>,
31}
32
33#[cfg(all(unix, feature = "libusb"))]
34impl Default for UsbHidMouseHandler {
35    fn default() -> Self {
36        Self::new()
37    }
38}
39
40#[cfg(all(unix, feature = "libusb"))]
41impl UsbHidMouseHandler {
42    pub fn new() -> Self {
43        Self {
44            report_descriptor: build_hid_mouse_report_descriptor(),
45            pending_reports: VecDeque::new(),
46        }
47    }
48
49    /// The endpoint set a HID mouse needs: a single interrupt IN endpoint.
50    pub fn endpoints() -> Vec<usbip::UsbEndpoint> {
51        vec![usbip::UsbEndpoint {
52            address: 0x81,      // EP1 IN
53            attributes: 0x03,   // Interrupt
54            max_packet_size: 4, // buttons, dx, dy, wheel
55            interval: 10,       // 10ms polling
56        }]
57    }
58
59    /// Queue a single report.
60    pub fn queue(&mut self, report: MouseReport) {
61        trace!(
62            "USB mouse queue report: buttons={:#04x} x={} y={} wheel={}",
63            report.buttons,
64            report.x,
65            report.y,
66            report.wheel
67        );
68        self.pending_reports.push_back(report.to_bytes());
69    }
70
71    /// Queue a report that holds buttons down, followed by the matching release.
72    ///
73    /// Without the trailing all-zero report the host sees the button as still held, which is
74    /// how a "click" turns into a stuck drag.
75    pub fn queue_with_release(&mut self, report: MouseReport) {
76        let held = report.buttons != 0;
77        self.queue(report);
78        if held {
79            self.queue(MouseReport::new());
80        }
81    }
82
83    /// Number of reports still waiting to go out (used by tests and diagnostics).
84    #[allow(dead_code)]
85    pub fn pending_len(&self) -> usize {
86        self.pending_reports.len()
87    }
88}
89
90#[cfg(all(unix, feature = "libusb"))]
91impl usbip::UsbInterfaceHandler for UsbHidMouseHandler {
92    fn handle_urb(
93        &mut self,
94        _interface: &usbip::UsbInterface,
95        ep: usbip::UsbEndpoint,
96        _transfer_buffer_length: u32,
97        setup: usbip::SetupPacket,
98        _req: &[u8],
99    ) -> std::result::Result<Vec<u8>, std::io::Error> {
100        if ep.is_ep0() {
101            // Control transfers on endpoint 0.
102            match (setup.request_type, setup.request) {
103                // GET_DESCRIPTOR (device-to-host, standard, interface)
104                (0b1000_0001, 0x06) => {
105                    let desc_type = (setup.value >> 8) as u8;
106                    if desc_type == hid_descriptor_type::REPORT {
107                        debug!(
108                            "USB mouse GET_DESCRIPTOR(Report): {} bytes",
109                            self.report_descriptor.len()
110                        );
111                        Ok(self.report_descriptor.clone())
112                    } else {
113                        warn!("USB mouse: unsupported descriptor type {:#04x}", desc_type);
114                        Ok(vec![])
115                    }
116                }
117                // SET_IDLE / SET_PROTOCOL (host-to-device, class, interface)
118                (0b0010_0001, 0x0A) | (0b0010_0001, 0x0B) => Ok(vec![]),
119                // GET_REPORT (device-to-host, class, interface): answer with current state.
120                (0b1010_0001, 0x01) => Ok(self
121                    .pending_reports
122                    .front()
123                    .map(|r| r.to_vec())
124                    .unwrap_or_else(|| vec![0u8; 4])),
125                _ => {
126                    // Unknown control requests are answered empty rather than with an error:
127                    // an error aborts the USB/IP session for the whole device, and a host
128                    // probing an optional request must not disconnect the mouse.
129                    debug!(
130                        "USB mouse: unhandled control request type={:#04x} request={:#04x}",
131                        setup.request_type, setup.request
132                    );
133                    Ok(vec![])
134                }
135            }
136        } else if let usbip::Direction::In = ep.direction() {
137            // Interrupt IN: hand the host the next queued report, or nothing.
138            Ok(self
139                .pending_reports
140                .pop_front()
141                .map(|r| r.to_vec())
142                .unwrap_or_default())
143        } else {
144            // A boot-protocol mouse has no OUT endpoint; ignore anything that arrives.
145            Ok(vec![])
146        }
147    }
148
149    fn get_class_specific_descriptor(&self) -> Vec<u8> {
150        let len = self.report_descriptor.len();
151        vec![
152            0x09,                     // bLength
153            hid_descriptor_type::HID, // bDescriptorType: HID
154            0x11,
155            0x01,                        // bcdHID 1.11
156            0x00,                        // bCountryCode
157            0x01,                        // bNumDescriptors
158            hid_descriptor_type::REPORT, // bDescriptorType[0]
159            len as u8,
160            (len >> 8) as u8, // wDescriptorLength[0]
161        ]
162    }
163
164    fn as_any(&mut self) -> &mut dyn std::any::Any {
165        self
166    }
167}