Skip to main content

strat9_kernel/arch/x86_64/
keyboard.rs

1//! PS/2 Keyboard driver (inspired by MaestroOS `keyboard.rs`)
2//!
3//! Handles IRQ1 keyboard interrupts, reads scancodes from port 0x60,
4//! and converts them to characters for the VGA console.
5
6use super::io::{inb, outb};
7use spin::Mutex;
8
9/// Keyboard input buffer size
10const KEYBOARD_BUFFER_SIZE: usize = 256;
11
12// ========== Interrupt-safe ring buffer ======================================================================================================================================================
13//
14// The previous design used three separate `Mutex` fields (buffer, head, tail).
15// `push()` : called from the keyboard ISR : acquired them in order
16// tail → buffer → head.  `pop()` : called from task context : acquired them in
17// order head → tail → buffer.
18//
19// This created a classic spinlock + interrupt deadlock:
20//   1. Task context calls `pop()` → acquires `head` lock.
21//   2. Keyboard IRQ fires before `pop()` releases `head`.
22//   3. ISR calls `push()` → acquires `tail`, then `buffer`, then tries `head`
23//      → spins forever because `head` is still held by the preempted task.
24//
25// Fix: collapse all state into a SINGLE `Mutex` and, in the non-ISR path
26// (`pop` / `has_data`), disable interrupts BEFORE taking the lock.
27// The ISR already runs with IF=0, so it never needs to disable interrupts.
28
29struct KeyboardBufferInner {
30    buf: [u8; KEYBOARD_BUFFER_SIZE],
31    head: usize,
32    tail: usize,
33}
34
35struct KeyboardBuffer {
36    inner: Mutex<KeyboardBufferInner>,
37}
38
39static KEYBOARD_BUFFER: KeyboardBuffer = KeyboardBuffer::new();
40
41impl KeyboardBuffer {
42    /// Creates a new instance.
43    const fn new() -> Self {
44        Self {
45            inner: Mutex::new(KeyboardBufferInner {
46                buf: [0u8; KEYBOARD_BUFFER_SIZE],
47                head: 0,
48                tail: 0,
49            }),
50        }
51    }
52
53    /// Called exclusively from IRQ context (IF=0 already).  No need to
54    /// save/restore flags here.
55    pub fn push(&self, ch: u8) {
56        let mut g = self.inner.lock();
57        let tail = g.tail;
58        g.buf[tail] = ch;
59        g.tail = (tail + 1) % KEYBOARD_BUFFER_SIZE;
60        // Buffer full : drop oldest character silently.
61        if g.head == g.tail {
62            g.head = (g.head + 1) % KEYBOARD_BUFFER_SIZE;
63        }
64    }
65
66    /// Called from task context.  We must disable interrupts before taking
67    /// the lock so that the keyboard ISR cannot fire while we hold it.
68    pub fn pop(&self) -> Option<u8> {
69        let saved = super::save_flags_and_cli();
70        let result = {
71            let mut g = self.inner.lock();
72            if g.head == g.tail {
73                None
74            } else {
75                let ch = g.buf[g.head];
76                g.head = (g.head + 1) % KEYBOARD_BUFFER_SIZE;
77                Some(ch)
78            }
79        };
80        super::restore_flags(saved);
81        result
82    }
83
84    /// Called from task context : same interrupt-disable discipline as `pop`.
85    pub fn has_data(&self) -> bool {
86        let saved = super::save_flags_and_cli();
87        let result = {
88            let g = self.inner.lock();
89            g.head != g.tail
90        };
91        super::restore_flags(saved);
92        result
93    }
94}
95
96/// Add a character to the keyboard buffer (called from IRQ context).
97///
98/// Ctrl+C (0x03) is also pushed to the buffer but additionally sets the
99/// global [`crate::shell::SHELL_INTERRUPTED`] flag so that long-running
100/// commands can detect cancellation.
101///
102/// Must NOT call serial_force_println! or any lock that could contend with
103/// the interrupted task (e.g. FORCE_LOCK). Redox/Maestro/Asterinas keep
104/// IRQ handlers minimal; printing here can deadlock if the preempted task
105/// was holding FORCE_LOCK.
106pub fn add_to_buffer(ch: u8) {
107    if ch == 0x03 {
108        crate::shell::SHELL_INTERRUPTED.store(true, core::sync::atomic::Ordering::Relaxed);
109    }
110    KEYBOARD_BUFFER.push(ch);
111}
112
113/// Get a character from the keyboard buffer (non-blocking, task context only).
114pub fn read_char() -> Option<u8> {
115    KEYBOARD_BUFFER.pop()
116}
117
118/// Check if keyboard buffer has data (task context only).
119pub fn has_input() -> bool {
120    KEYBOARD_BUFFER.has_data()
121}
122
123/// Inject a PS/2 scancode from USB HID or other non-PS/2 source.
124///
125/// Must be called from task context. Converts scancode to character via
126/// the current layout and pushes to the buffer if applicable.
127/// Used by hid::poll_all() when USB HID reports arrive.
128///
129/// - `scancode`: PS/2 Set 1 scancode (0x00-0x7F for press, or 0x80|code for release)
130/// - `pressed`: true = key press, false = key release
131pub fn inject_hid_scancode(scancode: u8, pressed: bool) {
132    let raw = if pressed { scancode } else { scancode | 0x80 };
133    if let Some(ch) = super::keyboard_layout::handle_scancode_raw(raw) {
134        let saved = super::save_flags_and_cli();
135        KEYBOARD_BUFFER.push(ch);
136        super::restore_flags(saved);
137    }
138}
139
140/// PS/2 keyboard data port
141const KEYBOARD_DATA_PORT: u16 = 0x60;
142const PS2_CMD_PORT: u16 = 0x64;
143
144const CMD_READ_CFG: u8 = 0x20;
145const CMD_WRITE_CFG: u8 = 0x60;
146const CMD_ENABLE_KBD: u8 = 0xAE;
147
148const STATUS_OUTPUT_FULL: u8 = 0x01;
149const STATUS_INPUT_FULL: u8 = 0x02;
150
151#[inline]
152fn wait_write() {
153    for _ in 0..100_000u32 {
154        if unsafe { inb(PS2_CMD_PORT) } & STATUS_INPUT_FULL == 0 {
155            return;
156        }
157        core::hint::spin_loop();
158    }
159}
160
161#[inline]
162fn wait_read() {
163    for _ in 0..100_000u32 {
164        if unsafe { inb(PS2_CMD_PORT) } & STATUS_OUTPUT_FULL != 0 {
165            return;
166        }
167        core::hint::spin_loop();
168    }
169}
170
171fn ps2_read() -> u8 {
172    wait_read();
173    unsafe { inb(KEYBOARD_DATA_PORT) }
174}
175
176fn ps2_write_cmd(cmd: u8) {
177    wait_write();
178    unsafe { outb(PS2_CMD_PORT, cmd) };
179}
180
181fn ps2_write_data(data: u8) {
182    wait_write();
183    unsafe { outb(KEYBOARD_DATA_PORT, data) };
184}
185
186fn flush_output() {
187    for _ in 0..16 {
188        if unsafe { inb(PS2_CMD_PORT) } & STATUS_OUTPUT_FULL == 0 {
189            break;
190        }
191        unsafe { inb(KEYBOARD_DATA_PORT) };
192    }
193}
194
195/// Initialize the PS/2 keyboard controler
196///
197/// The firmware typically leaves IRQ1 enabled, but this is not guaranteed.
198/// After APIC/IOAPIC reconfiguration and mouse initialization, we explicitly
199/// enable the keyboard port, IRQ1, and the keyboard clock.
200pub fn init() {
201    flush_output();
202
203    ps2_write_cmd(CMD_ENABLE_KBD);
204
205    ps2_write_cmd(CMD_READ_CFG);
206    let mut cfg = ps2_read();
207    cfg |= 0x01;
208    cfg &= !0x10;
209    ps2_write_cmd(CMD_WRITE_CFG);
210    ps2_write_data(cfg);
211
212    flush_output();
213}
214
215/// Keyboard state
216pub struct KeyboardState {
217    /// Left shift pressed
218    pub left_shift: bool,
219    /// Right shift pressed
220    pub right_shift: bool,
221    /// Caps lock active
222    pub caps_lock: bool,
223    /// Control pressed
224    pub ctrl: bool,
225    /// Alt pressed
226    pub alt: bool,
227}
228
229impl KeyboardState {
230    /// Creates a new instance.
231    pub const fn new() -> Self {
232        Self {
233            left_shift: false,
234            right_shift: false,
235            caps_lock: false,
236            ctrl: false,
237            alt: false,
238        }
239    }
240
241    /// Returns whether shift is active (shift XOR caps_lock for letters)
242    pub fn shift_active(&self) -> bool {
243        self.left_shift || self.right_shift
244    }
245}
246
247/// Global keyboard state
248pub static KEYBOARD: Mutex<KeyboardState> = Mutex::new(KeyboardState::new());
249
250/// French AZERTY scancode set 1 -> ASCII mapping (lowercase)
251static SCANCODE_TO_ASCII: [u8; 128] = {
252    let mut table = [0u8; 128];
253    // Row 1: Esc, 1-0, -, =, Backspace
254    table[0x01] = 0x1B; // Esc
255    table[0x02] = b'&'; // 1
256    table[0x03] = b'e'; // 2 (é -> e)
257    table[0x04] = b'"'; // 3
258    table[0x05] = b'\''; // 4
259    table[0x06] = b'('; // 5
260    table[0x07] = b'-'; // 6
261    table[0x08] = b'e'; // 7 (è -> e)
262    table[0x09] = b'_'; // 8
263    table[0x0A] = b'c'; // 9 (ç -> c)
264    table[0x0B] = b'a'; // 0 (à -> a)
265    table[0x0C] = b')'; // -
266    table[0x0D] = b'='; // =
267    table[0x0E] = 0x08; // Backspace
268                        // Row 2: Tab, A-Z, [, ], Enter
269    table[0x0F] = b'\t';
270    table[0x10] = b'a'; // Q
271    table[0x11] = b'z'; // W
272    table[0x12] = b'e'; // E
273    table[0x13] = b'r'; // R
274    table[0x14] = b't'; // T
275    table[0x15] = b'y'; // Y
276    table[0x16] = b'u'; // U
277    table[0x17] = b'i'; // I
278    table[0x18] = b'o'; // O
279    table[0x19] = b'p'; // P
280    table[0x1A] = b'^'; // [
281    table[0x1B] = b'$'; // ]
282    table[0x1C] = b'\n'; // Enter
283                         // Row 3: Ctrl, Q-S, M, ;, :, `
284    table[0x1E] = b'q'; // A
285    table[0x1F] = b's'; // S
286    table[0x20] = b'd'; // D
287    table[0x21] = b'f'; // F
288    table[0x22] = b'g'; // G
289    table[0x23] = b'h'; // H
290    table[0x24] = b'j'; // J
291    table[0x25] = b'k'; // K
292    table[0x26] = b'l'; // L
293    table[0x27] = b'm'; // ;
294    table[0x28] = b'u'; // ' (ù -> u)
295    table[0x29] = b'*'; // `
296                        // Row 4: LShift, <, W-X, C-V, B-N, ,-. /?
297    table[0x2A] = 0x00; // LShift (handled separately)
298    table[0x2B] = b'<'; // \
299    table[0x2C] = b'w'; // Z
300    table[0x2D] = b'x'; // X
301    table[0x2E] = b'c'; // C
302    table[0x2F] = b'v'; // V
303    table[0x30] = b'b'; // B
304    table[0x31] = b'n'; // N
305    table[0x32] = b','; // M
306    table[0x33] = b';'; // ,
307    table[0x34] = b'.'; // .
308    table[0x35] = b'/'; // /
309                        // Space
310    table[0x39] = b' ';
311    table
312};
313
314/// Shifted scancode -> ASCII mapping for French AZERTY
315static SCANCODE_TO_ASCII_SHIFT: [u8; 128] = {
316    let mut table = [0u8; 128];
317    table[0x01] = 0x1B; // Esc
318    table[0x02] = b'1'; // &
319    table[0x03] = b'2'; // é
320    table[0x04] = b'3'; // "
321    table[0x05] = b'4'; // '
322    table[0x06] = b'5'; // (
323    table[0x07] = b'6'; // -
324    table[0x08] = b'7'; // è
325    table[0x09] = b'8'; // _
326    table[0x0A] = b'9'; // ç
327    table[0x0B] = b'0'; // à
328    table[0x0C] = b')'; // ° -> )
329    table[0x0D] = b'+'; // =
330    table[0x0E] = 0x08; // Backspace
331    table[0x0F] = b'\t'; // Tab
332    table[0x10] = b'A'; // Q
333    table[0x11] = b'Z'; // W
334    table[0x12] = b'E'; // E
335    table[0x13] = b'R'; // R
336    table[0x14] = b'T'; // T
337    table[0x15] = b'Y'; // Y
338    table[0x16] = b'U'; // U
339    table[0x17] = b'I'; // I
340    table[0x18] = b'O'; // O
341    table[0x19] = b'P'; // P
342    table[0x1A] = b'^'; // ¨ -> ^
343    table[0x1B] = b'*'; // $
344    table[0x1C] = b'\n'; // Enter
345    table[0x1E] = b'Q'; // A
346    table[0x1F] = b'S'; // S
347    table[0x20] = b'D'; // D
348    table[0x21] = b'F'; // F
349    table[0x22] = b'G'; // G
350    table[0x23] = b'H'; // H
351    table[0x24] = b'J'; // J
352    table[0x25] = b'K'; // K
353    table[0x26] = b'L'; // L
354    table[0x27] = b'M'; // ;
355    table[0x28] = b'%'; // '
356    table[0x29] = b'm'; // µ -> m
357    table[0x2B] = b'>'; // <
358    table[0x2C] = b'W'; // Z
359    table[0x2D] = b'X'; // X
360    table[0x2E] = b'C'; // C
361    table[0x2F] = b'V'; // V
362    table[0x30] = b'B'; // B
363    table[0x31] = b'N'; // N
364    table[0x32] = b'?'; // ,
365    table[0x33] = b'.'; // ;
366    table[0x34] = b','; // .
367    table[0x35] = b'/'; // § -> /
368    table[0x39] = b' '; // Space
369    table
370};
371
372// Special key constants (non-ASCII, outside 0-127)
373pub const KEY_UP: u8 = 0x80;
374pub const KEY_DOWN: u8 = 0x81;
375pub const KEY_LEFT: u8 = 0x82;
376pub const KEY_RIGHT: u8 = 0x83;
377pub const KEY_HOME: u8 = 0x84;
378pub const KEY_END: u8 = 0x85;
379
380/// Handle a keyboard IRQ (called from interrupt handler)
381///
382/// Returns the ASCII character if a printable key was pressed,
383/// or None for modifier keys / key releases.
384pub fn handle_scancode() -> Option<u8> {
385    let scancode = unsafe { inb(KEYBOARD_DATA_PORT) };
386    handle_scancode_raw(scancode)
387}
388
389/// Same as `handle_scancode` but takes a pre-read scancode byte.
390pub fn handle_scancode_raw(scancode: u8) -> Option<u8> {
391    let mut kbd = KEYBOARD.lock();
392
393    // Key release (bit 7 set)
394    if scancode & 0x80 != 0 {
395        let released = scancode & 0x7F;
396        match released {
397            0x2A => kbd.left_shift = false,
398            0x36 => kbd.right_shift = false,
399            0x1D => kbd.ctrl = false,
400            0x38 => kbd.alt = false,
401            _ => {}
402        }
403        return None;
404    }
405
406    // Key press
407    match scancode {
408        0x2A => {
409            kbd.left_shift = true;
410            return None;
411        }
412        0x36 => {
413            kbd.right_shift = true;
414            return None;
415        }
416        0x1D => {
417            kbd.ctrl = true;
418            return None;
419        }
420        0x38 => {
421            kbd.alt = true;
422            return None;
423        }
424        0x3A => {
425            kbd.caps_lock = !kbd.caps_lock;
426            return None;
427        }
428        // Arrow keys and special keys (Set 1 scancodes)
429        0x48 => return Some(KEY_UP),
430        0x50 => return Some(KEY_DOWN),
431        0x4B => return Some(KEY_LEFT),
432        0x4D => return Some(KEY_RIGHT),
433        0x47 => return Some(KEY_HOME),
434        0x4F => return Some(KEY_END),
435        _ => {}
436    }
437
438    // Convert scancode to ASCII
439    if scancode < 128 {
440        let shift = kbd.shift_active();
441        let ch = if shift {
442            SCANCODE_TO_ASCII_SHIFT[scancode as usize]
443        } else {
444            SCANCODE_TO_ASCII[scancode as usize]
445        };
446
447        // Handle caps lock for letters
448        if kbd.caps_lock && ch.is_ascii_alphabetic() {
449            let ch = if shift {
450                ch.to_ascii_lowercase()
451            } else {
452                ch.to_ascii_uppercase()
453            };
454            if ch != 0 {
455                return Some(ch);
456            }
457        }
458
459        if ch != 0 {
460            return Some(ch);
461        }
462    }
463
464    None
465}