use maitake::sync::WaitQueue;
use mycelium_util::{fmt, sync::blocking::Mutex};
use pc_keyboard::{layouts, Keyboard};
pub use pc_keyboard::{DecodedKey, KeyCode};
pub struct Ps2Keyboard {
buf: thingbuf::StaticThingBuf<Option<DecodedKey>, 128>,
kbd: Mutex<Keyboard<layouts::Us104Key, pc_keyboard::ScancodeSet1>>,
waiters: WaitQueue,
}
static PS2_KEYBOARD: Ps2Keyboard = Ps2Keyboard {
buf: thingbuf::StaticThingBuf::new(),
kbd: Mutex::new(
Keyboard::<layouts::Us104Key, pc_keyboard::ScancodeSet1>::new(
pc_keyboard::HandleControl::MapLettersToUnicode,
),
),
waiters: WaitQueue::new(),
};
pub async fn next_key() -> DecodedKey {
if let Some(key) = PS2_KEYBOARD.buf.pop() {
return key.expect("no one should push `None`s to the buffer, this is a bug");
}
PS2_KEYBOARD
.waiters
.wait()
.await
.expect("PS/2 keyboard waiters should never be closed, this is a bug");
PS2_KEYBOARD
.buf
.pop()
.expect("we just got woken up, there should be a key in the buffer")
.expect("no one should push `None`s to the buffer, this is a bug")
}
pub(crate) fn handle_scancode(scancode: u8) {
PS2_KEYBOARD
.kbd
.try_with_lock(|kbd| {
match kbd.add_byte(scancode) {
Err(error) => {
tracing::warn!(
?error,
scancode = fmt::hex(&scancode),
"error decoding scancode, ignoring it!"
);
}
Ok(None) => {}
Ok(Some(event)) => {
if let Some(decoded_key) = kbd.process_keyevent(event) {
if let Err(decoded_key) = PS2_KEYBOARD.buf.push(Some(decoded_key)) {
tracing::warn!(
?decoded_key,
"keyboard buffer full, dropping key event!"
)
}
PS2_KEYBOARD.waiters.wake_all();
}
}
};
})
.expect("handle_scancode should only be called in an ISR!");
}