summaryrefslogtreecommitdiffstats
path: root/rsnix/src/ps2.rs
diff options
context:
space:
mode:
Diffstat (limited to 'rsnix/src/ps2.rs')
-rw-r--r--rsnix/src/ps2.rs363
1 files changed, 363 insertions, 0 deletions
diff --git a/rsnix/src/ps2.rs b/rsnix/src/ps2.rs
new file mode 100644
index 0000000..b83a4e0
--- /dev/null
+++ b/rsnix/src/ps2.rs
@@ -0,0 +1,363 @@
1/// PS/2 keyboard driver — polling, scancode set 1, US QWERTY layout.
2// Port addresses
3const DATA_PORT: u16 = 0x60;
4const STATUS_PORT: u16 = 0x64;
5
6// Status register bits
7const STAT_OUTPUT_FULL: u8 = 0x01;
8
9// Modifier bit constants
10const MOD_LSHIFT: u8 = 0x01;
11const MOD_RSHIFT: u8 = 0x02;
12const MOD_LCTRL: u8 = 0x04;
13const MOD_RCTRL: u8 = 0x08;
14const MOD_LALT: u8 = 0x10;
15const MOD_RALT: u8 = 0x20;
16const MOD_CAPS: u8 = 0x40;
17
18/// A decoded key event.
19pub enum KeyEvent {
20 Char(char),
21 Enter,
22 Backspace,
23 Tab,
24 Escape,
25 ArrowUp,
26 ArrowDown,
27 ArrowLeft,
28 ArrowRight,
29 Home,
30 End,
31 PageUp,
32 PageDown,
33 Delete,
34 F(u8),
35 Unknown(u8),
36}
37
38// ----- scancode-set-1 translation tables (US QWERTY) -----
39
40/// Normal (unshifted) scancode → ASCII mapping. 0 = unmapped.
41static SCAN_NORMAL: [u8; 128] = {
42 let mut t = [0u8; 128];
43 t[0x02] = b'1';
44 t[0x03] = b'2';
45 t[0x04] = b'3';
46 t[0x05] = b'4';
47 t[0x06] = b'5';
48 t[0x07] = b'6';
49 t[0x08] = b'7';
50 t[0x09] = b'8';
51 t[0x0A] = b'9';
52 t[0x0B] = b'0';
53 t[0x0C] = b'-';
54 t[0x0D] = b'=';
55 t[0x10] = b'q';
56 t[0x11] = b'w';
57 t[0x12] = b'e';
58 t[0x13] = b'r';
59 t[0x14] = b't';
60 t[0x15] = b'y';
61 t[0x16] = b'u';
62 t[0x17] = b'i';
63 t[0x18] = b'o';
64 t[0x19] = b'p';
65 t[0x1A] = b'[';
66 t[0x1B] = b']';
67 t[0x1E] = b'a';
68 t[0x1F] = b's';
69 t[0x20] = b'd';
70 t[0x21] = b'f';
71 t[0x22] = b'g';
72 t[0x23] = b'h';
73 t[0x24] = b'j';
74 t[0x25] = b'k';
75 t[0x26] = b'l';
76 t[0x27] = b';';
77 t[0x28] = b'\'';
78 t[0x29] = b'`';
79 t[0x2B] = b'\\';
80 t[0x2C] = b'z';
81 t[0x2D] = b'x';
82 t[0x2E] = b'c';
83 t[0x2F] = b'v';
84 t[0x30] = b'b';
85 t[0x31] = b'n';
86 t[0x32] = b'm';
87 t[0x33] = b',';
88 t[0x34] = b'.';
89 t[0x35] = b'/';
90 t[0x37] = b'*';
91 t[0x39] = b' ';
92 t
93};
94
95/// Shifted scancode → ASCII.
96static SCAN_SHIFT: [u8; 128] = {
97 let mut t = [0u8; 128];
98 t[0x02] = b'!';
99 t[0x03] = b'@';
100 t[0x04] = b'#';
101 t[0x05] = b'$';
102 t[0x06] = b'%';
103 t[0x07] = b'^';
104 t[0x08] = b'&';
105 t[0x09] = b'*';
106 t[0x0A] = b'(';
107 t[0x0B] = b')';
108 t[0x0C] = b'_';
109 t[0x0D] = b'+';
110 t[0x10] = b'Q';
111 t[0x11] = b'W';
112 t[0x12] = b'E';
113 t[0x13] = b'R';
114 t[0x14] = b'T';
115 t[0x15] = b'Y';
116 t[0x16] = b'U';
117 t[0x17] = b'I';
118 t[0x18] = b'O';
119 t[0x19] = b'P';
120 t[0x1A] = b'{';
121 t[0x1B] = b'}';
122 t[0x1E] = b'A';
123 t[0x1F] = b'S';
124 t[0x20] = b'D';
125 t[0x21] = b'F';
126 t[0x22] = b'G';
127 t[0x23] = b'H';
128 t[0x24] = b'J';
129 t[0x25] = b'K';
130 t[0x26] = b'L';
131 t[0x27] = b':';
132 t[0x28] = b'"';
133 t[0x29] = b'~';
134 t[0x2B] = b'|';
135 t[0x2C] = b'Z';
136 t[0x2D] = b'X';
137 t[0x2E] = b'C';
138 t[0x2F] = b'V';
139 t[0x30] = b'B';
140 t[0x31] = b'N';
141 t[0x32] = b'M';
142 t[0x33] = b'<';
143 t[0x34] = b'>';
144 t[0x35] = b'?';
145 t[0x37] = b'*';
146 t[0x39] = b' ';
147 t
148};
149
150/// Internal keyboard state
151struct KbdState {
152 modifiers: u8,
153 got_e0: bool,
154}
155
156static mut KBD: KbdState = KbdState {
157 modifiers: 0,
158 got_e0: false,
159};
160
161/// Initialise the PS/2 port: drain stale bytes + enable keyboard interrupt.
162pub fn init() {
163 drain_input();
164
165 // Enable the keyboard interrupt on the PS/2 controller.
166 // Read Configuration Byte (command 0x20), set bit 0 (IRQ1 enable),
167 // write back (command 0x60).
168 unsafe {
169 // Tell the keyboard controller we want to read the config byte.
170 // Need to wait for the controller to be ready first.
171 wait_write();
172 core::arch::asm!("out dx, al", in("dx") 0x64u16, in("al") 0x20u8,
173 options(nostack, preserves_flags));
174 wait_read();
175 let mut cfg: u8;
176 core::arch::asm!("in al, dx", out("al") cfg, in("dx") 0x60u16,
177 options(nostack, preserves_flags));
178
179 cfg |= 0x01; // enable keyboard interrupt
180
181 // Write config byte back
182 wait_write();
183 core::arch::asm!("out dx, al", in("dx") 0x64u16, in("al") 0x60u8,
184 options(nostack, preserves_flags));
185 wait_write();
186 core::arch::asm!("out dx, al", in("dx") 0x60u16, in("al") cfg,
187 options(nostack, preserves_flags));
188 }
189}
190
191/// Wait until the PS/2 controller is ready to receive a command.
192fn wait_write() {
193 unsafe {
194 loop {
195 let status: u8;
196 core::arch::asm!("in al, dx", out("al") status, in("dx") 0x64u16,
197 options(nostack, preserves_flags));
198 if status & 0x02 == 0 {
199 // Input buffer empty — controller ready to receive
200 break;
201 }
202 }
203 }
204}
205
206/// Wait until the PS/2 controller has data available to read.
207fn wait_read() {
208 unsafe {
209 loop {
210 let status: u8;
211 core::arch::asm!("in al, dx", out("al") status, in("dx") 0x64u16,
212 options(nostack, preserves_flags));
213 if status & 0x01 != 0 {
214 // Output buffer full — data ready to read
215 break;
216 }
217 }
218 }
219}
220
221fn drain_input() {
222 unsafe {
223 loop {
224 let status: u8;
225 core::arch::asm!("in al, dx", out("al") status, in("dx") STATUS_PORT,
226 options(nostack, preserves_flags));
227 if status & STAT_OUTPUT_FULL == 0 {
228 break;
229 }
230 let _data: u8;
231 core::arch::asm!("in al, dx", out("al") _data, in("dx") DATA_PORT,
232 options(nostack, preserves_flags));
233 }
234 }
235}
236
237/// Poll for a key event. Blocks via `hlt` until a key is pressed.
238/// The keyboard interrupt handler pushes raw scancodes to a ring buffer;
239/// this function pops and decodes them.
240pub fn poll_key() -> KeyEvent {
241 loop {
242 if let Some(byte) = crate::interrupts::read_scancode() {
243 if let Some(ev) = process_byte(byte) {
244 return ev;
245 }
246 } else {
247 unsafe {
248 core::arch::asm!("sti; hlt", options(nostack, preserves_flags));
249 }
250 }
251 }
252}
253
254/// Non-blocking poll — read from the interrupt ring buffer if available.
255pub fn try_poll_key() -> Option<KeyEvent> {
256 let byte = crate::interrupts::read_scancode()?;
257 process_byte(byte)
258}
259
260/// Process a single scancode byte. Returns Some(KeyEvent) when a visible
261/// event happens; returns None for modifier changes, releases, and prefixes.
262fn process_byte(byte: u8) -> Option<KeyEvent> {
263 let state = unsafe { &mut KBD };
264
265 if byte == 0xE0 {
266 state.got_e0 = true;
267 return None;
268 }
269
270 if state.got_e0 {
271 state.got_e0 = false;
272 return process_e0(byte);
273 }
274
275 // Break code (key release) — bit 7 set
276 if byte & 0x80 != 0 {
277 update_modifiers(byte & 0x7F, false, false);
278 return None;
279 }
280
281 // Make code (key press)
282 update_modifiers(byte, true, false);
283
284 match byte {
285 0x01 => Some(KeyEvent::Escape),
286 0x0E => Some(KeyEvent::Backspace),
287 0x0F => Some(KeyEvent::Tab),
288 0x1C => Some(KeyEvent::Enter),
289 0x37 => Some(KeyEvent::Char('*')),
290 0x3B..=0x44 => Some(KeyEvent::F(byte - 0x3B + 1)),
291 _ => {
292 let shifted = (state.modifiers & (MOD_LSHIFT | MOD_RSHIFT)) != 0;
293 // CapsLock inverts case for letters
294 let caps = state.modifiers & MOD_CAPS != 0;
295 let table = if shifted { SCAN_SHIFT } else { SCAN_NORMAL };
296 let mut ch = table[byte as usize];
297 // Handle CapsLock for letters
298 if caps && ch.is_ascii_alphabetic() {
299 // Swap case: if table gives lowercase, make uppercase; etc.
300 // Since normal table has lowercase, shift table has uppercase:
301 ch = if shifted {
302 ch.to_ascii_lowercase()
303 } else {
304 ch.to_ascii_uppercase()
305 };
306 }
307 if ch != 0 {
308 Some(KeyEvent::Char(ch as char))
309 } else {
310 Some(KeyEvent::Unknown(byte))
311 }
312 }
313 }
314}
315
316/// Process an E0-prefixed scancode.
317fn process_e0(byte: u8) -> Option<KeyEvent> {
318 if byte & 0x80 != 0 {
319 update_modifiers(byte & 0x7F, false, true);
320 return None;
321 }
322
323 update_modifiers(byte, true, true);
324
325 match byte {
326 0x48 => Some(KeyEvent::ArrowUp),
327 0x50 => Some(KeyEvent::ArrowDown),
328 0x4B => Some(KeyEvent::ArrowLeft),
329 0x4D => Some(KeyEvent::ArrowRight),
330 0x47 => Some(KeyEvent::Home),
331 0x4F => Some(KeyEvent::End),
332 0x49 => Some(KeyEvent::PageUp),
333 0x51 => Some(KeyEvent::PageDown),
334 0x53 => Some(KeyEvent::Delete),
335 0x35 => Some(KeyEvent::Char('/')),
336 _ => Some(KeyEvent::Unknown(byte)),
337 }
338}
339
340fn update_modifiers(scancode: u8, pressed: bool, e0: bool) {
341 let state = unsafe { &mut KBD };
342 match (scancode, e0) {
343 (0x2A, false) => set_mod(state, MOD_LSHIFT, pressed),
344 (0x36, false) => set_mod(state, MOD_RSHIFT, pressed),
345 (0x1D, false) => set_mod(state, MOD_LCTRL, pressed),
346 (0x1D, true) => set_mod(state, MOD_RCTRL, pressed),
347 (0x38, false) => set_mod(state, MOD_LALT, pressed),
348 (0x38, true) => set_mod(state, MOD_RALT, pressed),
349 (0x3A, false)
350 if pressed => {
351 state.modifiers ^= MOD_CAPS;
352 }
353 _ => {}
354 }
355}
356
357fn set_mod(state: &mut KbdState, bit: u8, pressed: bool) {
358 if pressed {
359 state.modifiers |= bit;
360 } else {
361 state.modifiers &= !bit;
362 }
363}