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}