⚠️ VeridianOS Kernel Documentation - This is low-level kernel code. All functions are unsafe unless explicitly marked otherwise. no_std

veridian_kernel/drivers/
keyboard.rs

1//! PS/2 keyboard driver for x86_64.
2//!
3//! Reads scancodes from I/O port 0x60, decodes them via the `pc_keyboard`
4//! crate (ScancodeSet1, US 104-key layout), and pushes decoded ASCII bytes
5//! to a lock-free ring buffer. The shell reads from this buffer.
6//!
7//! On non-x86_64 architectures, all functions are no-op stubs.
8
9use core::sync::atomic::{AtomicBool, AtomicU8, Ordering};
10
11static INITIALIZED: AtomicBool = AtomicBool::new(false);
12
13/// Check if the keyboard driver has been initialized.
14pub fn is_initialized() -> bool {
15    INITIALIZED.load(Ordering::Acquire)
16}
17
18// ---------------------------------------------------------------------------
19// Modifier state tracking
20// ---------------------------------------------------------------------------
21
22/// Bitmask: Shift is held.
23pub const MOD_SHIFT: u8 = 0x01;
24/// Bitmask: Ctrl is held.
25pub const MOD_CTRL: u8 = 0x02;
26/// Bitmask: Alt is held.
27pub const MOD_ALT: u8 = 0x04;
28/// Bitmask: Super/Win is held.
29pub const MOD_SUPER: u8 = 0x08;
30
31static MODIFIER_STATE: AtomicU8 = AtomicU8::new(0);
32
33/// Get the current modifier key bitmask.
34pub fn get_modifiers() -> u8 {
35    MODIFIER_STATE.load(Ordering::Relaxed)
36}
37
38// ---------------------------------------------------------------------------
39// GUI mode: single-byte key codes for special keys
40// ---------------------------------------------------------------------------
41
42/// When true, arrow/special keys emit single-byte codes (0x80+) instead of
43/// multi-byte ANSI escape sequences. This prevents the 0x1B ESC prefix from
44/// triggering the GUI exit guard.
45static GUI_MODE: AtomicBool = AtomicBool::new(false);
46
47/// Enable or disable GUI key encoding mode.
48pub fn set_gui_mode(enabled: bool) {
49    GUI_MODE.store(enabled, Ordering::Release);
50}
51
52/// Single-byte key code for Up arrow (GUI mode).
53pub const KEY_UP: u8 = 0x80;
54/// Single-byte key code for Down arrow (GUI mode).
55pub const KEY_DOWN: u8 = 0x81;
56/// Single-byte key code for Left arrow (GUI mode).
57pub const KEY_LEFT: u8 = 0x82;
58/// Single-byte key code for Right arrow (GUI mode).
59pub const KEY_RIGHT: u8 = 0x83;
60/// Single-byte key code for Home (GUI mode).
61pub const KEY_HOME: u8 = 0x84;
62/// Single-byte key code for End (GUI mode).
63pub const KEY_END: u8 = 0x85;
64/// Single-byte key code for Delete (GUI mode).
65pub const KEY_DELETE: u8 = 0x86;
66
67// ---------------------------------------------------------------------------
68// x86_64 implementation
69// ---------------------------------------------------------------------------
70
71#[cfg(target_arch = "x86_64")]
72mod x86_64_impl {
73    use core::sync::atomic::AtomicUsize;
74
75    use pc_keyboard::{layouts, DecodedKey, HandleControl, Keyboard, ScancodeSet1};
76    use spin::Mutex;
77
78    use super::*;
79
80    /// Ring buffer size for decoded key bytes (must be power of 2).
81    const KEY_BUFFER_SIZE: usize = 256;
82
83    /// Lock-free single-producer single-consumer ring buffer for decoded keys.
84    struct KeyBuffer {
85        buf: [u8; KEY_BUFFER_SIZE],
86        head: AtomicUsize,
87        tail: AtomicUsize,
88    }
89
90    impl KeyBuffer {
91        const fn new() -> Self {
92            Self {
93                buf: [0; KEY_BUFFER_SIZE],
94                head: AtomicUsize::new(0),
95                tail: AtomicUsize::new(0),
96            }
97        }
98
99        /// Push a byte (called from interrupt handler -- single producer).
100        fn push(&mut self, byte: u8) {
101            let head = self.head.load(Ordering::Relaxed);
102            let next = (head + 1) & (KEY_BUFFER_SIZE - 1);
103            let tail = self.tail.load(Ordering::Acquire);
104            if next == tail {
105                return; // Buffer full, drop key
106            }
107            self.buf[head] = byte;
108            self.head.store(next, Ordering::Release);
109        }
110
111        /// Pop a byte (called from shell main loop -- single consumer).
112        fn pop(&self) -> Option<u8> {
113            let tail = self.tail.load(Ordering::Relaxed);
114            let head = self.head.load(Ordering::Acquire);
115            if tail == head {
116                return None;
117            }
118            let byte = self.buf[tail];
119            self.tail
120                .store((tail + 1) & (KEY_BUFFER_SIZE - 1), Ordering::Release);
121            Some(byte)
122        }
123    }
124
125    // SAFETY: KeyBuffer uses atomic operations for head/tail synchronization.
126    // The push side (interrupt handler) is single-producer and pop side
127    // (shell loop) is single-consumer, making concurrent access safe.
128    unsafe impl Send for KeyBuffer {}
129    unsafe impl Sync for KeyBuffer {}
130
131    static KEY_BUFFER: Mutex<KeyBuffer> = Mutex::new(KeyBuffer::new());
132
133    static KEYBOARD: Mutex<Option<Keyboard<layouts::Us104Key, ScancodeSet1>>> = Mutex::new(None);
134
135    /// Initialize the PS/2 keyboard driver.
136    pub fn init() {
137        let kb = Keyboard::new(
138            ScancodeSet1::new(),
139            layouts::Us104Key,
140            HandleControl::MapLettersToUnicode,
141        );
142        *KEYBOARD.lock() = Some(kb);
143        INITIALIZED.store(true, Ordering::Release);
144    }
145
146    /// Handle a scancode from the PS/2 keyboard interrupt (vector 33).
147    ///
148    /// This function must NOT call println! or acquire any spinlock used
149    /// by the serial/fbcon output path.
150    pub fn handle_scancode(scancode: u8) {
151        use pc_keyboard::KeyCode;
152
153        let mut kb_guard = KEYBOARD.lock();
154        if let Some(ref mut keyboard) = *kb_guard {
155            if let Ok(Some(key_event)) = keyboard.add_byte(scancode) {
156                // Track modifier state from the raw key event BEFORE
157                // process_keyevent consumes it. Modifiers are tracked
158                // globally so the render loop can detect hotkeys.
159                let code = key_event.code;
160                let is_down = key_event.state == pc_keyboard::KeyState::Down;
161                match code {
162                    KeyCode::LShift | KeyCode::RShift => update_modifier(MOD_SHIFT, is_down),
163                    KeyCode::LControl | KeyCode::RControl => update_modifier(MOD_CTRL, is_down),
164                    KeyCode::LAlt | KeyCode::RAltGr => update_modifier(MOD_ALT, is_down),
165                    KeyCode::LWin | KeyCode::RWin => update_modifier(MOD_SUPER, is_down),
166                    _ => {}
167                }
168
169                if let Some(key) = keyboard.process_keyevent(key_event) {
170                    match key {
171                        DecodedKey::Unicode(ch) => {
172                            if ch.is_ascii() {
173                                KEY_BUFFER.lock().push(ch as u8);
174                            }
175                        }
176                        DecodedKey::RawKey(key) => {
177                            if GUI_MODE.load(Ordering::Relaxed) {
178                                // GUI mode: emit single-byte codes for special
179                                // keys to avoid ANSI escape sequence conflicts.
180                                let gui_byte = match key {
181                                    KeyCode::ArrowUp => Some(KEY_UP),
182                                    KeyCode::ArrowDown => Some(KEY_DOWN),
183                                    KeyCode::ArrowRight => Some(KEY_RIGHT),
184                                    KeyCode::ArrowLeft => Some(KEY_LEFT),
185                                    KeyCode::Home => Some(KEY_HOME),
186                                    KeyCode::End => Some(KEY_END),
187                                    KeyCode::Delete => Some(KEY_DELETE),
188                                    _ => None,
189                                };
190                                if let Some(byte) = gui_byte {
191                                    KEY_BUFFER.lock().push(byte);
192                                }
193                            } else {
194                                // Shell mode: emit ANSI escape sequences as before.
195                                let seq: &[u8] = match key {
196                                    KeyCode::ArrowUp => b"\x1b[A",
197                                    KeyCode::ArrowDown => b"\x1b[B",
198                                    KeyCode::ArrowRight => b"\x1b[C",
199                                    KeyCode::ArrowLeft => b"\x1b[D",
200                                    KeyCode::Home => b"\x1b[H",
201                                    KeyCode::End => b"\x1b[F",
202                                    KeyCode::Delete => b"\x1b[3~",
203                                    _ => b"",
204                                };
205                                let mut buf = KEY_BUFFER.lock();
206                                for &byte in seq {
207                                    buf.push(byte);
208                                }
209                            }
210                        }
211                    }
212                }
213            }
214        }
215    }
216
217    /// Update a modifier bit in the global modifier state.
218    fn update_modifier(bit: u8, down: bool) {
219        if down {
220            MODIFIER_STATE.fetch_or(bit, Ordering::Relaxed);
221        } else {
222            MODIFIER_STATE.fetch_and(!bit, Ordering::Relaxed);
223        }
224    }
225
226    /// Read a decoded key byte from the keyboard buffer (non-blocking).
227    pub fn read_key() -> Option<u8> {
228        KEY_BUFFER.lock().pop()
229    }
230}
231
232#[cfg(target_arch = "x86_64")]
233pub use x86_64_impl::{handle_scancode, init, read_key};
234
235// ---------------------------------------------------------------------------
236// Stubs for non-x86_64 architectures
237// ---------------------------------------------------------------------------
238
239#[cfg(not(target_arch = "x86_64"))]
240pub fn init() {}
241
242#[cfg(not(target_arch = "x86_64"))]
243pub fn read_key() -> Option<u8> {
244    None
245}