blob: dee2fec906ad882b6b6c84f712ed32c601f2a9c0 [file] [log] [blame] [edit]
//! Driver for VirtIO input devices.
use super::common::Feature;
use crate::hal::Hal;
use crate::queue::VirtQueue;
use crate::transport::Transport;
use crate::volatile::{volread, volwrite, ReadOnly, WriteOnly};
use crate::Result;
use alloc::boxed::Box;
use core::ptr::NonNull;
use log::info;
use zerocopy::{AsBytes, FromBytes};
/// Virtual human interface devices such as keyboards, mice and tablets.
///
/// An instance of the virtio device represents one such input device.
/// Device behavior mirrors that of the evdev layer in Linux,
/// making pass-through implementations on top of evdev easy.
pub struct VirtIOInput<H: Hal, T: Transport> {
transport: T,
event_queue: VirtQueue<H, QUEUE_SIZE>,
status_queue: VirtQueue<H, QUEUE_SIZE>,
event_buf: Box<[InputEvent; 32]>,
config: NonNull<Config>,
}
impl<H: Hal, T: Transport> VirtIOInput<H, T> {
/// Create a new VirtIO-Input driver.
pub fn new(mut transport: T) -> Result<Self> {
let mut event_buf = Box::new([InputEvent::default(); QUEUE_SIZE]);
transport.begin_init(|features| {
let features = Feature::from_bits_truncate(features);
info!("Device features: {:?}", features);
// negotiate these flags only
let supported_features = Feature::empty();
(features & supported_features).bits()
});
let config = transport.config_space::<Config>()?;
let mut event_queue = VirtQueue::new(&mut transport, QUEUE_EVENT)?;
let status_queue = VirtQueue::new(&mut transport, QUEUE_STATUS)?;
for (i, event) in event_buf.as_mut().iter_mut().enumerate() {
// Safe because the buffer lasts as long as the queue.
let token = unsafe { event_queue.add(&[], &mut [event.as_bytes_mut()])? };
assert_eq!(token, i as u16);
}
if event_queue.should_notify() {
transport.notify(QUEUE_EVENT);
}
transport.finish_init();
Ok(VirtIOInput {
transport,
event_queue,
status_queue,
event_buf,
config,
})
}
/// Acknowledge interrupt and process events.
pub fn ack_interrupt(&mut self) -> bool {
self.transport.ack_interrupt()
}
/// Pop the pending event.
pub fn pop_pending_event(&mut self) -> Option<InputEvent> {
if let Some(token) = self.event_queue.peek_used() {
let event = &mut self.event_buf[token as usize];
// Safe because we are passing the same buffer as we passed to `VirtQueue::add` and it
// is still valid.
unsafe {
self.event_queue
.pop_used(token, &[], &mut [event.as_bytes_mut()])
.ok()?;
}
let event_saved = *event;
// requeue
// Safe because buffer lasts as long as the queue.
if let Ok(new_token) = unsafe { self.event_queue.add(&[], &mut [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);
if self.event_queue.should_notify() {
self.transport.notify(QUEUE_EVENT);
}
return Some(event_saved);
}
}
None
}
/// Query a specific piece of information by `select` and `subsel`, and write
/// result to `out`, return the result size.
pub fn query_config_select(
&mut self,
select: InputConfigSelect,
subsel: u8,
out: &mut [u8],
) -> u8 {
let size;
let data;
// Safe because config points to a valid MMIO region for the config space.
unsafe {
volwrite!(self.config, select, select as u8);
volwrite!(self.config, subsel, subsel);
size = volread!(self.config, size);
data = volread!(self.config, data);
}
out[..size as usize].copy_from_slice(&data[..size as usize]);
size
}
}
impl<H: Hal, T: Transport> Drop for VirtIOInput<H, T> {
fn drop(&mut self) {
// Clear any pointers pointing to DMA regions, so the device doesn't try to access them
// after they have been freed.
self.transport.queue_unset(QUEUE_EVENT);
self.transport.queue_unset(QUEUE_STATUS);
}
}
/// Select value used for [`VirtIOInput::query_config_select()`].
#[repr(u8)]
#[derive(Debug, Clone, Copy)]
pub enum InputConfigSelect {
/// Returns the name of the device, in u.string. subsel is zero.
IdName = 0x01,
/// Returns the serial number of the device, in u.string. subsel is zero.
IdSerial = 0x02,
/// Returns ID information of the device, in u.ids. subsel is zero.
IdDevids = 0x03,
/// Returns input properties of the device, in u.bitmap. subsel is zero.
/// Individual bits in the bitmap correspond to INPUT_PROP_* constants used
/// by the underlying evdev implementation.
PropBits = 0x10,
/// subsel specifies the event type using EV_* constants in the underlying
/// evdev implementation. If size is non-zero the event type is supported
/// and a bitmap of supported event codes is returned in u.bitmap. Individual
/// bits in the bitmap correspond to implementation-defined input event codes,
/// for example keys or pointing device axes.
EvBits = 0x11,
/// subsel specifies the absolute axis using ABS_* constants in the underlying
/// evdev implementation. Information about the axis will be returned in u.abs.
AbsInfo = 0x12,
}
#[repr(C)]
struct Config {
select: WriteOnly<u8>,
subsel: WriteOnly<u8>,
size: ReadOnly<u8>,
_reversed: [ReadOnly<u8>; 5],
data: ReadOnly<[u8; 128]>,
}
#[repr(C)]
#[derive(Debug)]
struct AbsInfo {
min: u32,
max: u32,
fuzz: u32,
flat: u32,
res: u32,
}
#[repr(C)]
#[derive(Debug)]
struct DevIDs {
bustype: u16,
vendor: u16,
product: u16,
version: u16,
}
/// Both queues use the same `virtio_input_event` struct. `type`, `code` and `value`
/// are filled according to the Linux input layer (evdev) interface.
#[repr(C)]
#[derive(AsBytes, Clone, Copy, Debug, Default, FromBytes)]
pub struct InputEvent {
/// Event type.
pub event_type: u16,
/// Event code.
pub code: u16,
/// Event value.
pub value: u32,
}
const QUEUE_EVENT: u16 = 0;
const QUEUE_STATUS: u16 = 1;
// a parameter that can change
const QUEUE_SIZE: usize = 32;