From 561354b69c5ae5e73b036b382988fda02bc9fb01 Mon Sep 17 00:00:00 2001 From: salmonik Date: Sun, 1 Nov 2026 16:05:09 +0100 Subject: usb: re-enable EHCI, share HID keyboard decoding, fix boot protocol - ehci: DEBUG_MATRIX was left true in be868a4, so every boot replaced enumeration with the one-shot diagnostic transfer matrix and returned false -- the EHCI driver never brought a device up. Default it off; flip it back on only when bisecting a controller fault. - new rsnix/src/hid.rs: boot-keyboard HID usage -> scancode-set-1 tables, the shared scancode FIFO, and a per-device report differ. UHCI now uses it instead of its own copy, so EHCI/xHCI keyboards can feed the same PS/2 decoder (`ps2::feed_scancode`). - uhci: SET_PROTOCOL now asks for the boot protocol (wValue 0) instead of report protocol (wValue 1). In report protocol the report layout is device-defined, but every driver parses boot reports. --- rsnix/src/hid.rs | 232 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 232 insertions(+) create mode 100644 rsnix/src/hid.rs (limited to 'rsnix/src/hid.rs') diff --git a/rsnix/src/hid.rs b/rsnix/src/hid.rs new file mode 100644 index 0000000..42c7602 --- /dev/null +++ b/rsnix/src/hid.rs @@ -0,0 +1,232 @@ +// --------------------------------------------------------------------------- +// Shared USB HID helpers: boot-keyboard report decoding into scancode-set-1 +// bytes, plus the FIFO the drivers hand to the PS/2 decoder. +// +// Every HID keyboard (UHCI, EHCI, xHCI) feeds the same `ps2::feed_scancode` +// state machine, so a USB keyboard behaves exactly like the PS/2 one. The +// per-report diff state lives in `Keyboard` (one instance per device); the +// byte FIFO is shared because the consumer is a single scancode stream. +// --------------------------------------------------------------------------- + +use core::sync::atomic::Ordering; + +/// HID usage -> scancode set 1 (make code). High byte 0xE0 = E0-prefixed. +/// 0 = unmapped. Table covers the common keyboard usages (0x04..0x63). +static HID_TO_SCAN: [u16; 0x64] = { + let mut t = [0u16; 0x64]; + let mut i = 0; + while i < 0x64 { + t[i] = 0; + i += 1; + } + // Letters a-z + t[0x04] = 0x1E; + t[0x05] = 0x30; + t[0x06] = 0x2E; + t[0x07] = 0x20; + t[0x08] = 0x12; + t[0x09] = 0x21; + t[0x0A] = 0x22; + t[0x0B] = 0x23; + t[0x0C] = 0x17; + t[0x0D] = 0x24; + t[0x0E] = 0x25; + t[0x0F] = 0x26; + t[0x10] = 0x32; + t[0x11] = 0x31; + t[0x12] = 0x18; + t[0x13] = 0x19; + t[0x14] = 0x10; + t[0x15] = 0x13; + t[0x16] = 0x1F; + t[0x17] = 0x14; + t[0x18] = 0x16; + t[0x19] = 0x2F; + t[0x1A] = 0x11; + t[0x1B] = 0x2D; + t[0x1C] = 0x15; + t[0x1D] = 0x2C; + // Digits + punctuation + t[0x1E] = 0x02; + t[0x1F] = 0x03; + t[0x20] = 0x04; + t[0x21] = 0x05; + t[0x22] = 0x06; + t[0x23] = 0x07; + t[0x24] = 0x08; + t[0x25] = 0x09; + t[0x26] = 0x0A; + t[0x27] = 0x0B; + t[0x28] = 0x1C; // Enter + t[0x29] = 0x01; // Esc + t[0x2A] = 0x0E; // Backspace + t[0x2B] = 0x0F; // Tab + t[0x2C] = 0x39; // Space + t[0x2D] = 0x0C; // - + t[0x2E] = 0x0D; // = + t[0x2F] = 0x1A; // [ + t[0x30] = 0x1B; // ] + t[0x31] = 0x2B; // backslash + t[0x33] = 0x27; // ; + t[0x34] = 0x28; // ' + t[0x35] = 0x29; // ` + t[0x36] = 0x33; // , + t[0x37] = 0x34; // . + t[0x38] = 0x35; // / + t[0x39] = 0x3A; // CapsLock + // F-keys + t[0x3A] = 0x3B; // F1 + t[0x3B] = 0x3C; + t[0x3C] = 0x3D; + t[0x3D] = 0x3E; + t[0x3E] = 0x3F; + t[0x3F] = 0x40; + t[0x40] = 0x41; + t[0x41] = 0x42; + t[0x42] = 0x43; + t[0x43] = 0x44; // F10 + t[0x44] = 0xE057; // F11 + t[0x45] = 0xE058; // F12 + // Nav cluster + t[0x49] = 0xE052; // Insert + t[0x4A] = 0xE047; // Home + t[0x4B] = 0xE049; // PageUp + t[0x4C] = 0xE053; // Delete + t[0x4D] = 0xE04F; // End + t[0x4E] = 0xE051; // PageDown + t[0x4F] = 0xE04D; // Right + t[0x50] = 0xE04B; // Left + t[0x51] = 0xE050; // Down + t[0x52] = 0xE048; // Up + // Keypad + t[0x53] = 0x45; // NumLock + t[0x54] = 0xE035; // / + t[0x55] = 0x37; // * + t[0x56] = 0x4A; // - + t[0x57] = 0x4E; // + + t[0x58] = 0xE01C; // Enter + t[0x59] = 0x4F; // 1 + t[0x5A] = 0x50; // 2 + t[0x5B] = 0x51; // 3 + t[0x5C] = 0x4B; // 4 + t[0x5D] = 0x4C; // 5 + t[0x5E] = 0x4D; // 6 + t[0x5F] = 0x47; // 7 + t[0x60] = 0x48; // 8 + t[0x61] = 0x49; // 9 + t[0x62] = 0x52; // 0 + t[0x63] = 0x53; // . + t +}; + +/// Modifier bit -> scancode (set 1, high byte 0xE0 = E0-prefixed). +static MOD_TO_SCAN: [u16; 8] = [ + 0x1D, // 0 LCtrl + 0x2A, // 1 LShift + 0x38, // 2 LAlt + 0xE05B, // 3 LGui + 0xE01D, // 4 RCtrl + 0x36, // 5 RShift + 0xE038, // 6 RAlt + 0xE05C, // 7 RGui +]; + +// ---- scancode FIFO (shared: one PS/2 decoder consumer) ---- +const Q_SIZE: usize = 128; +static mut Q: [u8; Q_SIZE] = [0; Q_SIZE]; +static Q_HEAD: core::sync::atomic::AtomicU32 = core::sync::atomic::AtomicU32::new(0); +static Q_LEN: core::sync::atomic::AtomicU32 = core::sync::atomic::AtomicU32::new(0); + +/// Queue one scancode-set-1 byte (drops when the FIFO is full). +pub fn push_byte(b: u8) { + unsafe { + let h = Q_HEAD.load(Ordering::Relaxed) as usize; + let l = Q_LEN.load(Ordering::Relaxed) as usize; + if l >= Q_SIZE { + return; + } + Q[(h + l) % Q_SIZE] = b; + Q_LEN.store((l + 1) as u32, Ordering::Relaxed); + } +} + +/// Pop one queued scancode byte (callers feed it to `ps2::feed_scancode`). +pub fn pop_byte() -> Option { + unsafe { + let l = Q_LEN.load(Ordering::Relaxed) as usize; + if l == 0 { + return None; + } + let h = Q_HEAD.load(Ordering::Relaxed) as usize; + let b = Q[h]; + Q_HEAD.store(((h + 1) % Q_SIZE) as u32, Ordering::Relaxed); + Q_LEN.store((l - 1) as u32, Ordering::Relaxed); + Some(b) + } +} + +/// Push the make (press) or break (release) scancode sequence for a +/// scancode-map entry (0 = unmapped, 0x00XX = plain, 0xE0XX = E0-prefixed). +pub fn push_scancode(scan: u16, press: bool) { + if scan == 0 { + return; + } + if scan & 0xFF00 == 0xE000 { + push_byte(0xE0); + let code = (scan & 0xFF) as u8; + push_byte(if press { code } else { code | 0x80 }); + } else { + let code = scan as u8; + push_byte(if press { code } else { code | 0x80 }); + } +} + +/// Per-device boot-keyboard report differ. Boot protocol reports are +/// 8 bytes: modifiers, reserved, 6 usage codes. +pub struct Keyboard { + prev: [u8; 8], +} + +impl Keyboard { + pub const fn new() -> Self { + Self { prev: [0; 8] } + } + + /// Diff `report` against the previous one and queue make/break bytes. + pub fn diff(&mut self, report: &[u8]) { + let mut rep = [0u8; 8]; + for (i, b) in rep.iter_mut().enumerate() { + *b = report.get(i).copied().unwrap_or(0); + } + let new_mods = rep[0]; + let old_mods = self.prev[0]; + for bit in 0..8 { + let new_set = new_mods & (1 << bit) != 0; + let old_set = old_mods & (1 << bit) != 0; + if new_set != old_set { + push_scancode(MOD_TO_SCAN[bit], new_set); + } + } + let new_keys = &rep[2..8]; + let old_keys = &self.prev[2..8]; + // Releases first (old keys not in the new set). + for &k in old_keys { + if k == 0 || new_keys.contains(&k) { + continue; + } + if (k as usize) < HID_TO_SCAN.len() { + push_scancode(HID_TO_SCAN[k as usize], false); + } + } + // Then presses (new keys not in the old set). + for &k in new_keys { + if k == 0 || old_keys.contains(&k) { + continue; + } + if (k as usize) < HID_TO_SCAN.len() { + push_scancode(HID_TO_SCAN[k as usize], true); + } + } + self.prev = rep; + } +} -- cgit v1.2.3