Skip to content

Commit 4db677f

Browse files
author
Andreas Wessing
committed
feat: add PS/2 keyboard interrupt driver
1 parent 57228ff commit 4db677f

5 files changed

Lines changed: 104 additions & 1 deletion

File tree

Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,11 @@ virtio-vsock = ["virtio"]
216216
## This is only useful on PCs (x86-64).
217217
vga = []
218218

219+
## Enables the PS/2 keyboard driver.
220+
##
221+
## This is only useful on PCs (x86-64).
222+
keyboard = []
223+
219224
#! ### Performance Features
220225

221226
## Disables putting the CPU to sleep.

src/arch/x86_64/kernel/interrupts.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,14 @@ pub(crate) fn install() {
157157
IRQ_NAMES.lock().insert(7, "FPU");
158158
}
159159

160-
pub(crate) fn install_handlers(handlers: InterruptHandlerMap) {
160+
pub(crate) fn install_handlers(#[allow(unused_mut)] mut handlers: InterruptHandlerMap) {
161+
#[cfg(feature = "keyboard")]
162+
{
163+
use crate::arch::kernel::keyboard::get_keyboard_handler;
164+
let (irq, handler) = get_keyboard_handler();
165+
handlers.entry(irq).or_default().push_back(handler);
166+
}
167+
161168
IRQ_HANDLERS.set(handlers).unwrap();
162169
}
163170

src/arch/x86_64/kernel/keyboard.rs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
use core::sync::atomic::{AtomicU8, AtomicUsize, Ordering};
2+
3+
use x86_64::instructions::port::Port;
4+
5+
use crate::kernel::interrupts;
6+
7+
const BUFFER_SIZE: usize = 256;
8+
#[allow(clippy::declare_interior_mutable_const)]
9+
const ATOMIC_ZERO: AtomicU8 = AtomicU8::new(0);
10+
const PS2_DATA_PORT: u16 = 0x60;
11+
const PS2_CMD_PORT: u16 = 0x64;
12+
const PS2_CMD_READ_CNFG: u8 = 0x20;
13+
const PS2_CMD_WRITE_CNFG: u8 = 0x60;
14+
const PS2_CMD_DISABLE_KEYBOARD: u8 = 0xad;
15+
const PS2_CMD_DISABLE_MOUSE: u8 = 0xa7;
16+
const PS2_CMD_ENABLE_KEYBOARD: u8 = 0xae;
17+
const PS2_CNFG_ENABLE_KEYBOARD_INTERRUPT: u8 = 0x01;
18+
const PS2_BUFFER_FULL: u8 = 0x01;
19+
20+
static KEYBOARD_BUFFER: [AtomicU8; BUFFER_SIZE] = [ATOMIC_ZERO; BUFFER_SIZE];
21+
static WRITE_INDEX: AtomicUsize = AtomicUsize::new(0);
22+
static READ_INDEX: AtomicUsize = AtomicUsize::new(0);
23+
24+
pub(crate) fn get_keyboard_handler() -> (u8, fn()) {
25+
unsafe {
26+
let mut cmd_port = Port::<u8>::new(PS2_CMD_PORT);
27+
let mut data_port = Port::<u8>::new(PS2_DATA_PORT);
28+
cmd_port.write(PS2_CMD_DISABLE_KEYBOARD);
29+
cmd_port.write(PS2_CMD_DISABLE_MOUSE);
30+
31+
while (cmd_port.read() & PS2_BUFFER_FULL) != 0 {
32+
let _ = data_port.read();
33+
}
34+
35+
cmd_port.write(PS2_CMD_READ_CNFG);
36+
let mut config = data_port.read();
37+
38+
config |= PS2_CNFG_ENABLE_KEYBOARD_INTERRUPT;
39+
40+
cmd_port.write(PS2_CMD_WRITE_CNFG);
41+
data_port.write(config);
42+
cmd_port.write(PS2_CMD_ENABLE_KEYBOARD);
43+
}
44+
fn keyboard_handler() {
45+
let mut data_port = Port::<u8>::new(PS2_DATA_PORT);
46+
let scancode = unsafe { data_port.read() };
47+
48+
let write_idx = WRITE_INDEX.load(Ordering::Relaxed);
49+
let next_write_idx = write_idx.wrapping_add(1) % BUFFER_SIZE;
50+
51+
let read_idx = READ_INDEX.load(Ordering::Acquire);
52+
if next_write_idx != read_idx {
53+
KEYBOARD_BUFFER[write_idx].store(scancode, Ordering::Release);
54+
WRITE_INDEX.store(next_write_idx, Ordering::Release);
55+
}
56+
}
57+
58+
interrupts::add_irq_name(1, "PS/2 Keyboard");
59+
60+
(1, keyboard_handler)
61+
}
62+
63+
/// Pops a scancode from the keyboard buffer, returning None if the buffer is empty.
64+
pub fn pop_scancode() -> Option<u8> {
65+
let read_idx = READ_INDEX.load(Ordering::Relaxed);
66+
let write_idx = WRITE_INDEX.load(Ordering::Acquire);
67+
68+
if read_idx == write_idx {
69+
None
70+
} else {
71+
let scancode = KEYBOARD_BUFFER[read_idx].load(Ordering::Acquire);
72+
READ_INDEX.store(read_idx.wrapping_add(1) % BUFFER_SIZE, Ordering::Release);
73+
Some(scancode)
74+
}
75+
}

src/arch/x86_64/kernel/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ pub mod gdt;
2020
pub mod interrupts;
2121
#[cfg(feature = "kernel-stack")]
2222
pub mod kernel_stack;
23+
#[cfg(feature = "keyboard")]
24+
pub mod keyboard;
2325
#[cfg(all(not(feature = "pci"), feature = "virtio"))]
2426
pub mod mmio;
2527
#[cfg(feature = "pci")]

src/syscalls/system.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,17 @@ use crate::arch::mm::paging::{BasePageSize, PageSize};
66
pub extern "C" fn sys_getpagesize() -> i32 {
77
BasePageSize::SIZE.try_into().unwrap()
88
}
9+
10+
#[cfg(all(target_arch = "x86_64", feature = "keyboard"))]
11+
#[hermit_macro::system]
12+
#[unsafe(no_mangle)]
13+
pub extern "C" fn sys_read_keyboard() -> u8 {
14+
crate::kernel::keyboard::pop_scancode().unwrap_or(0)
15+
}
16+
17+
#[cfg(not(all(target_arch = "x86_64", feature = "keyboard")))]
18+
#[hermit_macro::system]
19+
#[unsafe(no_mangle)]
20+
pub extern "C" fn sys_read_keyboard() -> u8 {
21+
0
22+
}

0 commit comments

Comments
 (0)