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/ehci.rs | 7 +- rsnix/src/hid.rs | 232 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ rsnix/src/lib.rs | 1 + rsnix/src/uhci.rs | 228 ++++------------------------------------------------- 4 files changed, 253 insertions(+), 215 deletions(-) create mode 100644 rsnix/src/hid.rs diff --git a/rsnix/src/ehci.rs b/rsnix/src/ehci.rs index 6209155..8baa0ad 100644 --- a/rsnix/src/ehci.rs +++ b/rsnix/src/ehci.rs @@ -89,7 +89,12 @@ static QR_SHOWN: AtomicBool = AtomicBool::new(false); // DEBUG: run the one-boot transfer variant matrix (real-hardware bisection // of the EHCI System Error trigger). MATRIX_DONE gates it to one pass; // XFER_TWEAK flips construction variables inside ctl_xfer. -static DEBUG_MATRIX: bool = true; +// +// OFF in normal builds: with it on, `enumerate()` is replaced by the +// diagnostic matrix and returns false, i.e. the EHCI driver never brings a +// device up. Flip to true only when bisecting a hardware fault, and expect +// no USB input in that build. +static DEBUG_MATRIX: bool = false; static MATRIX_DONE: AtomicBool = AtomicBool::new(false); static XFER_TWEAK: AtomicU32 = AtomicU32::new(0); // Low-region (<1 MB) control QH for the matrix's low-region variant. 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; + } +} diff --git a/rsnix/src/lib.rs b/rsnix/src/lib.rs index 325932a..b4307a3 100644 --- a/rsnix/src/lib.rs +++ b/rsnix/src/lib.rs @@ -11,6 +11,7 @@ pub mod font; pub mod fs; pub mod gdt; pub mod gop; +pub mod hid; pub mod interrupts; pub mod klog; pub mod load; diff --git a/rsnix/src/uhci.rs b/rsnix/src/uhci.rs index 87ec9c6..988db72 100644 --- a/rsnix/src/uhci.rs +++ b/rsnix/src/uhci.rs @@ -133,12 +133,9 @@ const OFF_CFG: usize = 0x300; // config descriptor (256 B) const OFF_KBD_REPORT: usize = 0x500; // 8 B const OFF_MOUSE_REPORT: usize = 0x520; // 8 B -// Keyboard: previous report + pending scancode FIFO (fed to ps2 decoder). -static mut KBD_PREV: [u8; 8] = [0; 8]; -const KBDQ_SIZE: usize = 64; -static mut KBDQ: [u8; KBDQ_SIZE] = [0; KBDQ_SIZE]; -static KBDQ_HEAD: AtomicU32 = AtomicU32::new(0); -static KBDQ_LEN: AtomicU32 = AtomicU32::new(0); +// Keyboard: per-device HID report diff state. Scancode bytes go to the +// shared FIFO in `hid.rs` (drained by the single PS/2 decoder). +static mut KBD: crate::hid::Keyboard = crate::hid::Keyboard::new(); // ---- port I/O ---- #[inline] @@ -495,7 +492,10 @@ unsafe fn enumerate_port(p: usize, want: u8) -> bool { } // 6. Boot protocol + no idle. Some devices stall these; tolerate. - let req = ctrl_req(RT_CLASS_IFACE, REQ_SET_PROTOCOL, 1, ep.iface as u16, 0); + // wValue 0 = boot protocol: guarantees the 3-byte mouse / 8-byte + // keyboard report layout the drivers parse. (wValue 1 would be + // report protocol, where the layout is device-defined.) + let req = ctrl_req(RT_CLASS_IFACE, REQ_SET_PROTOCOL, 0, ep.iface as u16, 0); if ctl_xfer(dev, mps0, low_speed, &req, false, core::ptr::null_mut(), 0) < 0 { crate::serial_println!("uhci: set-protocol stalled on port {}", p); } @@ -618,211 +618,10 @@ unsafe fn arm_device(want: u8) -> bool { } // --------------------------------------------------------------------------- -// Keyboard: HID report -> scancode-set-1 make/break bytes -> ps2 decoder +// Keyboard: HID reports feed the shared scancode FIFO (`hid.rs`), drained by +// try_poll_key through the PS/2 decoder. // --------------------------------------------------------------------------- -/// 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 -]; - -fn kbdq_push(b: u8) { - unsafe { - let h = KBDQ_HEAD.load(Ordering::Relaxed) as usize; - let l = KBDQ_LEN.load(Ordering::Relaxed) as usize; - if l >= KBDQ_SIZE { - return; - } - KBDQ[(h + l) % KBDQ_SIZE] = b; - KBDQ_LEN.store((l + 1) as u32, Ordering::Relaxed); - } -} -fn kbdq_pop() -> Option { - unsafe { - let l = KBDQ_LEN.load(Ordering::Relaxed) as usize; - if l == 0 { - return None; - } - let h = KBDQ_HEAD.load(Ordering::Relaxed) as usize; - let b = KBDQ[h]; - KBDQ_HEAD.store(((h + 1) % KBDQ_SIZE) as u32, Ordering::Relaxed); - KBDQ_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). -fn push_scancode(scan: u16, press: bool) { - if scan == 0 { - return; - } - if scan & 0xFF00 == 0xE000 { - kbdq_push(0xE0); - let code = (scan & 0xFF) as u8; - kbdq_push(if press { code } else { code | 0x80 }); - } else { - let code = scan as u8; - kbdq_push(if press { code } else { code | 0x80 }); - } -} - -/// Diff the completed HID report against the previous one and queue the -/// scancode transitions (make/break) for the shared PS/2 decoder. -unsafe fn diff_report(report: &[u8]) { - unsafe { - let prev = &mut *core::ptr::addr_of_mut!(KBD_PREV); - let new_mods = report[0]; - let old_mods = 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 = &report[2..8]; - let old_keys = &prev[2..8]; - // Releases first (old keys not in new set). - for &k in old_keys { - if k == 0 || new_keys.contains(&k) { - continue; - } - if k as usize >= HID_TO_SCAN.len() { - continue; - } - 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() { - continue; - } - push_scancode(HID_TO_SCAN[k as usize], true); - } - prev.copy_from_slice(report); - } -} - /// Re-arm a completed interrupt-IN TD (one-outstanding report pattern). unsafe fn rearm_td(qho: usize, tdo: usize, toggle: u32, ls: bool, mps: usize, explen: u32) { unsafe { @@ -998,7 +797,7 @@ pub fn init() -> bool { pub fn try_poll_key() -> Option { unsafe { // Fast path: pending scancodes first. - if let Some(b) = kbdq_pop() { + if let Some(b) = crate::hid::pop_byte() { return crate::ps2::feed_scancode(b); } if KBD_ARMED.load(Ordering::Relaxed) { @@ -1016,7 +815,8 @@ pub fn try_poll_key() -> Option { for (i, b) in report.iter_mut().enumerate() { *b = core::ptr::read_volatile(rep.add(i)); } - diff_report(&report); + let kbd = &mut *core::ptr::addr_of_mut!(KBD); + kbd.diff(&report); let tog = KBD_TOGGLE.load(Ordering::Relaxed) ^ 1; KBD_TOGGLE.store(tog, Ordering::Relaxed); rearm_td( @@ -1027,7 +827,7 @@ pub fn try_poll_key() -> Option { KBD_MPS.load(Ordering::Relaxed) as usize, 8, ); - return kbdq_pop().and_then(crate::ps2::feed_scancode); + return crate::hid::pop_byte().and_then(crate::ps2::feed_scancode); } return None; } @@ -1039,7 +839,7 @@ pub fn try_poll_key() -> Option { let _ = arm_device(1); } } - kbdq_pop().and_then(crate::ps2::feed_scancode) + crate::hid::pop_byte().and_then(crate::ps2::feed_scancode) } } -- cgit v1.2.3