diff options
Diffstat (limited to 'rsnix/src/hid.rs')
| -rw-r--r-- | rsnix/src/hid.rs | 232 |
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 | |||
| 11 | use 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). | ||
| 15 | static 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). | ||
| 123 | static 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) ---- | ||
| 135 | const Q_SIZE: usize = 128; | ||
| 136 | static mut Q: [u8; Q_SIZE] = [0; Q_SIZE]; | ||
| 137 | static Q_HEAD: core::sync::atomic::AtomicU32 = core::sync::atomic::AtomicU32::new(0); | ||
| 138 | static 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). | ||
| 141 | pub 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`). | ||
| 154 | pub 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). | ||
| 170 | pub 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. | ||
| 186 | pub struct Keyboard { | ||
| 187 | prev: [u8; 8], | ||
| 188 | } | ||
| 189 | |||
| 190 | impl 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 | } | ||
