| //! 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; |