//! Driver for VirtIO input devices. 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 bitflags::bitflags; 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 { transport: T, event_queue: VirtQueue, status_queue: VirtQueue, event_buf: Box<[InputEvent; 32]>, config: NonNull, } impl VirtIOInput { /// Create a new VirtIO-Input driver. pub fn new(mut transport: T) -> Result { 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::()?; 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 { 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()?; } // 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); } } 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 Drop for VirtIOInput { 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, subsel: WriteOnly, size: ReadOnly, _reversed: [ReadOnly; 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, } bitflags! { struct Feature: u64 { // device independent const NOTIFY_ON_EMPTY = 1 << 24; // legacy const ANY_LAYOUT = 1 << 27; // legacy const RING_INDIRECT_DESC = 1 << 28; const RING_EVENT_IDX = 1 << 29; const UNUSED = 1 << 30; // legacy const VERSION_1 = 1 << 32; // detect legacy // since virtio v1.1 const ACCESS_PLATFORM = 1 << 33; const RING_PACKED = 1 << 34; const IN_ORDER = 1 << 35; const ORDER_PLATFORM = 1 << 36; const SR_IOV = 1 << 37; const NOTIFICATION_DATA = 1 << 38; } } const QUEUE_EVENT: u16 = 0; const QUEUE_STATUS: u16 = 1; // a parameter that can change const QUEUE_SIZE: usize = 32;