Skip to main content

strat9_kernel/hardware/usb/
hid.rs

1// USB HID (Human Interface Device) Driver
2// Supports boot protocol keyboards and mice
3//
4// Features:
5// - Boot protocol keyboard support
6// - Boot protocol mouse support
7// - Event queue for key presses and mouse movements
8// - PS/2 to USB keycode translation
9// - Unification with PS/2: events feed into the same keyboard/mouse buffers
10//
11// Inspired by Redox usbhid, Asterinas input subsystem, Maestro device manager.
12
13#![allow(dead_code)]
14
15use crate::arch::x86_64::{keyboard, mouse};
16use alloc::{sync::Arc, vec::Vec};
17use core::sync::atomic::{AtomicBool, Ordering};
18use spin::Mutex;
19
20pub const HID_BOOT_KEYBOARD: u8 = 0x01;
21pub const HID_BOOT_MOUSE: u8 = 0x02;
22
23// USB HID Boot Keyboard Report size (8 bytes)
24const KBD_REPORT_SIZE: usize = 8;
25
26// USB HID Boot Mouse Report size (3 bytes for basic mice)
27const MOUSE_REPORT_SIZE: usize = 3;
28
29// Modifier keys
30const MOD_LCTRL: u8 = 0x01;
31const MOD_LSHIFT: u8 = 0x02;
32const MOD_LALT: u8 = 0x04;
33const MOD_LGUI: u8 = 0x08;
34const MOD_RCTRL: u8 = 0x10;
35const MOD_RSHIFT: u8 = 0x20;
36const MOD_RALT: u8 = 0x40;
37const MOD_RGUI: u8 = 0x80;
38
39#[derive(Clone, Copy, Debug)]
40pub struct KeyEvent {
41    pub keycode: u8,
42    pub pressed: bool,
43    pub modifiers: u8,
44}
45
46#[derive(Clone, Copy, Debug)]
47pub struct MouseEvent {
48    pub dx: i8,
49    pub dy: i8,
50    pub dz: i8,
51    pub buttons: u8,
52}
53
54// USB to PS/2 scan code translation table (subset)
55// Maps USB HID usage IDs to PS/2 scan codes
56const USB_TO_PS2: [u8; 128] = [
57    0x00, 0x00, 0x00, 0x00, 0x1C, 0x32, 0x21, 0x23, // 00-07
58    0x1D, 0x24, 0x2B, 0x34, 0x33, 0x43, 0x35, 0x0E, // 08-0F
59    0x15, 0x16, 0x17, 0x1C, 0x18, 0x19, 0x14, 0x1A, // 10-17
60    0x1B, 0x1D, 0x1E, 0x21, 0x22, 0x23, 0x24, 0x2B, // 18-1F
61    0x29, 0x2F, 0x2E, 0x30, 0x20, 0x31, 0x32, 0x33, // 20-27
62    0x2C, 0x2D, 0x11, 0x12, 0x13, 0x3F, 0x3E, 0x46, // 28-2F
63    0x45, 0x5D, 0x4C, 0x36, 0x4A, 0x55, 0x37, 0x4E, // 30-37
64    0x57, 0x5E, 0x5C, 0x41, 0x52, 0x4D, 0x4B, 0x5B, // 38-3F
65    0x5A, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, // 40-47
66    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, // 48-4F
67    0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, // 50-57
68    0x80, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 58-5F
69    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 60-67
70    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 68-6F
71    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 70-77
72    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 78-7F
73];
74
75/// Performs the usb to ps2 operation.
76fn usb_to_ps2(keycode: u8) -> u8 {
77    if keycode < USB_TO_PS2.len() as u8 {
78        USB_TO_PS2[keycode as usize]
79    } else {
80        0x00
81    }
82}
83
84pub struct HidKeyboard {
85    port: usize,
86    interface: u8,
87    endpoint: u8,
88    max_packet: u16,
89    interval: u8,
90    event_queue: Vec<KeyEvent>,
91    last_report: [u8; KBD_REPORT_SIZE],
92}
93
94unsafe impl Send for HidKeyboard {}
95unsafe impl Sync for HidKeyboard {}
96
97impl HidKeyboard {
98    /// Creates a new instance.
99    pub fn new(port: usize, interface: u8, endpoint: u8, max_packet: u16, interval: u8) -> Self {
100        Self {
101            port,
102            interface,
103            endpoint,
104            max_packet,
105            interval,
106            event_queue: Vec::new(),
107            last_report: [0; KBD_REPORT_SIZE],
108        }
109    }
110
111    /// Reads event.
112    pub fn read_event(&mut self) -> Option<KeyEvent> {
113        if self.event_queue.is_empty() {
114            self.poll();
115        }
116        self.event_queue.pop()
117    }
118
119    /// Performs the poll operation.
120    pub fn poll(&mut self) {
121        // In a full implementation, this would submit an interrupt transfer
122        // and parse the HID report. For now, we simulate basic functionality.
123        // The actual polling would be done via xHCI interrupt transfers.
124    }
125
126    /// Performs the process report operation.
127    pub fn process_report(&mut self, report: &[u8; KBD_REPORT_SIZE]) {
128        // report[0] = modifiers
129        // report[1] = reserved
130        // report[2..7] = keycodes
131
132        let modifiers = report[0];
133
134        for i in 2..8 {
135            let keycode = report[i];
136            if keycode == 0 {
137                continue;
138            }
139
140            // Check if key was pressed (new in this report)
141            let was_pressed = self.last_report[2..8].contains(&keycode);
142            if !was_pressed {
143                self.event_queue.push(KeyEvent {
144                    keycode: usb_to_ps2(keycode),
145                    pressed: true,
146                    modifiers,
147                });
148            }
149        }
150
151        // Check for released keys
152        for i in 2..8 {
153            let keycode = self.last_report[i];
154            if keycode != 0 && !report[2..8].contains(&keycode) {
155                self.event_queue.push(KeyEvent {
156                    keycode: usb_to_ps2(keycode),
157                    pressed: false,
158                    modifiers,
159                });
160            }
161        }
162
163        self.last_report = *report;
164    }
165
166    /// Returns whether modifier pressed.
167    pub fn is_modifier_pressed(&self, modifier: u8) -> bool {
168        self.last_report[0] & modifier != 0
169    }
170
171    /// Drain event queue and inject into the unified keyboard buffer.
172    pub fn drain_into_unified(&mut self) {
173        while let Some(ev) = self.event_queue.pop() {
174            keyboard::inject_hid_scancode(ev.keycode, ev.pressed);
175        }
176    }
177}
178
179pub struct HidMouse {
180    port: usize,
181    interface: u8,
182    endpoint: u8,
183    max_packet: u16,
184    interval: u8,
185    event_queue: Vec<MouseEvent>,
186    last_buttons: u8,
187}
188
189unsafe impl Send for HidMouse {}
190unsafe impl Sync for HidMouse {}
191
192impl HidMouse {
193    /// Creates a new instance.
194    pub fn new(port: usize, interface: u8, endpoint: u8, max_packet: u16, interval: u8) -> Self {
195        Self {
196            port,
197            interface,
198            endpoint,
199            max_packet,
200            interval,
201            event_queue: Vec::new(),
202            last_buttons: 0,
203        }
204    }
205
206    /// Reads event.
207    pub fn read_event(&mut self) -> Option<MouseEvent> {
208        if self.event_queue.is_empty() {
209            self.poll();
210        }
211        self.event_queue.pop()
212    }
213
214    /// Performs the poll operation.
215    pub fn poll(&mut self) {
216        // In a full implementation, this would submit an interrupt transfer
217        // and parse the HID report.
218    }
219
220    /// Performs the process report operation.
221    pub fn process_report(&mut self, report: &[u8]) {
222        if report.len() < 3 {
223            return;
224        }
225
226        let buttons = report[0];
227        let dx = report[1] as i8;
228        let dy = report[2] as i8;
229        let dz = if report.len() > 3 { report[3] as i8 } else { 0 };
230
231        // Check for button changes
232        for i in 0..5 {
233            let mask = 1 << i;
234            let was_pressed = self.last_buttons & mask != 0;
235            let is_pressed = buttons & mask != 0;
236
237            if was_pressed != is_pressed {
238                self.event_queue.push(MouseEvent {
239                    dx: 0,
240                    dy: 0,
241                    dz: 0,
242                    buttons: if is_pressed { mask } else { 0 },
243                });
244            }
245        }
246
247        // Add movement event if there was movement
248        if dx != 0 || dy != 0 || dz != 0 {
249            self.event_queue.push(MouseEvent {
250                dx,
251                dy,
252                dz,
253                buttons,
254            });
255        }
256
257        self.last_buttons = buttons;
258    }
259
260    /// Returns whether button pressed.
261    pub fn is_button_pressed(&self, button: u8) -> bool {
262        self.last_buttons & (1 << button) != 0
263    }
264
265    /// Drain event queue and inject into the unified mouse buffer.
266    pub fn drain_into_unified(&mut self) {
267        while let Some(ev) = self.event_queue.pop() {
268            let left = ev.buttons & 0x01 != 0;
269            let right = ev.buttons & 0x02 != 0;
270            let middle = ev.buttons & 0x04 != 0;
271            mouse::push_event_from_hid(ev.dx as i16, ev.dy as i16, ev.dz, left, right, middle);
272        }
273    }
274}
275
276static KEYBOARDS: Mutex<Vec<Arc<Mutex<HidKeyboard>>>> = Mutex::new(Vec::new());
277static MICE: Mutex<Vec<Arc<Mutex<HidMouse>>>> = Mutex::new(Vec::new());
278static HID_INITIALIZED: AtomicBool = AtomicBool::new(false);
279
280/// Performs the init operation.
281pub fn init() {
282    log::info!("[USB-HID] Initializing HID drivers...");
283
284    if !crate::hardware::usb::xhci::is_available() {
285        log::warn!("[USB-HID] xHCI not available, skipping HID init");
286        return;
287    }
288
289    // xHCI host bring-up is present, but slot/endpoint enumeration is not yet
290    // robust enough to run during kernel boot. Keep HID disabled until the
291    // command path is completed.
292    log::warn!("[USB-HID] xHCI enumeration not enabled yet, skipping HID probe");
293
294    HID_INITIALIZED.store(true, Ordering::SeqCst);
295    log::info!(
296        "[USB-HID] Initialized: {} keyboard(s), {} mouse/mice",
297        KEYBOARDS.lock().len(),
298        MICE.lock().len()
299    );
300}
301
302/// Performs the probe hid device operation.
303fn probe_hid_device(port: usize) {
304    if port == 0 {
305        let keyboard = HidKeyboard::new(port, 0, 0x81, 8, 10);
306        KEYBOARDS.lock().push(Arc::new(Mutex::new(keyboard)));
307        log::info!("[USB-HID] Found keyboard on port {}", port);
308    } else if port == 1 {
309        let mouse_dev = HidMouse::new(port, 0, 0x81, 4, 10);
310        MICE.lock().push(Arc::new(Mutex::new(mouse_dev)));
311        log::info!("[USB-HID] Found mouse on port {}", port);
312    }
313}
314
315pub fn enumerate_device(port: usize) {
316    if let Some(controller_arc) = crate::hardware::usb::xhci::get_controller(0) {
317        let mut controller = controller_arc.lock();
318
319        let mut dev_desc = [0u8; 18];
320        if controller.get_device_descriptor(1, &mut dev_desc).is_ok() {
321            log::info!(
322                "[USB-HID] Device: VID={:04x} PID={:04x}",
323                u16::from_le_bytes([dev_desc[2], dev_desc[3]]),
324                u16::from_le_bytes([dev_desc[4], dev_desc[5]])
325            );
326        }
327
328        let mut config_desc = [0u8; 256];
329        if controller
330            .get_configuration_descriptor(1, 0, &mut config_desc, 9)
331            .is_ok()
332        {
333            log::info!(
334                "[USB-HID] Config: total_len={}",
335                u16::from_le_bytes([config_desc[2], config_desc[3]])
336            );
337        }
338
339        controller.set_configuration(1, 1).ok();
340    }
341
342    probe_hid_device(port);
343}
344
345/// Returns keyboard.
346pub fn get_keyboard(index: usize) -> Option<Arc<Mutex<HidKeyboard>>> {
347    KEYBOARDS.lock().get(index).cloned()
348}
349
350/// Returns mouse.
351pub fn get_mouse(index: usize) -> Option<Arc<Mutex<HidMouse>>> {
352    MICE.lock().get(index).cloned()
353}
354
355/// Performs the keyboard count operation.
356pub fn keyboard_count() -> usize {
357    KEYBOARDS.lock().len()
358}
359
360/// Performs the mouse count operation.
361pub fn mouse_count() -> usize {
362    MICE.lock().len()
363}
364
365/// Returns whether available.
366pub fn is_available() -> bool {
367    HID_INITIALIZED.load(Ordering::Relaxed)
368}
369
370/// Poll all HID devices and inject events into the unified keyboard/mouse buffers.
371///
372/// Call from the shell loop (task context) to drain USB HID event queues and
373/// feed them into the same buffers used by PS/2. This enables USB keyboards
374/// and mice to work alongside or instead of PS/2.
375pub fn poll_all() {
376    for kbd in KEYBOARDS.lock().iter() {
377        let mut k = kbd.lock();
378        k.drain_into_unified();
379    }
380    for m in MICE.lock().iter() {
381        let mut dev = m.lock();
382        dev.drain_into_unified();
383    }
384}
385
386/// Called by xHCI IRQ handler when a transfer completes.
387///
388/// Processes the HID report and queues events.
389pub fn notify_transfer_complete(_slot_id: u8, _ep_id: u8) {
390    poll_all();
391}