-
Notifications
You must be signed in to change notification settings - Fork 129
feat: add PS/2 keyboard interrupt driver #2532
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 4 commits
4db677f
153302f
df6d5cf
68d8082
2913b39
166f396
2167837
a0b942a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would the pc-keyboard crate help here in any way? I'd like to avoid reimplementing logic if the ecosystem already has a well-established crate for (parts of) this.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I took a look at the crate and the following stood out to me: There are three basic steps to handling keyboard input. Your application may bypass some of these.
We actually don't need the first step because we are using the i8042 keyboard controller.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree with @GloriousAlpaca. This is not something for the kernel. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,85 @@ | ||
| use alloc::collections::VecDeque; | ||
|
|
||
| use hermit_sync::{InterruptTicketMutex, Lazy}; | ||
| use x86_64::instructions::port::Port; | ||
|
|
||
| use crate::kernel::interrupts; | ||
|
|
||
| const PS2_DATA_PORT: u16 = 0x60; | ||
| const PS2_CMD_PORT: u16 = 0x64; | ||
| const PS2_CMD_READ_CNFG: u8 = 0x20; | ||
| const PS2_CMD_WRITE_CNFG: u8 = 0x60; | ||
| const PS2_CMD_DISABLE_KEYBOARD: u8 = 0xad; | ||
| const PS2_CMD_DISABLE_MOUSE: u8 = 0xa7; | ||
| const PS2_CMD_ENABLE_KEYBOARD: u8 = 0xae; | ||
|
GloriousAlpaca marked this conversation as resolved.
Outdated
|
||
| const PS2_CNFG_ENABLE_KEYBOARD_INTERRUPT: u8 = 0x01; | ||
| const PS2_BUFFER_FULL: u8 = 0x01; | ||
|
|
||
| const BUFFER_SIZE: usize = 256; | ||
| struct Ps2; | ||
|
|
||
|
GloriousAlpaca marked this conversation as resolved.
Outdated
|
||
| impl Ps2 { | ||
| pub fn read_status() -> u8 { | ||
| let mut status_port = Port::<u8>::new(PS2_CMD_PORT); | ||
| unsafe { status_port.read() } | ||
|
GloriousAlpaca marked this conversation as resolved.
Outdated
|
||
| } | ||
|
|
||
| pub fn write_cmd(cmd: u8) { | ||
| let mut cmd_port = Port::<u8>::new(PS2_CMD_PORT); | ||
| unsafe { cmd_port.write(cmd) } | ||
| } | ||
|
|
||
| pub fn read_data() -> u8 { | ||
| let mut data_port = Port::<u8>::new(PS2_DATA_PORT); | ||
| unsafe { data_port.read() } | ||
| } | ||
|
|
||
| pub fn write_data(data: u8) { | ||
| let mut data_port = Port::<u8>::new(PS2_DATA_PORT); | ||
| unsafe { data_port.write(data) } | ||
| } | ||
| } | ||
|
|
||
| static KEYBOARD_BUFFER: Lazy<InterruptTicketMutex<VecDeque<u8>>> = | ||
| Lazy::new(|| InterruptTicketMutex::new(VecDeque::with_capacity(BUFFER_SIZE))); | ||
|
|
||
| fn keyboard_handler() { | ||
| let scancode = Ps2::read_data(); | ||
| let mut buffer = KEYBOARD_BUFFER.lock(); | ||
|
|
||
| if buffer.len() >= BUFFER_SIZE { | ||
| buffer.pop_front(); | ||
| } | ||
| buffer.push_back(scancode); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if retaining the keys in the queue is how this is handled best. I'm thinking that maybe adding a timestamp to each key event and discarding it after x seconds is a correct approach. But maybe I'm prematurely optimizing this. It would be interesting to know how other systems are handling this. |
||
| } | ||
|
|
||
| pub(crate) fn get_keyboard_handler() -> (u8, fn()) { | ||
| Ps2::write_cmd(PS2_CMD_DISABLE_KEYBOARD); | ||
| Ps2::write_cmd(PS2_CMD_DISABLE_MOUSE); | ||
| // Ensure an empty buffer to guard against stuck data | ||
| while (Ps2::read_status() & PS2_BUFFER_FULL) != 0 { | ||
| let _ = Ps2::read_data(); | ||
| } | ||
|
|
||
| Ps2::write_cmd(PS2_CMD_READ_CNFG); | ||
| let mut config = Ps2::read_data(); | ||
|
|
||
| config |= PS2_CNFG_ENABLE_KEYBOARD_INTERRUPT; | ||
|
|
||
| Ps2::write_cmd(PS2_CMD_WRITE_CNFG); | ||
|
|
||
| Ps2::write_data(config); | ||
| Ps2::write_cmd(PS2_CMD_ENABLE_KEYBOARD); | ||
|
|
||
| // Force the initialization of the keyboard buffer to ensure it is ready before any interrupts occur. | ||
| Lazy::force(&KEYBOARD_BUFFER); | ||
|
|
||
| interrupts::add_irq_name(1, "PS/2 Keyboard"); | ||
|
|
||
| (1, keyboard_handler) | ||
| } | ||
|
|
||
| /// Pops a scancode from the keyboard buffer, returning None if the buffer is empty. | ||
| pub fn pop_scancode() -> Option<u8> { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A scancode can never be zero, right? Returning |
||
| KEYBOARD_BUFFER.lock().pop_front() | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.