feat: devices via caps (BootInfo, init assigns devices, drivers map MMIO/DMA themselves, IRQ-driven input); drop boot device wiring and info pages; headless display fallback
This commit is contained in:
@@ -1,20 +1,70 @@
|
||||
use server_framework::Server;
|
||||
|
||||
use port_input_device::InputDevice;
|
||||
use port_input_device::{InputDevice, InputEvent};
|
||||
use types::bounded::CapSlot;
|
||||
use crate::protocol::{InputRequest, InputResponse};
|
||||
|
||||
/// Events captured on interrupts and not yet asked for.
|
||||
const BUFFERED: usize = 32;
|
||||
|
||||
pub struct InputServer<D: InputDevice> {
|
||||
device: D,
|
||||
/// The IRQ to re-arm after each interrupt, if the device has one.
|
||||
irq: Option<CapSlot>,
|
||||
events: [Option<InputEvent>; BUFFERED],
|
||||
head: usize,
|
||||
len: usize,
|
||||
}
|
||||
|
||||
impl<D: InputDevice> InputServer<D> {
|
||||
/// A server that reads the device only when a client asks.
|
||||
pub fn new(device: D) -> Self {
|
||||
Self { device }
|
||||
Self { device, irq: None, events: [None; BUFFERED], head: 0, len: 0 }
|
||||
}
|
||||
|
||||
/// A server woken by `irq` (routed and bound by the caller): it drains
|
||||
/// the device into a buffer on every interrupt, so events that arrive
|
||||
/// between requests are kept.
|
||||
pub fn with_irq(device: D, irq: CapSlot) -> Self {
|
||||
Self { irq: Some(irq), ..Self::new(device) }
|
||||
}
|
||||
|
||||
fn push(&mut self, event: InputEvent) {
|
||||
if self.len == BUFFERED {
|
||||
// Full: the oldest event goes.
|
||||
self.head = (self.head + 1) % BUFFERED;
|
||||
self.len -= 1;
|
||||
}
|
||||
let tail = (self.head + self.len) % BUFFERED;
|
||||
if let Some(slot) = self.events.get_mut(tail) {
|
||||
*slot = Some(event);
|
||||
self.len += 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn pop(&mut self) -> Option<InputEvent> {
|
||||
if self.len == 0 {
|
||||
return None;
|
||||
}
|
||||
let event = self.events.get_mut(self.head)?.take();
|
||||
self.head = (self.head + 1) % BUFFERED;
|
||||
self.len -= 1;
|
||||
event
|
||||
}
|
||||
|
||||
/// The next event: buffered first, then whatever the device has now.
|
||||
fn next_event(&mut self) -> Option<InputEvent> {
|
||||
self.pop().or_else(|| self.device.poll())
|
||||
}
|
||||
|
||||
pub fn device(&self) -> &D {
|
||||
&self.device
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn replace_device_for_test(&mut self, device: D) {
|
||||
self.device = device;
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: InputDevice> Server for InputServer<D> {
|
||||
@@ -23,14 +73,22 @@ impl<D: InputDevice> Server for InputServer<D> {
|
||||
|
||||
fn handle(&mut self, request: InputRequest) -> InputResponse {
|
||||
match request {
|
||||
InputRequest::PollEvent => match self.device.poll() {
|
||||
Some(event) => InputResponse::Event(event),
|
||||
None => InputResponse::NoEvent,
|
||||
},
|
||||
InputRequest::WaitEvent => match self.device.poll() {
|
||||
// WaitEvent cannot block: a server thread has one reply slot,
|
||||
// so it answers like PollEvent.
|
||||
InputRequest::PollEvent | InputRequest::WaitEvent => match self.next_event() {
|
||||
Some(event) => InputResponse::Event(event),
|
||||
None => InputResponse::NoEvent,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn signal(&mut self, _bits: usize) {
|
||||
while let Some(event) = self.device.poll() {
|
||||
self.push(event);
|
||||
}
|
||||
self.device.acknowledge();
|
||||
if let Some(irq) = self.irq {
|
||||
let _ = libkos::syscall::ack_interrupt(irq);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,3 +49,39 @@ fn char_event_preserved() {
|
||||
let resp = server.handle(InputRequest::PollEvent);
|
||||
assert_eq!(resp, InputResponse::Event(InputEvent::KeyDown(Key::Char(b'z'))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_interrupt_buffers_events_until_asked() {
|
||||
let mut device = MockInputDevice::new();
|
||||
device.push_event(InputEvent::KeyDown(Key::Up));
|
||||
device.push_event(InputEvent::KeyDown(Key::Down));
|
||||
let mut server = InputServer::with_irq(device, types::bounded::CapSlot::new(1).unwrap());
|
||||
server.signal(1);
|
||||
// The device itself is now drained; the events wait in the server.
|
||||
assert!(!port_input_device::InputDevice::has_event(server.device()));
|
||||
assert_eq!(
|
||||
server.handle(InputRequest::PollEvent),
|
||||
InputResponse::Event(InputEvent::KeyDown(Key::Up))
|
||||
);
|
||||
assert_eq!(
|
||||
server.handle(InputRequest::PollEvent),
|
||||
InputResponse::Event(InputEvent::KeyDown(Key::Down))
|
||||
);
|
||||
assert_eq!(server.handle(InputRequest::PollEvent), InputResponse::NoEvent);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_full_buffer_drops_the_oldest_event() {
|
||||
let mut server = InputServer::with_irq(MockInputDevice::new(), types::bounded::CapSlot::new(1).unwrap());
|
||||
// 40 interrupts, one key each; the buffer keeps the last 32.
|
||||
for i in 0..40u8 {
|
||||
let mut device = MockInputDevice::new();
|
||||
device.push_event(InputEvent::KeyDown(Key::Char(i)));
|
||||
server.replace_device_for_test(device);
|
||||
server.signal(1);
|
||||
}
|
||||
assert_eq!(
|
||||
server.handle(InputRequest::PollEvent),
|
||||
InputResponse::Event(InputEvent::KeyDown(Key::Char(8)))
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user