mycelium_kernel/drivers/
serial_input.rs

1use maitake::sync::WaitQueue;
2use mycelium_util::sync::InitOnce;
3
4pub struct SerialInput {
5    // TODO(eliza): this should use some kind of broadcast channel that waits
6    // for *all* readers to consume each byte...
7    buf: thingbuf::StaticThingBuf<Option<u8>, 128>,
8    waiters: WaitQueue,
9}
10
11impl SerialInput {
12    pub fn new() -> Self {
13        Self {
14            buf: thingbuf::StaticThingBuf::new(),
15            waiters: WaitQueue::new(),
16        }
17    }
18}
19
20impl Default for SerialInput {
21    fn default() -> Self {
22        Self::new()
23    }
24}
25
26pub static SERIAL_INPUTS: InitOnce<alloc::vec::Vec<SerialInput>> = InitOnce::uninitialized();
27
28impl SerialInput {
29    pub async fn next_byte(&self) -> u8 {
30        if let Some(byte) = self.buf.pop() {
31            return byte.expect("buffer contains somes");
32        }
33
34        self.waiters
35            .wait()
36            .await
37            .expect("serial waiters should never be closed FOR NOW");
38        self.buf
39            .pop()
40            .expect("just got woken up wtf")
41            .expect("buffer contains somes")
42    }
43
44    pub fn handle_input(&self, byte: u8) {
45        if let Err(byte) = self.buf.push(Some(byte)) {
46            // TODO(ixi): We were trying to handle serial console input but we're so backed up that
47            // we don't have space in the buffer to accept a byte. We should probably mask the
48            // interrupt until space in the buffer is available, but instead for now we'll eat the
49            // byte and drop it.
50            tracing::warn!(?byte, "serial buffer full, dropping byte!")
51        } else {
52            // We actually enqueued a byte, let everyone know.
53            self.waiters.wake_all();
54        }
55    }
56}