Refactor virtio drivers with DMA APIs

This commit is contained in:
Jianfeng Jiang
2024-03-12 11:01:50 +00:00
committed by Tate, Hongliang Tian
parent 5e127b2da0
commit cd1575bc6d
22 changed files with 853 additions and 311 deletions

View File

@ -6,9 +6,15 @@ use alloc::{
sync::Arc,
vec::Vec,
};
use core::fmt::Debug;
use core::{fmt::Debug, mem};
use aster_frame::{io_mem::IoMem, offset_of, sync::SpinLock, trap::TrapFrame};
use aster_frame::{
io_mem::IoMem,
offset_of,
sync::SpinLock,
trap::TrapFrame,
vm::{Daddr, DmaDirection, DmaStream, HasDaddr, VmAllocOptions, VmIo, VmReader, PAGE_SIZE},
};
use aster_input::{
key::{Key, KeyStatus},
InputEvent,
@ -16,10 +22,11 @@ use aster_input::{
use aster_util::{field_ptr, safe_ptr::SafePtr};
use bitflags::bitflags;
use log::{debug, info};
use pod::Pod;
use super::{InputConfigSelect, VirtioInputConfig, VirtioInputEvent, QUEUE_EVENT, QUEUE_STATUS};
use crate::{device::VirtioDeviceError, queue::VirtQueue, transport::VirtioTransport};
use crate::{
device::VirtioDeviceError, dma_buf::DmaBuf, queue::VirtQueue, transport::VirtioTransport,
};
bitflags! {
/// The properties of input device.
@ -67,25 +74,25 @@ pub struct InputDevice {
config: SafePtr<VirtioInputConfig, IoMem>,
event_queue: SpinLock<VirtQueue>,
status_queue: VirtQueue,
event_buf: SpinLock<Box<[VirtioInputEvent; QUEUE_SIZE as usize]>>,
event_table: EventTable,
#[allow(clippy::type_complexity)]
callbacks: SpinLock<Vec<Arc<dyn Fn(InputEvent) + Send + Sync + 'static>>>,
transport: Box<dyn VirtioTransport>,
transport: SpinLock<Box<dyn VirtioTransport>>,
}
impl InputDevice {
/// Create a new VirtIO-Input driver.
/// msix_vector_left should at least have one element or n elements where n is the virtqueue amount
pub fn init(mut transport: Box<dyn VirtioTransport>) -> Result<(), VirtioDeviceError> {
let mut event_buf = Box::new([VirtioInputEvent::default(); QUEUE_SIZE as usize]);
let mut event_queue = VirtQueue::new(QUEUE_EVENT, QUEUE_SIZE, transport.as_mut())
.expect("create event virtqueue failed");
let status_queue = VirtQueue::new(QUEUE_STATUS, QUEUE_SIZE, transport.as_mut())
.expect("create status virtqueue failed");
for (i, event) in event_buf.as_mut().iter_mut().enumerate() {
// FIEME: replace slice with a more secure data structure to use dma mapping.
let token = event_queue.add_buf(&[], &[event.as_bytes_mut()]);
let event_table = EventTable::new(QUEUE_SIZE as usize);
for i in 0..event_table.num_events() {
let event_buf = event_table.get(i);
let token = event_queue.add_dma_buf(&[], &[&event_buf]);
match token {
Ok(value) => {
assert_eq!(value, i as u16);
@ -96,14 +103,14 @@ impl InputDevice {
}
}
let mut device = Self {
let device = Arc::new(Self {
config: VirtioInputConfig::new(transport.as_mut()),
event_queue: SpinLock::new(event_queue),
status_queue,
event_buf: SpinLock::new(event_buf),
transport,
event_table,
transport: SpinLock::new(transport),
callbacks: SpinLock::new(Vec::new()),
};
});
let mut raw_name: [u8; 128] = [0; 128];
device.query_config_select(InputConfigSelect::IdName, 0, &mut raw_name);
@ -115,51 +122,49 @@ impl InputDevice {
let input_prop = InputProp::from_bits(prop[0]).unwrap();
debug!("input device prop:{:?}", input_prop);
fn handle_input(_: &TrapFrame) {
debug!("Handle Virtio input interrupt");
let device = aster_input::get_device(super::DEVICE_NAME).unwrap();
device.handle_irq().unwrap();
}
let mut transport = device.transport.lock_irq_disabled();
fn config_space_change(_: &TrapFrame) {
debug!("input device config space change");
}
device
.transport
transport
.register_cfg_callback(Box::new(config_space_change))
.unwrap();
device
.transport
let handle_input = {
let device = device.clone();
move |_: &TrapFrame| device.handle_irq()
};
transport
.register_queue_callback(QUEUE_EVENT, Box::new(handle_input), false)
.unwrap();
device.transport.finish_init();
transport.finish_init();
drop(transport);
aster_input::register_device(super::DEVICE_NAME.to_string(), Arc::new(device));
aster_input::register_device(super::DEVICE_NAME.to_string(), device);
Ok(())
}
/// Pop the pending event.
pub fn pop_pending_event(&self) -> Option<VirtioInputEvent> {
let mut lock = self.event_queue.lock();
if let Ok((token, _)) = lock.pop_used() {
if token >= QUEUE_SIZE {
return None;
}
let event = &mut self.event_buf.lock()[token as usize];
// requeue
// FIEME: replace slice with a more secure data structure to use dma mapping.
if let Ok(new_token) = lock.add_buf(&[], &[event.as_bytes_mut()]) {
// This only works because nothing happen between `pop_used` and `add` that affects
// the list of free descriptors in the queue, so `add` reuses the descriptor which
// was just freed by `pop_used`.
assert_eq!(new_token, token);
return Some(*event);
fn pop_pending_events(&self, handle_event: &impl Fn(&EventBuf) -> bool) {
let mut event_queue = self.event_queue.lock_irq_disabled();
// one interrupt may contain several input events, so it should loop
while let Ok((token, _)) = event_queue.pop_used() {
debug_assert!(token < QUEUE_SIZE);
let event_buf = self.event_table.get(token as usize);
let res = handle_event(&event_buf);
let new_token = event_queue.add_dma_buf(&[], &[&event_buf]).unwrap();
// This only works because nothing happen between `pop_used` and `add` that affects
// the list of free descriptors in the queue, so `add` reuses the descriptor which
// was just freed by `pop_used`.
assert_eq!(new_token, token);
if !res {
break;
}
}
None
}
/// Query a specific piece of information by `select` and `subsel`, and write
@ -181,6 +186,42 @@ impl InputDevice {
size
}
fn handle_irq(&self) {
// Returns ture if there may be more events to handle
let handle_event = |event: &EventBuf| -> bool {
let event: VirtioInputEvent = {
let mut reader = event.reader();
reader.read_val()
};
match event.event_type {
0 => return false,
// Keyboard
1 => {}
// TODO: Support mouse device.
_ => return true,
}
let status = match event.value {
1 => KeyStatus::Pressed,
0 => KeyStatus::Released,
_ => return false,
};
let event = InputEvent::KeyBoard(Key::try_from(event.code).unwrap(), status);
info!("Input Event:{:?}", event);
let callbacks = self.callbacks.lock();
for callback in callbacks.iter() {
callback(event);
}
true
};
self.pop_pending_events(&handle_event);
}
/// Negotiate features for the device specified bits 0~23
pub(crate) fn negotiate_features(features: u64) -> u64 {
assert_eq!(features, 0);
@ -188,37 +229,85 @@ impl InputDevice {
}
}
impl aster_input::InputDevice for InputDevice {
fn handle_irq(&self) -> Option<()> {
// one interrupt may contains serval input, so it should loop
loop {
let Some(event) = self.pop_pending_event() else {
return Some(());
};
match event.event_type {
0 => return Some(()),
// Keyboard
1 => {}
// TODO: Support mouse device.
_ => continue,
}
let status = match event.value {
1 => KeyStatus::Pressed,
0 => KeyStatus::Released,
_ => return Some(()),
};
let event = InputEvent::KeyBoard(Key::try_from(event.code).unwrap(), status);
info!("Input Event:{:?}", event);
/// A event table consists of many event buffers,
/// each of which is large enough to contain a `VirtioInputEvent`.
#[derive(Debug)]
struct EventTable {
stream: DmaStream,
num_events: usize,
}
let callbacks = self.callbacks.lock();
for callback in callbacks.iter() {
callback.call((event,));
}
impl EventTable {
fn new(num_events: usize) -> Self {
assert!(num_events * mem::size_of::<VirtioInputEvent>() <= PAGE_SIZE);
let vm_segment = VmAllocOptions::new(1).alloc_contiguous().unwrap();
let default_event = VirtioInputEvent::default();
for idx in 0..num_events {
let offset = idx * EVENT_SIZE;
vm_segment.write_val(offset, &default_event).unwrap();
}
let stream = DmaStream::map(vm_segment, DmaDirection::FromDevice, false).unwrap();
Self { stream, num_events }
}
fn get(&self, idx: usize) -> EventBuf<'_> {
assert!(idx < self.num_events);
let offset = idx * EVENT_SIZE;
EventBuf {
event_table: self,
offset,
size: EVENT_SIZE,
}
}
const fn num_events(&self) -> usize {
self.num_events
}
}
const EVENT_SIZE: usize = core::mem::size_of::<VirtioInputEvent>();
/// A buffer stores exact one `VirtioInputEvent`
struct EventBuf<'a> {
event_table: &'a EventTable,
offset: usize,
size: usize,
}
impl<'a> HasDaddr for EventBuf<'a> {
fn daddr(&self) -> Daddr {
self.event_table.stream.daddr() + self.offset
}
}
impl<'a> DmaBuf for EventBuf<'a> {
fn len(&self) -> usize {
self.size
}
}
impl<'a> EventBuf<'a> {
fn reader(&self) -> VmReader<'a> {
self.event_table
.stream
.sync(self.offset..self.offset + self.size)
.unwrap();
self.event_table
.stream
.reader()
.unwrap()
.skip(self.offset)
.limit(self.size)
}
}
impl aster_input::InputDevice for InputDevice {
fn register_callbacks(&self, function: &'static (dyn Fn(InputEvent) + Send + Sync)) {
self.callbacks.lock().push(Arc::new(function))
self.callbacks.lock_irq_disabled().push(Arc::new(function))
}
}
@ -228,7 +317,7 @@ impl Debug for InputDevice {
.field("config", &self.config)
.field("event_queue", &self.event_queue)
.field("status_queue", &self.status_queue)
.field("event_buf", &self.event_buf)
.field("event_buf", &self.event_table)
.field("transport", &self.transport)
.finish()
}