summaryrefslogtreecommitdiffstats
path: root/rsnix/src/hid.rs
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-11-01 16:05:09 +0100
committersalmonik <salmonik@tuta.io>2026-11-01 16:05:09 +0100
commit561354b69c5ae5e73b036b382988fda02bc9fb01 (patch)
treee030b0c493637502f24e0f6e60ae2ca4418e0c03 /rsnix/src/hid.rs
parentscripts: rebuild the dev loop around build.sh/run.sh (diff)
downloadrs-dos-561354b69c5ae5e73b036b382988fda02bc9fb01.tar.gz
rs-dos-561354b69c5ae5e73b036b382988fda02bc9fb01.zip
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.
Diffstat (limited to 'rsnix/src/hid.rs')
-rw-r--r--rsnix/src/hid.rs232
1 files changed, 232 insertions, 0 deletions
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 @@
1// ---------------------------------------------------------------------------
2// Shared USB HID helpers: boot-keyboard report decoding into scancode-set-1
3// bytes, plus the FIFO the drivers hand to the PS/2 decoder.
4//
5// Every HID keyboard (UHCI, EHCI, xHCI) feeds the same `ps2::feed_scancode`
6// state machine, so a USB keyboard behaves exactly like the PS/2 one. The
7// per-report diff state lives in `Keyboard` (one instance per device); the
8// byte FIFO is shared because the consumer is a single scancode stream.
9// ---------------------------------------------------------------------------
10
11use core::sync::atomic::Ordering;
12
13/// HID usage -> scancode set 1 (make code). High byte 0xE0 = E0-prefixed.
14/// 0 = unmapped. Table covers the common keyboard usages (0x04..0x63).
15static HID_TO_SCAN: [u16; 0x64] = {
16 let mut t = [0u16; 0x64];
17 let mut i = 0;
18 while i < 0x64 {
19 t[i] = 0;
20 i += 1;
21 }
22 // Letters a-z
23 t[0x04] = 0x1E;
24 t[0x05] = 0x30;
25 t[0x06] = 0x2E;
26 t[0x07] = 0x20;
27 t[0x08] = 0x12;
28 t[0x09] = 0x21;
29 t[0x0A] = 0x22;
30 t[0x0B] = 0x23;
31 t[0x0C] = 0x17;
32 t[0x0D] = 0x24;
33 t[0x0E] = 0x25;
34 t[0x0F] = 0x26;
35 t[0x10] = 0x32;
36 t[0x11] = 0x31;
37 t[0x12] = 0x18;
38 t[0x13] = 0x19;
39 t[0x14] = 0x10;
40 t[0x15] = 0x13;
41 t[0x16] = 0x1F;
42 t[0x17] = 0x14;
43 t[0x18] = 0x16;
44 t[0x19] = 0x2F;
45 t[0x1A] = 0x11;
46 t[0x1B] = 0x2D;
47 t[0x1C] = 0x15;
48 t[0x1D] = 0x2C;
49 // Digits + punctuation
50 t[0x1E] = 0x02;
51 t[0x1F] = 0x03;
52 t[0x20] = 0x04;
53 t[0x21] = 0x05;
54 t[0x22] = 0x06;
55 t[0x23] = 0x07;
56 t[0x24] = 0x08;
57 t[0x25] = 0x09;
58 t[0x26] = 0x0A;
59 t[0x27] = 0x0B;
60 t[0x28] = 0x1C; // Enter
61 t[0x29] = 0x01; // Esc
62 t[0x2A] = 0x0E; // Backspace
63 t[0x2B] = 0x0F; // Tab
64 t[0x2C] = 0x39; // Space
65 t[0x2D] = 0x0C; // -
66 t[0x2E] = 0x0D; // =
67 t[0x2F] = 0x1A; // [
68 t[0x30] = 0x1B; // ]
69 t[0x31] = 0x2B; // backslash
70 t[0x33] = 0x27; // ;
71 t[0x34] = 0x28; // '
72 t[0x35] = 0x29; // `
73 t[0x36] = 0x33; // ,
74 t[0x37] = 0x34; // .
75 t[0x38] = 0x35; // /
76 t[0x39] = 0x3A; // CapsLock
77 // F-keys
78 t[0x3A] = 0x3B; // F1
79 t[0x3B] = 0x3C;
80 t[0x3C] = 0x3D;
81 t[0x3D] = 0x3E;
82 t[0x3E] = 0x3F;
83 t[0x3F] = 0x40;
84 t[0x40] = 0x41;
85 t[0x41] = 0x42;
86 t[0x42] = 0x43;
87 t[0x43] = 0x44; // F10
88 t[0x44] = 0xE057; // F11
89 t[0x45] = 0xE058; // F12
90 // Nav cluster
91 t[0x49] = 0xE052; // Insert
92 t[0x4A] = 0xE047; // Home
93 t[0x4B] = 0xE049; // PageUp
94 t[0x4C] = 0xE053; // Delete
95 t[0x4D] = 0xE04F; // End
96 t[0x4E] = 0xE051; // PageDown
97 t[0x4F] = 0xE04D; // Right
98 t[0x50] = 0xE04B; // Left
99 t[0x51] = 0xE050; // Down
100 t[0x52] = 0xE048; // Up
101 // Keypad
102 t[0x53] = 0x45; // NumLock
103 t[0x54] = 0xE035; // /
104 t[0x55] = 0x37; // *
105 t[0x56] = 0x4A; // -
106 t[0x57] = 0x4E; // +
107 t[0x58] = 0xE01C; // Enter
108 t[0x59] = 0x4F; // 1
109 t[0x5A] = 0x50; // 2
110 t[0x5B] = 0x51; // 3
111 t[0x5C] = 0x4B; // 4
112 t[0x5D] = 0x4C; // 5
113 t[0x5E] = 0x4D; // 6
114 t[0x5F] = 0x47; // 7
115 t[0x60] = 0x48; // 8
116 t[0x61] = 0x49; // 9
117 t[0x62] = 0x52; // 0
118 t[0x63] = 0x53; // .
119 t
120};
121
122/// Modifier bit -> scancode (set 1, high byte 0xE0 = E0-prefixed).
123static MOD_TO_SCAN: [u16; 8] = [
124 0x1D, // 0 LCtrl
125 0x2A, // 1 LShift
126 0x38, // 2 LAlt
127 0xE05B, // 3 LGui
128 0xE01D, // 4 RCtrl
129 0x36, // 5 RShift
130 0xE038, // 6 RAlt
131 0xE05C, // 7 RGui
132];
133
134// ---- scancode FIFO (shared: one PS/2 decoder consumer) ----
135const Q_SIZE: usize = 128;
136static mut Q: [u8; Q_SIZE] = [0; Q_SIZE];
137static Q_HEAD: core::sync::atomic::AtomicU32 = core::sync::atomic::AtomicU32::new(0);
138static Q_LEN: core::sync::atomic::AtomicU32 = core::sync::atomic::AtomicU32::new(0);
139
140/// Queue one scancode-set-1 byte (drops when the FIFO is full).
141pub fn push_byte(b: u8) {
142 unsafe {
143 let h = Q_HEAD.load(Ordering::Relaxed) as usize;
144 let l = Q_LEN.load(Ordering::Relaxed) as usize;
145 if l >= Q_SIZE {
146 return;
147 }
148 Q[(h + l) % Q_SIZE] = b;
149 Q_LEN.store((l + 1) as u32, Ordering::Relaxed);
150 }
151}
152
153/// Pop one queued scancode byte (callers feed it to `ps2::feed_scancode`).
154pub fn pop_byte() -> Option<u8> {
155 unsafe {
156 let l = Q_LEN.load(Ordering::Relaxed) as usize;
157 if l == 0 {
158 return None;
159 }
160 let h = Q_HEAD.load(Ordering::Relaxed) as usize;
161 let b = Q[h];
162 Q_HEAD.store(((h + 1) % Q_SIZE) as u32, Ordering::Relaxed);
163 Q_LEN.store((l - 1) as u32, Ordering::Relaxed);
164 Some(b)
165 }
166}
167
168/// Push the make (press) or break (release) scancode sequence for a
169/// scancode-map entry (0 = unmapped, 0x00XX = plain, 0xE0XX = E0-prefixed).
170pub fn push_scancode(scan: u16, press: bool) {
171 if scan == 0 {
172 return;
173 }
174 if scan & 0xFF00 == 0xE000 {
175 push_byte(0xE0);
176 let code = (scan & 0xFF) as u8;
177 push_byte(if press { code } else { code | 0x80 });
178 } else {
179 let code = scan as u8;
180 push_byte(if press { code } else { code | 0x80 });
181 }
182}
183
184/// Per-device boot-keyboard report differ. Boot protocol reports are
185/// 8 bytes: modifiers, reserved, 6 usage codes.
186pub struct Keyboard {
187 prev: [u8; 8],
188}
189
190impl Keyboard {
191 pub const fn new() -> Self {
192 Self { prev: [0; 8] }
193 }
194
195 /// Diff `report` against the previous one and queue make/break bytes.
196 pub fn diff(&mut self, report: &[u8]) {
197 let mut rep = [0u8; 8];
198 for (i, b) in rep.iter_mut().enumerate() {
199 *b = report.get(i).copied().unwrap_or(0);
200 }
201 let new_mods = rep[0];
202 let old_mods = self.prev[0];
203 for bit in 0..8 {
204 let new_set = new_mods & (1 << bit) != 0;
205 let old_set = old_mods & (1 << bit) != 0;
206 if new_set != old_set {
207 push_scancode(MOD_TO_SCAN[bit], new_set);
208 }
209 }
210 let new_keys = &rep[2..8];
211 let old_keys = &self.prev[2..8];
212 // Releases first (old keys not in the new set).
213 for &k in old_keys {
214 if k == 0 || new_keys.contains(&k) {
215 continue;
216 }
217 if (k as usize) < HID_TO_SCAN.len() {
218 push_scancode(HID_TO_SCAN[k as usize], false);
219 }
220 }
221 // Then presses (new keys not in the old set).
222 for &k in new_keys {
223 if k == 0 || old_keys.contains(&k) {
224 continue;
225 }
226 if (k as usize) < HID_TO_SCAN.len() {
227 push_scancode(HID_TO_SCAN[k as usize], true);
228 }
229 }
230 self.prev = rep;
231 }
232}