mycelium_kernel/drivers/
ps2_keyboard.rs

1use maitake::sync::WaitQueue;
2use mycelium_util::{fmt, sync::blocking::Mutex};
3use pc_keyboard::{layouts, Keyboard};
4pub use pc_keyboard::{DecodedKey, KeyCode};
5
6pub struct Ps2Keyboard {
7    // TODO(eliza): this should use some kind of broadcast channel that waits
8    // for *all* readers to consume each keycode...
9    buf: thingbuf::StaticThingBuf<Option<DecodedKey>, 128>,
10    /// This doesn't strictly need to be a mutex; we will never spin while
11    /// trying to lock it. Instead, we use the mutex to assert that only
12    /// ISRs call `handle_scancode`.
13    // TODO(eliza): we probably shouldn't assume that the keyboard is always a
14    // US 104-key keyboard with scancode set 1, figure out how to
15    // detect/configure this...
16    kbd: Mutex<Keyboard<layouts::Us104Key, pc_keyboard::ScancodeSet1>>,
17    waiters: WaitQueue,
18}
19
20static PS2_KEYBOARD: Ps2Keyboard = Ps2Keyboard {
21    buf: thingbuf::StaticThingBuf::new(),
22    kbd: Mutex::new(
23        Keyboard::<layouts::Us104Key, pc_keyboard::ScancodeSet1>::new(
24            pc_keyboard::HandleControl::MapLettersToUnicode,
25        ),
26    ),
27    waiters: WaitQueue::new(),
28};
29
30pub async fn next_key() -> DecodedKey {
31    if let Some(key) = PS2_KEYBOARD.buf.pop() {
32        return key.expect("no one should push `None`s to the buffer, this is a bug");
33    }
34
35    PS2_KEYBOARD
36        .waiters
37        .wait()
38        .await
39        .expect("PS/2 keyboard waiters should never be closed, this is a bug");
40    PS2_KEYBOARD
41        .buf
42        .pop()
43        .expect("we just got woken up, there should be a key in the buffer")
44        .expect("no one should push `None`s to the buffer, this is a bug")
45}
46
47pub(crate) fn handle_scancode(scancode: u8) {
48    PS2_KEYBOARD
49        .kbd
50        .try_with_lock(|kbd| {
51            match kbd.add_byte(scancode) {
52                Err(error) => {
53                    tracing::warn!(
54                        ?error,
55                        scancode = fmt::hex(&scancode),
56                        "error decoding scancode, ignoring it!"
57                    );
58                }
59                // state advanced, no character decoded yet
60                Ok(None) => {}
61                // got a key event
62                Ok(Some(event)) => {
63                    if let Some(decoded_key) = kbd.process_keyevent(event) {
64                        // got something!
65                        if let Err(decoded_key) = PS2_KEYBOARD.buf.push(Some(decoded_key)) {
66                            tracing::warn!(
67                                ?decoded_key,
68                                "keyboard buffer full, dropping key event!"
69                            )
70                        }
71                        PS2_KEYBOARD.waiters.wake_all();
72                    }
73                }
74            };
75        })
76        .expect("handle_scancode should only be called in an ISR!");
77}