Skip to content

Commit 45e3fd5

Browse files
authored
Keyboard (#66)
* Add keyboard interrupts * Add basic keyboard driver * Add basic mouse support
1 parent e3fd44d commit 45e3fd5

File tree

10 files changed

+1413
-4
lines changed

10 files changed

+1413
-4
lines changed

kernel/Cargo.lock

+8
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

kernel/Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,5 @@ arrayvec = { version = "0.7.6", default-features = false }
2626
log = { version = "0.4.25", default-features = false }
2727
async-trait = "0.1.86"
2828
fontdue = { version = "0.9.3", default-features = false, features = ["hashbrown"] }
29+
pc-keyboard = "0.8.0"
30+
futures-util = { version = "0.3.31", default-features = false, features = ["alloc", "async-await", "async-await-macro", "futures-macro"] }

kernel/src/constants/idt.rs

+2
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,7 @@
33
/// Vector number assigned to the timer interrupt.
44
pub const TIMER_VECTOR: u8 = 32;
55
pub const SYSCALL_HANDLER: u8 = 0x80;
6+
pub const KEYBOARD_VECTOR: u8 = 33;
7+
pub const MOUSE_VECTOR: u8 = 44;
68

79
pub const TLB_SHOOTDOWN_VECTOR: u8 = 33;

kernel/src/devices/keyboard.rs

+219
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
//! Keyboard management
2+
//!
3+
//! Currently does not support fancy stuff like key repeats
4+
use crate::{
5+
interrupts::{idt::without_interrupts, x2apic},
6+
serial_println,
7+
};
8+
use core::{
9+
pin::Pin,
10+
sync::atomic::{AtomicBool, AtomicU64, Ordering},
11+
task::{Context, Poll, Waker},
12+
};
13+
use futures_util::stream::{Stream, StreamExt}; // StreamExt trait for .next() method
14+
use pc_keyboard::{
15+
layouts, DecodedKey, Error, HandleControl, KeyCode, KeyState, Keyboard, Modifiers, ScancodeSet1,
16+
};
17+
use spin::Mutex;
18+
use x86_64::{instructions::port::Port, structures::idt::InterruptStackFrame};
19+
20+
/// Maximum number of keyboard events to store in the buffer
21+
const KEYBOARD_BUFFER_SIZE: usize = 32;
22+
23+
/// The global keyboard state
24+
pub static KEYBOARD: Mutex<KeyboardState> = Mutex::new(KeyboardState::new());
25+
26+
/// Has the keyboard been initialized
27+
static KEYBOARD_INITIALIZED: AtomicBool = AtomicBool::new(false);
28+
29+
/// The number of keyboard interrupts received
30+
static KEYBOARD_INTERRUPT_COUNT: AtomicU64 = AtomicU64::new(0);
31+
32+
/// Wake task waiting for keyboard input
33+
static KEYBOARD_WAKER: Mutex<Option<Waker>> = Mutex::new(None);
34+
35+
/// Represents a key event with additional metadata
36+
#[derive(Debug, Clone)]
37+
pub struct BufferKeyEvent {
38+
/// The key code from the event
39+
pub key_code: KeyCode,
40+
/// The key state (up or down)
41+
pub state: KeyState,
42+
/// The decoded key if applicable
43+
pub decoded: Option<DecodedKey>,
44+
}
45+
46+
/// Structure to track keyboard state
47+
pub struct KeyboardState {
48+
/// The pc_keyboard handler
49+
keyboard: Keyboard<layouts::Us104Key, ScancodeSet1>,
50+
/// Circular buffer for key events
51+
buffer: [Option<BufferKeyEvent>; KEYBOARD_BUFFER_SIZE],
52+
/// Read position in the buffer
53+
read_pos: usize,
54+
/// Write position in the buffer
55+
write_pos: usize,
56+
/// Is the buffer full
57+
full: bool,
58+
}
59+
60+
/// Stream that yields keyboard events
61+
pub struct KeyboardStream;
62+
63+
impl Default for KeyboardState {
64+
fn default() -> Self {
65+
Self::new()
66+
}
67+
}
68+
69+
impl KeyboardState {
70+
/// Create a new keyboard state
71+
pub const fn new() -> Self {
72+
const NONE_OPTION: Option<BufferKeyEvent> = None;
73+
Self {
74+
keyboard: Keyboard::new(
75+
ScancodeSet1::new(),
76+
layouts::Us104Key,
77+
HandleControl::Ignore,
78+
),
79+
buffer: [NONE_OPTION; KEYBOARD_BUFFER_SIZE],
80+
read_pos: 0,
81+
write_pos: 0,
82+
full: false,
83+
}
84+
}
85+
86+
/// Check if the buffer is empty
87+
pub fn is_empty(&self) -> bool {
88+
!self.full && self.read_pos == self.write_pos
89+
}
90+
91+
/// Process a scancode and add resulting key events to the buffer
92+
pub fn process_scancode(&mut self, scancode: u8) -> Result<(), Error> {
93+
if let Some(key_event) = self.keyboard.add_byte(scancode)? {
94+
let decoded = self.keyboard.process_keyevent(key_event.clone());
95+
96+
let buff_event = BufferKeyEvent {
97+
key_code: key_event.code,
98+
state: key_event.state,
99+
decoded,
100+
};
101+
102+
self.push_event(buff_event);
103+
}
104+
105+
Ok(())
106+
}
107+
108+
/// Push an event to the buffer
109+
fn push_event(&mut self, event: BufferKeyEvent) {
110+
if self.full {
111+
self.read_pos = (self.read_pos + 1) % KEYBOARD_BUFFER_SIZE;
112+
}
113+
114+
self.buffer[self.write_pos] = Some(event);
115+
116+
self.write_pos = (self.write_pos + 1) % KEYBOARD_BUFFER_SIZE;
117+
118+
if self.write_pos == self.read_pos {
119+
self.full = true;
120+
}
121+
122+
without_interrupts(|| {
123+
let mut waker = KEYBOARD_WAKER.lock();
124+
if let Some(w) = waker.take() {
125+
w.wake();
126+
}
127+
});
128+
}
129+
130+
/// Read a key event from the buffer
131+
pub fn read_event(&mut self) -> Option<BufferKeyEvent> {
132+
if self.is_empty() {
133+
return None;
134+
}
135+
136+
let event = self.buffer[self.read_pos].clone();
137+
138+
self.buffer[self.read_pos] = None;
139+
self.read_pos = (self.read_pos + 1) % KEYBOARD_BUFFER_SIZE;
140+
141+
self.full = false;
142+
143+
event
144+
}
145+
146+
/// Get current modifier state
147+
pub fn modifiers(&self) -> &Modifiers {
148+
self.keyboard.get_modifiers()
149+
}
150+
151+
/// Clear keyboard buffer
152+
pub fn clear_buffer(&mut self) {
153+
const NONE_OPTION: Option<BufferKeyEvent> = None;
154+
self.buffer = [NONE_OPTION; KEYBOARD_BUFFER_SIZE];
155+
self.read_pos = 0;
156+
self.write_pos = 0;
157+
self.full = false;
158+
}
159+
}
160+
161+
/// Initialize the keyboard system
162+
pub fn init() -> Result<(), &'static str> {
163+
if KEYBOARD_INITIALIZED.load(Ordering::SeqCst) {
164+
return Ok(());
165+
}
166+
167+
KEYBOARD_INITIALIZED.store(true, Ordering::SeqCst);
168+
169+
Ok(())
170+
}
171+
172+
/// Get a stream of keyboard events
173+
pub fn get_stream() -> KeyboardStream {
174+
KeyboardStream
175+
}
176+
177+
/// Wait for and return the next key event
178+
pub async fn next_key() -> BufferKeyEvent {
179+
KeyboardStream.next().await.unwrap()
180+
}
181+
182+
/// Read a key without waiting
183+
pub fn try_read_key() -> Option<BufferKeyEvent> {
184+
without_interrupts(|| KEYBOARD.lock().read_event())
185+
}
186+
187+
pub extern "x86-interrupt" fn keyboard_handler(_frame: InterruptStackFrame) {
188+
KEYBOARD_INTERRUPT_COUNT.fetch_add(1, Ordering::SeqCst);
189+
190+
let mut port = Port::new(0x60);
191+
let scancode: u8 = unsafe { port.read() };
192+
193+
let mut keyboard = KEYBOARD.lock();
194+
if let Err(e) = keyboard.process_scancode(scancode) {
195+
serial_println!("Error processing keyboard scancode: {:?}", e);
196+
}
197+
198+
x2apic::send_eoi();
199+
}
200+
201+
impl Stream for KeyboardStream {
202+
type Item = BufferKeyEvent;
203+
204+
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
205+
let mut keyboard = KEYBOARD.lock();
206+
207+
if let Some(event) = keyboard.read_event() {
208+
return Poll::Ready(Some(event));
209+
}
210+
211+
// No event available, register waker for notification
212+
without_interrupts(|| {
213+
let mut waker = KEYBOARD_WAKER.lock();
214+
*waker = Some(cx.waker().clone());
215+
});
216+
217+
Poll::Pending
218+
}
219+
}

kernel/src/devices/mod.rs

+5
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ use pci::walk_pci_bus;
1010
use sd_card::{find_sd_card, initalize_sd_card};
1111
pub mod graphics;
1212
use graphics::framebuffer::{self, colors};
13+
pub mod keyboard;
14+
pub mod mouse;
1315
pub mod pci;
1416
pub mod sd_card;
1517
pub mod serial;
@@ -63,5 +65,8 @@ pub fn init(cpu_id: u32) {
6365
let mut mapper = MAPPER.lock();
6466
initalize_sd_card(&sd_card_device, &mut mapper).unwrap();
6567
serial_println!("Sd card initialized");
68+
69+
keyboard::init().expect("Failed to initialize keyboard");
70+
mouse::init().expect("Failed to initialize mouse");
6671
}
6772
}

0 commit comments

Comments
 (0)