virtio_drivers/device/
input.rs

1//! Driver for VirtIO input devices.
2
3use super::common::Feature;
4use crate::config::{read_config, write_config, ReadOnly, WriteOnly};
5use crate::hal::Hal;
6use crate::queue::VirtQueue;
7use crate::transport::{InterruptStatus, Transport};
8use crate::Error;
9use alloc::{boxed::Box, string::String};
10use core::cmp::min;
11use core::mem::{offset_of, size_of};
12use zerocopy::{FromBytes, FromZeros, Immutable, IntoBytes, KnownLayout};
13
14/// Virtual human interface devices such as keyboards, mice and tablets.
15///
16/// An instance of the virtio device represents one such input device.
17/// Device behavior mirrors that of the evdev layer in Linux,
18/// making pass-through implementations on top of evdev easy.
19pub struct VirtIOInput<H: Hal, T: Transport> {
20    transport: T,
21    event_queue: VirtQueue<H, QUEUE_SIZE>,
22    status_queue: VirtQueue<H, QUEUE_SIZE>,
23    event_buf: Box<[InputEvent; 32]>,
24}
25
26impl<H: Hal, T: Transport> VirtIOInput<H, T> {
27    /// Create a new VirtIO-Input driver.
28    pub fn new(mut transport: T) -> Result<Self, Error> {
29        let mut event_buf = Box::new([InputEvent::default(); QUEUE_SIZE]);
30
31        let negotiated_features = transport.begin_init(SUPPORTED_FEATURES);
32
33        let mut event_queue = VirtQueue::new(
34            &mut transport,
35            QUEUE_EVENT,
36            negotiated_features.contains(Feature::RING_INDIRECT_DESC),
37            negotiated_features.contains(Feature::RING_EVENT_IDX),
38        )?;
39        let status_queue = VirtQueue::new(
40            &mut transport,
41            QUEUE_STATUS,
42            negotiated_features.contains(Feature::RING_INDIRECT_DESC),
43            negotiated_features.contains(Feature::RING_EVENT_IDX),
44        )?;
45        for (i, event) in event_buf.as_mut().iter_mut().enumerate() {
46            // SAFETY: The buffer lasts as long as the queue.
47            let token = unsafe { event_queue.add(&[], &mut [event.as_mut_bytes()])? };
48            assert_eq!(token, i as u16);
49        }
50        if event_queue.should_notify() {
51            transport.notify(QUEUE_EVENT);
52        }
53
54        transport.finish_init();
55
56        Ok(VirtIOInput {
57            transport,
58            event_queue,
59            status_queue,
60            event_buf,
61        })
62    }
63
64    /// Acknowledge interrupt and process events.
65    pub fn ack_interrupt(&mut self) -> InterruptStatus {
66        self.transport.ack_interrupt()
67    }
68
69    /// Pop the pending event.
70    pub fn pop_pending_event(&mut self) -> Option<InputEvent> {
71        if let Some(token) = self.event_queue.peek_used() {
72            let event = &mut self.event_buf[token as usize];
73            // SAFETY: We are passing the same buffer as we passed to `VirtQueue::add` and it is still valid.
74            unsafe {
75                self.event_queue
76                    .pop_used(token, &[], &mut [event.as_mut_bytes()])
77                    .ok()?;
78            }
79            let event_saved = *event;
80            // requeue
81            // SAFETY: The buffer lasts as long as the queue.
82            if let Ok(new_token) = unsafe { self.event_queue.add(&[], &mut [event.as_mut_bytes()]) }
83            {
84                // This only works because nothing happen between `pop_used` and `add` that affects
85                // the list of free descriptors in the queue, so `add` reuses the descriptor which
86                // was just freed by `pop_used`.
87                assert_eq!(new_token, token);
88                if self.event_queue.should_notify() {
89                    self.transport.notify(QUEUE_EVENT);
90                }
91                return Some(event_saved);
92            }
93        }
94        None
95    }
96
97    /// Query a specific piece of information by `select` and `subsel`, and write
98    /// result to `out`, return the result size.
99    pub fn query_config_select(
100        &mut self,
101        select: InputConfigSelect,
102        subsel: u8,
103        out: &mut [u8],
104    ) -> Result<u8, Error> {
105        write_config!(self.transport, Config, select, select as u8)?;
106        write_config!(self.transport, Config, subsel, subsel)?;
107        let size: u8 = read_config!(self.transport, Config, size)?;
108        // Safe because config points to a valid MMIO region for the config space.
109        let size_to_copy = min(usize::from(size), out.len());
110        for (i, out_item) in out.iter_mut().take(size_to_copy).enumerate() {
111            *out_item = self
112                .transport
113                .read_config_space(offset_of!(Config, data) + i * size_of::<u8>())?;
114        }
115
116        Ok(size)
117    }
118
119    /// Queries a specific piece of information by `select` and `subsel`, allocates a sufficiently
120    /// large byte buffer for it, and returns it.
121    fn query_config_select_alloc(
122        &mut self,
123        select: InputConfigSelect,
124        subsel: u8,
125    ) -> Result<Box<[u8]>, Error> {
126        write_config!(self.transport, Config, select, select as u8)?;
127        write_config!(self.transport, Config, subsel, subsel)?;
128        let size = usize::from(read_config!(self.transport, Config, size)?);
129        if size > CONFIG_DATA_MAX_LENGTH {
130            return Err(Error::IoError);
131        }
132        let mut buf = <[u8]>::new_box_zeroed_with_elems(size).unwrap();
133        for i in 0..size {
134            buf[i] = self
135                .transport
136                .read_config_space(offset_of!(Config, data) + i * size_of::<u8>())?;
137        }
138        Ok(buf)
139    }
140
141    /// Queries a specific piece of information by `select` and `subsel` into a newly-allocated
142    /// buffer, and tries to convert it to a string.
143    ///
144    /// Returns an error if it is not valid UTF-8.
145    fn query_config_string(
146        &mut self,
147        select: InputConfigSelect,
148        subsel: u8,
149    ) -> Result<String, Error> {
150        Ok(String::from_utf8(
151            self.query_config_select_alloc(select, subsel)?.into(),
152        )?)
153    }
154
155    /// Queries and returns the name of the device, or an error if it is not valid UTF-8.
156    pub fn name(&mut self) -> Result<String, Error> {
157        self.query_config_string(InputConfigSelect::IdName, 0)
158    }
159
160    /// Queries and returns the serial number of the device, or an error if it is not valid UTF-8.
161    pub fn serial_number(&mut self) -> Result<String, Error> {
162        self.query_config_string(InputConfigSelect::IdSerial, 0)
163    }
164
165    /// Queries and returns the ID information of the device.
166    pub fn ids(&mut self) -> Result<DevIDs, Error> {
167        let mut ids = DevIDs::default();
168        let size = self.query_config_select(InputConfigSelect::IdDevids, 0, ids.as_mut_bytes())?;
169        if usize::from(size) == size_of::<DevIDs>() {
170            Ok(ids)
171        } else {
172            Err(Error::IoError)
173        }
174    }
175
176    /// Queries and returns the input properties of the device.
177    pub fn prop_bits(&mut self) -> Result<Box<[u8]>, Error> {
178        self.query_config_select_alloc(InputConfigSelect::PropBits, 0)
179    }
180
181    /// Queries and returns a bitmap of supported event codes for the given event type.
182    ///
183    /// If the event type is not supported an empty slice will be returned.
184    pub fn ev_bits(&mut self, event_type: u8) -> Result<Box<[u8]>, Error> {
185        self.query_config_select_alloc(InputConfigSelect::EvBits, event_type)
186    }
187
188    /// Queries and returns information about the given axis of the device.
189    pub fn abs_info(&mut self, axis: u8) -> Result<AbsInfo, Error> {
190        let mut info = AbsInfo::default();
191        let size =
192            self.query_config_select(InputConfigSelect::AbsInfo, axis, info.as_mut_bytes())?;
193        if usize::from(size) == size_of::<AbsInfo>() {
194            Ok(info)
195        } else {
196            Err(Error::IoError)
197        }
198    }
199}
200
201// SAFETY: The config space can be accessed from any thread.
202unsafe impl<H: Hal, T: Transport + Send> Send for VirtIOInput<H, T> where
203    VirtQueue<H, QUEUE_SIZE>: Send
204{
205}
206
207// SAFETY: An '&VirtIOInput` can't do anything, all methods take `&mut self`.
208unsafe impl<H: Hal, T: Transport + Sync> Sync for VirtIOInput<H, T> where
209    VirtQueue<H, QUEUE_SIZE>: Sync
210{
211}
212
213impl<H: Hal, T: Transport> Drop for VirtIOInput<H, T> {
214    fn drop(&mut self) {
215        // Clear any pointers pointing to DMA regions, so the device doesn't try to access them
216        // after they have been freed.
217        self.transport.queue_unset(QUEUE_EVENT);
218        self.transport.queue_unset(QUEUE_STATUS);
219    }
220}
221
222const CONFIG_DATA_MAX_LENGTH: usize = 128;
223
224/// Select value used for [`VirtIOInput::query_config_select()`].
225#[repr(u8)]
226#[derive(Debug, Clone, Copy)]
227pub enum InputConfigSelect {
228    /// Returns the name of the device, in u.string. subsel is zero.
229    IdName = 0x01,
230    /// Returns the serial number of the device, in u.string. subsel is zero.
231    IdSerial = 0x02,
232    /// Returns ID information of the device, in u.ids. subsel is zero.
233    IdDevids = 0x03,
234    /// Returns input properties of the device, in u.bitmap. subsel is zero.
235    /// Individual bits in the bitmap correspond to INPUT_PROP_* constants used
236    /// by the underlying evdev implementation.
237    PropBits = 0x10,
238    /// subsel specifies the event type using EV_* constants in the underlying
239    /// evdev implementation. If size is non-zero the event type is supported
240    /// and a bitmap of supported event codes is returned in u.bitmap. Individual
241    /// bits in the bitmap correspond to implementation-defined input event codes,
242    /// for example keys or pointing device axes.
243    EvBits = 0x11,
244    /// subsel specifies the absolute axis using ABS_* constants in the underlying
245    /// evdev implementation. Information about the axis will be returned in u.abs.
246    AbsInfo = 0x12,
247}
248
249#[derive(FromBytes, Immutable, IntoBytes)]
250#[repr(C)]
251struct Config {
252    select: WriteOnly<u8>,
253    subsel: WriteOnly<u8>,
254    size: ReadOnly<u8>,
255    _reserved: [ReadOnly<u8>; 5],
256    data: [ReadOnly<u8>; CONFIG_DATA_MAX_LENGTH],
257}
258
259/// Information about an axis of an input device, typically a joystick.
260#[repr(C)]
261#[derive(Clone, Debug, Default, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
262pub struct AbsInfo {
263    /// The minimum value for the axis.
264    pub min: u32,
265    /// The maximum value for the axis.
266    pub max: u32,
267    /// The fuzz value used to filter noise from the event stream.
268    pub fuzz: u32,
269    /// The size of the dead zone; values less than this will be reported as 0.
270    pub flat: u32,
271    /// The resolution for values reported for the axis.
272    pub res: u32,
273}
274
275/// The identifiers of a VirtIO input device.
276#[repr(C)]
277#[derive(Clone, Debug, Default, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)]
278pub struct DevIDs {
279    /// The bustype identifier.
280    pub bustype: u16,
281    /// The vendor identifier.
282    pub vendor: u16,
283    /// The product identifier.
284    pub product: u16,
285    /// The version identifier.
286    pub version: u16,
287}
288
289/// Both queues use the same `virtio_input_event` struct. `type`, `code` and `value`
290/// are filled according to the Linux input layer (evdev) interface.
291#[repr(C)]
292#[derive(Clone, Copy, Debug, Default, FromBytes, Immutable, IntoBytes, KnownLayout)]
293pub struct InputEvent {
294    /// Event type.
295    pub event_type: u16,
296    /// Event code.
297    pub code: u16,
298    /// Event value.
299    pub value: u32,
300}
301
302const QUEUE_EVENT: u16 = 0;
303const QUEUE_STATUS: u16 = 1;
304const SUPPORTED_FEATURES: Feature = Feature::RING_EVENT_IDX
305    .union(Feature::RING_INDIRECT_DESC)
306    .union(Feature::VERSION_1);
307
308// a parameter that can change
309const QUEUE_SIZE: usize = 32;
310
311#[cfg(test)]
312mod tests {
313    use super::*;
314    use crate::{
315        hal::fake::FakeHal,
316        transport::{
317            fake::{FakeTransport, QueueStatus, State},
318            DeviceType,
319        },
320    };
321    use alloc::{sync::Arc, vec};
322    use core::convert::TryInto;
323    use std::sync::Mutex;
324
325    #[test]
326    fn config() {
327        const DEFAULT_DATA: ReadOnly<u8> = ReadOnly::new(0);
328        let config_space = Config {
329            select: WriteOnly::default(),
330            subsel: WriteOnly::default(),
331            size: ReadOnly::new(0),
332            _reserved: Default::default(),
333            data: [DEFAULT_DATA; 128],
334        };
335        let state = Arc::new(Mutex::new(State::new(
336            vec![QueueStatus::default(), QueueStatus::default()],
337            config_space,
338        )));
339        let transport = FakeTransport {
340            device_type: DeviceType::Block,
341            max_queue_size: QUEUE_SIZE.try_into().unwrap(),
342            device_features: 0,
343            state: state.clone(),
344        };
345        let mut input = VirtIOInput::<FakeHal, FakeTransport<Config>>::new(transport).unwrap();
346
347        set_data(
348            &mut state.lock().unwrap().config_space,
349            "Test input device".as_bytes(),
350        );
351        assert_eq!(input.name().unwrap(), "Test input device");
352        assert_eq!(
353            state.lock().unwrap().config_space.select.0,
354            InputConfigSelect::IdName as u8
355        );
356        assert_eq!(state.lock().unwrap().config_space.subsel.0, 0);
357
358        set_data(
359            &mut state.lock().unwrap().config_space,
360            "Serial number".as_bytes(),
361        );
362        assert_eq!(input.serial_number().unwrap(), "Serial number");
363        assert_eq!(
364            state.lock().unwrap().config_space.select.0,
365            InputConfigSelect::IdSerial as u8
366        );
367        assert_eq!(state.lock().unwrap().config_space.subsel.0, 0);
368
369        let ids = DevIDs {
370            bustype: 0x4242,
371            product: 0x0067,
372            vendor: 0x1234,
373            version: 0x4321,
374        };
375        set_data(&mut state.lock().unwrap().config_space, ids.as_bytes());
376        assert_eq!(input.ids().unwrap(), ids);
377        assert_eq!(
378            state.lock().unwrap().config_space.select.0,
379            InputConfigSelect::IdDevids as u8
380        );
381        assert_eq!(state.lock().unwrap().config_space.subsel.0, 0);
382
383        set_data(&mut state.lock().unwrap().config_space, &[0x12, 0x34, 0x56]);
384        assert_eq!(input.prop_bits().unwrap().as_ref(), &[0x12, 0x34, 0x56]);
385        assert_eq!(
386            state.lock().unwrap().config_space.select.0,
387            InputConfigSelect::PropBits as u8
388        );
389        assert_eq!(state.lock().unwrap().config_space.subsel.0, 0);
390
391        set_data(&mut state.lock().unwrap().config_space, &[0x42, 0x66]);
392        assert_eq!(input.ev_bits(3).unwrap().as_ref(), &[0x42, 0x66]);
393        assert_eq!(
394            state.lock().unwrap().config_space.select.0,
395            InputConfigSelect::EvBits as u8
396        );
397        assert_eq!(state.lock().unwrap().config_space.subsel.0, 3);
398
399        let abs_info = AbsInfo {
400            min: 12,
401            max: 1234,
402            fuzz: 4,
403            flat: 10,
404            res: 2,
405        };
406        set_data(&mut state.lock().unwrap().config_space, abs_info.as_bytes());
407        assert_eq!(input.abs_info(5).unwrap(), abs_info);
408        assert_eq!(
409            state.lock().unwrap().config_space.select.0,
410            InputConfigSelect::AbsInfo as u8
411        );
412        assert_eq!(state.lock().unwrap().config_space.subsel.0, 5);
413    }
414
415    fn set_data(config_space: &mut Config, value: &[u8]) {
416        config_space.size.0 = value.len().try_into().unwrap();
417        for (i, &byte) in value.into_iter().enumerate() {
418            config_space.data[i].0 = byte;
419        }
420    }
421}