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;
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.
101pub fn add_to_buffer(ch: u8) {
102    if ch == 0x03 {
103        crate::shell::SHELL_INTERRUPTED.store(true, core::sync::atomic::Ordering::Relaxed);
104    }
105    KEYBOARD_BUFFER.push(ch);
106}
107
108/// Get a character from the keyboard buffer (non-blocking, task context only).
109pub fn read_char() -> Option<u8> {
110    KEYBOARD_BUFFER.pop()
111}
112
113/// Check if keyboard buffer has data (task context only).
114pub fn has_input() -> bool {
115    KEYBOARD_BUFFER.has_data()
116}
117
118/// PS/2 keyboard data port
119const KEYBOARD_DATA_PORT: u16 = 0x60;
120
121/// Keyboard state
122pub struct KeyboardState {
123    /// Left shift pressed
124    pub left_shift: bool,
125    /// Right shift pressed
126    pub right_shift: bool,
127    /// Caps lock active
128    pub caps_lock: bool,
129    /// Control pressed
130    pub ctrl: bool,
131    /// Alt pressed
132    pub alt: bool,
133}
134
135impl KeyboardState {
136    /// Creates a new instance.
137    pub const fn new() -> Self {
138        Self {
139            left_shift: false,
140            right_shift: false,
141            caps_lock: false,
142            ctrl: false,
143            alt: false,
144        }
145    }
146
147    /// Returns whether shift is active (shift XOR caps_lock for letters)
148    pub fn shift_active(&self) -> bool {
149        self.left_shift || self.right_shift
150    }
151}
152
153/// Global keyboard state
154pub static KEYBOARD: Mutex<KeyboardState> = Mutex::new(KeyboardState::new());
155
156/// French AZERTY scancode set 1 -> ASCII mapping (lowercase)
157static SCANCODE_TO_ASCII: [u8; 128] = {
158    let mut table = [0u8; 128];
159    // Row 1: Esc, 1-0, -, =, Backspace
160    table[0x01] = 0x1B; // Esc
161    table[0x02] = b'&'; // 1
162    table[0x03] = b'e'; // 2 (é -> e)
163    table[0x04] = b'"'; // 3
164    table[0x05] = b'\''; // 4
165    table[0x06] = b'('; // 5
166    table[0x07] = b'-'; // 6
167    table[0x08] = b'e'; // 7 (è -> e)
168    table[0x09] = b'_'; // 8
169    table[0x0A] = b'c'; // 9 (ç -> c)
170    table[0x0B] = b'a'; // 0 (à -> a)
171    table[0x0C] = b')'; // -
172    table[0x0D] = b'='; // =
173    table[0x0E] = 0x08; // Backspace
174                        // Row 2: Tab, A-Z, [, ], Enter
175    table[0x0F] = b'\t';
176    table[0x10] = b'a'; // Q
177    table[0x11] = b'z'; // W
178    table[0x12] = b'e'; // E
179    table[0x13] = b'r'; // R
180    table[0x14] = b't'; // T
181    table[0x15] = b'y'; // Y
182    table[0x16] = b'u'; // U
183    table[0x17] = b'i'; // I
184    table[0x18] = b'o'; // O
185    table[0x19] = b'p'; // P
186    table[0x1A] = b'^'; // [
187    table[0x1B] = b'$'; // ]
188    table[0x1C] = b'\n'; // Enter
189                         // Row 3: Ctrl, Q-S, M, ;, :, `
190    table[0x1E] = b'q'; // A
191    table[0x1F] = b's'; // S
192    table[0x20] = b'd'; // D
193    table[0x21] = b'f'; // F
194    table[0x22] = b'g'; // G
195    table[0x23] = b'h'; // H
196    table[0x24] = b'j'; // J
197    table[0x25] = b'k'; // K
198    table[0x26] = b'l'; // L
199    table[0x27] = b'm'; // ;
200    table[0x28] = b'u'; // ' (ù -> u)
201    table[0x29] = b'*'; // `
202                        // Row 4: LShift, <, W-X, C-V, B-N, ,-. /?
203    table[0x2A] = 0x00; // LShift (handled separately)
204    table[0x2B] = b'<'; // \
205    table[0x2C] = b'w'; // Z
206    table[0x2D] = b'x'; // X
207    table[0x2E] = b'c'; // C
208    table[0x2F] = b'v'; // V
209    table[0x30] = b'b'; // B
210    table[0x31] = b'n'; // N
211    table[0x32] = b','; // M
212    table[0x33] = b';'; // ,
213    table[0x34] = b'.'; // .
214    table[0x35] = b'/'; // /
215                        // Space
216    table[0x39] = b' ';
217    table
218};
219
220/// Shifted scancode -> ASCII mapping for French AZERTY
221static SCANCODE_TO_ASCII_SHIFT: [u8; 128] = {
222    let mut table = [0u8; 128];
223    table[0x01] = 0x1B; // Esc
224    table[0x02] = b'1'; // &
225    table[0x03] = b'2'; // é
226    table[0x04] = b'3'; // "
227    table[0x05] = b'4'; // '
228    table[0x06] = b'5'; // (
229    table[0x07] = b'6'; // -
230    table[0x08] = b'7'; // è
231    table[0x09] = b'8'; // _
232    table[0x0A] = b'9'; // ç
233    table[0x0B] = b'0'; // à
234    table[0x0C] = b')'; // ° -> )
235    table[0x0D] = b'+'; // =
236    table[0x0E] = 0x08; // Backspace
237    table[0x0F] = b'\t'; // Tab
238    table[0x10] = b'A'; // Q
239    table[0x11] = b'Z'; // W
240    table[0x12] = b'E'; // E
241    table[0x13] = b'R'; // R
242    table[0x14] = b'T'; // T
243    table[0x15] = b'Y'; // Y
244    table[0x16] = b'U'; // U
245    table[0x17] = b'I'; // I
246    table[0x18] = b'O'; // O
247    table[0x19] = b'P'; // P
248    table[0x1A] = b'^'; // ¨ -> ^
249    table[0x1B] = b'*'; // $
250    table[0x1C] = b'\n'; // Enter
251    table[0x1E] = b'Q'; // A
252    table[0x1F] = b'S'; // S
253    table[0x20] = b'D'; // D
254    table[0x21] = b'F'; // F
255    table[0x22] = b'G'; // G
256    table[0x23] = b'H'; // H
257    table[0x24] = b'J'; // J
258    table[0x25] = b'K'; // K
259    table[0x26] = b'L'; // L
260    table[0x27] = b'M'; // ;
261    table[0x28] = b'%'; // '
262    table[0x29] = b'm'; // µ -> m
263    table[0x2B] = b'>'; // <
264    table[0x2C] = b'W'; // Z
265    table[0x2D] = b'X'; // X
266    table[0x2E] = b'C'; // C
267    table[0x2F] = b'V'; // V
268    table[0x30] = b'B'; // B
269    table[0x31] = b'N'; // N
270    table[0x32] = b'?'; // ,
271    table[0x33] = b'.'; // ;
272    table[0x34] = b','; // .
273    table[0x35] = b'/'; // § -> /
274    table[0x39] = b' '; // Space
275    table
276};
277
278// Special key constants (non-ASCII, outside 0-127)
279pub const KEY_UP: u8 = 0x80;
280pub const KEY_DOWN: u8 = 0x81;
281pub const KEY_LEFT: u8 = 0x82;
282pub const KEY_RIGHT: u8 = 0x83;
283pub const KEY_HOME: u8 = 0x84;
284pub const KEY_END: u8 = 0x85;
285
286/// Handle a keyboard IRQ (called from interrupt handler)
287///
288/// Returns the ASCII character if a printable key was pressed,
289/// or None for modifier keys / key releases.
290pub fn handle_scancode() -> Option<u8> {
291    let scancode = unsafe { inb(KEYBOARD_DATA_PORT) };
292    handle_scancode_raw(scancode)
293}
294
295/// Same as `handle_scancode` but takes a pre-read scancode byte.
296pub fn handle_scancode_raw(scancode: u8) -> Option<u8> {
297    let mut kbd = KEYBOARD.lock();
298
299    // Key release (bit 7 set)
300    if scancode & 0x80 != 0 {
301        let released = scancode & 0x7F;
302        match released {
303            0x2A => kbd.left_shift = false,
304            0x36 => kbd.right_shift = false,
305            0x1D => kbd.ctrl = false,
306            0x38 => kbd.alt = false,
307            _ => {}
308        }
309        return None;
310    }
311
312    // Key press
313    match scancode {
314        0x2A => {
315            kbd.left_shift = true;
316            return None;
317        }
318        0x36 => {
319            kbd.right_shift = true;
320            return None;
321        }
322        0x1D => {
323            kbd.ctrl = true;
324            return None;
325        }
326        0x38 => {
327            kbd.alt = true;
328            return None;
329        }
330        0x3A => {
331            kbd.caps_lock = !kbd.caps_lock;
332            return None;
333        }
334        // Arrow keys and special keys (Set 1 scancodes)
335        0x48 => return Some(KEY_UP),
336        0x50 => return Some(KEY_DOWN),
337        0x4B => return Some(KEY_LEFT),
338        0x4D => return Some(KEY_RIGHT),
339        0x47 => return Some(KEY_HOME),
340        0x4F => return Some(KEY_END),
341        _ => {}
342    }
343
344    // Convert scancode to ASCII
345    if scancode < 128 {
346        let shift = kbd.shift_active();
347        let ch = if shift {
348            SCANCODE_TO_ASCII_SHIFT[scancode as usize]
349        } else {
350            SCANCODE_TO_ASCII[scancode as usize]
351        };
352
353        // Handle caps lock for letters
354        if kbd.caps_lock && ch.is_ascii_alphabetic() {
355            let ch = if shift {
356                ch.to_ascii_lowercase()
357            } else {
358                ch.to_ascii_uppercase()
359            };
360            if ch != 0 {
361                return Some(ch);
362            }
363        }
364
365        if ch != 0 {
366            return Some(ch);
367        }
368    }
369
370    None
371}