Skip to main content

strat9_kernel/arch/x86_64/
keyboard_us.rs

1//! US QWERTY PS/2 Keyboard driver
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/// PS/2 keyboard data port
10const KEYBOARD_DATA_PORT: u16 = 0x60;
11
12/// Keyboard state
13pub struct KeyboardState {
14    /// Left shift pressed
15    pub left_shift: bool,
16    /// Right shift pressed
17    pub right_shift: bool,
18    /// Caps lock active
19    pub caps_lock: bool,
20    /// Control pressed
21    pub ctrl: bool,
22    /// Alt pressed
23    pub alt: bool,
24}
25
26impl KeyboardState {
27    /// Creates a new instance.
28    pub const fn new() -> Self {
29        Self {
30            left_shift: false,
31            right_shift: false,
32            caps_lock: false,
33            ctrl: false,
34            alt: false,
35        }
36    }
37
38    /// Returns whether shift is active (shift XOR caps_lock for letters)
39    pub fn shift_active(&self) -> bool {
40        self.left_shift || self.right_shift
41    }
42}
43
44/// Global keyboard state
45pub static KEYBOARD: Mutex<KeyboardState> = Mutex::new(KeyboardState::new());
46
47/// US QWERTY scancode set 1 -> ASCII mapping (lowercase)
48static SCANCODE_TO_ASCII: [u8; 128] = {
49    let mut table = [0u8; 128];
50    // Row 1: Esc, 1-0, -, =, Backspace
51    table[0x01] = 0x1B; // Esc
52    table[0x02] = b'1';
53    table[0x03] = b'2';
54    table[0x04] = b'3';
55    table[0x05] = b'4';
56    table[0x06] = b'5';
57    table[0x07] = b'6';
58    table[0x08] = b'7';
59    table[0x09] = b'8';
60    table[0x0A] = b'9';
61    table[0x0B] = b'0';
62    table[0x0C] = b'-';
63    table[0x0D] = b'=';
64    table[0x0E] = 0x08; // Backspace
65                        // Row 2: Tab, Q-P, [, ], Enter
66    table[0x0F] = b'\t';
67    table[0x10] = b'q';
68    table[0x11] = b'w';
69    table[0x12] = b'e';
70    table[0x13] = b'r';
71    table[0x14] = b't';
72    table[0x15] = b'y';
73    table[0x16] = b'u';
74    table[0x17] = b'i';
75    table[0x18] = b'o';
76    table[0x19] = b'p';
77    table[0x1A] = b'[';
78    table[0x1B] = b']';
79    table[0x1C] = b'\n'; // Enter
80                         // Row 3: Ctrl, A-L, ;, ', `
81    table[0x1E] = b'a';
82    table[0x1F] = b's';
83    table[0x20] = b'd';
84    table[0x21] = b'f';
85    table[0x22] = b'g';
86    table[0x23] = b'h';
87    table[0x24] = b'j';
88    table[0x25] = b'k';
89    table[0x26] = b'l';
90    table[0x27] = b';';
91    table[0x28] = b'\'';
92    table[0x29] = b'`';
93    // Row 4: LShift, \, Z-M, comma, dot, /
94    table[0x2B] = b'\\';
95    table[0x2C] = b'z';
96    table[0x2D] = b'x';
97    table[0x2E] = b'c';
98    table[0x2F] = b'v';
99    table[0x30] = b'b';
100    table[0x31] = b'n';
101    table[0x32] = b'm';
102    table[0x33] = b',';
103    table[0x34] = b'.';
104    table[0x35] = b'/';
105    // Space
106    table[0x39] = b' ';
107    table
108};
109
110/// Shifted scancode -> ASCII mapping
111static SCANCODE_TO_ASCII_SHIFT: [u8; 128] = {
112    let mut table = [0u8; 128];
113    table[0x01] = 0x1B;
114    table[0x02] = b'!';
115    table[0x03] = b'@';
116    table[0x04] = b'#';
117    table[0x05] = b'$';
118    table[0x06] = b'%';
119    table[0x07] = b'^';
120    table[0x08] = b'&';
121    table[0x09] = b'*';
122    table[0x0A] = b'(';
123    table[0x0B] = b')';
124    table[0x0C] = b'_';
125    table[0x0D] = b'+';
126    table[0x0E] = 0x08;
127    table[0x0F] = b'\t';
128    table[0x10] = b'Q';
129    table[0x11] = b'W';
130    table[0x12] = b'E';
131    table[0x13] = b'R';
132    table[0x14] = b'T';
133    table[0x15] = b'Y';
134    table[0x16] = b'U';
135    table[0x17] = b'I';
136    table[0x18] = b'O';
137    table[0x19] = b'P';
138    table[0x1A] = b'{';
139    table[0x1B] = b'}';
140    table[0x1C] = b'\n';
141    table[0x1E] = b'A';
142    table[0x1F] = b'S';
143    table[0x20] = b'D';
144    table[0x21] = b'F';
145    table[0x22] = b'G';
146    table[0x23] = b'H';
147    table[0x24] = b'J';
148    table[0x25] = b'K';
149    table[0x26] = b'L';
150    table[0x27] = b':';
151    table[0x28] = b'"';
152    table[0x29] = b'~';
153    table[0x2B] = b'|';
154    table[0x2C] = b'Z';
155    table[0x2D] = b'X';
156    table[0x2E] = b'C';
157    table[0x2F] = b'V';
158    table[0x30] = b'B';
159    table[0x31] = b'N';
160    table[0x32] = b'M';
161    table[0x33] = b'<';
162    table[0x34] = b'>';
163    table[0x35] = b'?';
164    table[0x39] = b' ';
165    table
166};
167
168/// Handle a keyboard IRQ (called from interrupt handler)
169///
170/// Returns the ASCII character if a printable key was pressed,
171/// or None for modifier keys / key releases.
172pub fn handle_scancode() -> Option<u8> {
173    let scancode = unsafe { inb(KEYBOARD_DATA_PORT) };
174    handle_scancode_raw(scancode)
175}
176
177/// Same as `handle_scancode` but takes a pre-read scancode byte.
178pub fn handle_scancode_raw(scancode: u8) -> Option<u8> {
179    let mut kbd = KEYBOARD.lock();
180
181    // Key release (bit 7 set)
182    if scancode & 0x80 != 0 {
183        let released = scancode & 0x7F;
184        match released {
185            0x2A => kbd.left_shift = false,
186            0x36 => kbd.right_shift = false,
187            0x1D => kbd.ctrl = false,
188            0x38 => kbd.alt = false,
189            _ => {}
190        }
191        return None;
192    }
193
194    // Key press
195    match scancode {
196        0x2A => {
197            kbd.left_shift = true;
198            return None;
199        }
200        0x36 => {
201            kbd.right_shift = true;
202            return None;
203        }
204        0x1D => {
205            kbd.ctrl = true;
206            return None;
207        }
208        0x38 => {
209            kbd.alt = true;
210            return None;
211        }
212        0x3A => {
213            kbd.caps_lock = !kbd.caps_lock;
214            return None;
215        }
216        _ => {}
217    }
218
219    // Convert scancode to ASCII
220    if scancode < 128 {
221        let shift = kbd.shift_active();
222        let ch = if shift {
223            SCANCODE_TO_ASCII_SHIFT[scancode as usize]
224        } else {
225            SCANCODE_TO_ASCII[scancode as usize]
226        };
227
228        // Handle caps lock for letters
229        if kbd.caps_lock && ch.is_ascii_alphabetic() {
230            let ch = if shift {
231                ch.to_ascii_lowercase()
232            } else {
233                ch.to_ascii_uppercase()
234            };
235            if ch != 0 {
236                return Some(ch);
237            }
238        }
239
240        if ch != 0 {
241            return Some(ch);
242        }
243    }
244
245    None
246}