• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! Driver for VirtIO input devices.
2 
3 use super::common::Feature;
4 use crate::hal::Hal;
5 use crate::queue::VirtQueue;
6 use crate::transport::Transport;
7 use crate::volatile::{volread, volwrite, ReadOnly, WriteOnly};
8 use crate::Result;
9 use alloc::boxed::Box;
10 use core::ptr::NonNull;
11 use zerocopy::{AsBytes, FromBytes, FromZeroes};
12 
13 /// Virtual human interface devices such as keyboards, mice and tablets.
14 ///
15 /// An instance of the virtio device represents one such input device.
16 /// Device behavior mirrors that of the evdev layer in Linux,
17 /// making pass-through implementations on top of evdev easy.
18 pub struct VirtIOInput<H: Hal, T: Transport> {
19     transport: T,
20     event_queue: VirtQueue<H, QUEUE_SIZE>,
21     status_queue: VirtQueue<H, QUEUE_SIZE>,
22     event_buf: Box<[InputEvent; 32]>,
23     config: NonNull<Config>,
24 }
25 
26 impl<H: Hal, T: Transport> VirtIOInput<H, T> {
27     /// Create a new VirtIO-Input driver.
new(mut transport: T) -> Result<Self>28     pub fn new(mut transport: T) -> Result<Self> {
29         let mut event_buf = Box::new([InputEvent::default(); QUEUE_SIZE]);
30 
31         let negotiated_features = transport.begin_init(SUPPORTED_FEATURES);
32 
33         let config = transport.config_space::<Config>()?;
34 
35         let mut event_queue = VirtQueue::new(
36             &mut transport,
37             QUEUE_EVENT,
38             negotiated_features.contains(Feature::RING_INDIRECT_DESC),
39             negotiated_features.contains(Feature::RING_EVENT_IDX),
40         )?;
41         let status_queue = VirtQueue::new(
42             &mut transport,
43             QUEUE_STATUS,
44             negotiated_features.contains(Feature::RING_INDIRECT_DESC),
45             negotiated_features.contains(Feature::RING_EVENT_IDX),
46         )?;
47         for (i, event) in event_buf.as_mut().iter_mut().enumerate() {
48             // Safe because the buffer lasts as long as the queue.
49             let token = unsafe { event_queue.add(&[], &mut [event.as_bytes_mut()])? };
50             assert_eq!(token, i as u16);
51         }
52         if event_queue.should_notify() {
53             transport.notify(QUEUE_EVENT);
54         }
55 
56         transport.finish_init();
57 
58         Ok(VirtIOInput {
59             transport,
60             event_queue,
61             status_queue,
62             event_buf,
63             config,
64         })
65     }
66 
67     /// Acknowledge interrupt and process events.
ack_interrupt(&mut self) -> bool68     pub fn ack_interrupt(&mut self) -> bool {
69         self.transport.ack_interrupt()
70     }
71 
72     /// Pop the pending event.
pop_pending_event(&mut self) -> Option<InputEvent>73     pub fn pop_pending_event(&mut self) -> Option<InputEvent> {
74         if let Some(token) = self.event_queue.peek_used() {
75             let event = &mut self.event_buf[token as usize];
76             // Safe because we are passing the same buffer as we passed to `VirtQueue::add` and it
77             // is still valid.
78             unsafe {
79                 self.event_queue
80                     .pop_used(token, &[], &mut [event.as_bytes_mut()])
81                     .ok()?;
82             }
83             let event_saved = *event;
84             // requeue
85             // Safe because buffer lasts as long as the queue.
86             if let Ok(new_token) = unsafe { self.event_queue.add(&[], &mut [event.as_bytes_mut()]) }
87             {
88                 // This only works because nothing happen between `pop_used` and `add` that affects
89                 // the list of free descriptors in the queue, so `add` reuses the descriptor which
90                 // was just freed by `pop_used`.
91                 assert_eq!(new_token, token);
92                 if self.event_queue.should_notify() {
93                     self.transport.notify(QUEUE_EVENT);
94                 }
95                 return Some(event_saved);
96             }
97         }
98         None
99     }
100 
101     /// Query a specific piece of information by `select` and `subsel`, and write
102     /// result to `out`, return the result size.
query_config_select( &mut self, select: InputConfigSelect, subsel: u8, out: &mut [u8], ) -> u8103     pub fn query_config_select(
104         &mut self,
105         select: InputConfigSelect,
106         subsel: u8,
107         out: &mut [u8],
108     ) -> u8 {
109         let size;
110         let data;
111         // Safe because config points to a valid MMIO region for the config space.
112         unsafe {
113             volwrite!(self.config, select, select as u8);
114             volwrite!(self.config, subsel, subsel);
115             size = volread!(self.config, size);
116             data = volread!(self.config, data);
117         }
118         out[..size as usize].copy_from_slice(&data[..size as usize]);
119         size
120     }
121 }
122 
123 // SAFETY: The config space can be accessed from any thread.
124 unsafe impl<H: Hal, T: Transport + Send> Send for VirtIOInput<H, T> where
125     VirtQueue<H, QUEUE_SIZE>: Send
126 {
127 }
128 
129 // SAFETY: An '&VirtIOInput` can't do anything, all methods take `&mut self`.
130 unsafe impl<H: Hal, T: Transport + Sync> Sync for VirtIOInput<H, T> where
131     VirtQueue<H, QUEUE_SIZE>: Sync
132 {
133 }
134 
135 impl<H: Hal, T: Transport> Drop for VirtIOInput<H, T> {
drop(&mut self)136     fn drop(&mut self) {
137         // Clear any pointers pointing to DMA regions, so the device doesn't try to access them
138         // after they have been freed.
139         self.transport.queue_unset(QUEUE_EVENT);
140         self.transport.queue_unset(QUEUE_STATUS);
141     }
142 }
143 
144 /// Select value used for [`VirtIOInput::query_config_select()`].
145 #[repr(u8)]
146 #[derive(Debug, Clone, Copy)]
147 pub enum InputConfigSelect {
148     /// Returns the name of the device, in u.string. subsel is zero.
149     IdName = 0x01,
150     /// Returns the serial number of the device, in u.string. subsel is zero.
151     IdSerial = 0x02,
152     /// Returns ID information of the device, in u.ids. subsel is zero.
153     IdDevids = 0x03,
154     /// Returns input properties of the device, in u.bitmap. subsel is zero.
155     /// Individual bits in the bitmap correspond to INPUT_PROP_* constants used
156     /// by the underlying evdev implementation.
157     PropBits = 0x10,
158     /// subsel specifies the event type using EV_* constants in the underlying
159     /// evdev implementation. If size is non-zero the event type is supported
160     /// and a bitmap of supported event codes is returned in u.bitmap. Individual
161     /// bits in the bitmap correspond to implementation-defined input event codes,
162     /// for example keys or pointing device axes.
163     EvBits = 0x11,
164     /// subsel specifies the absolute axis using ABS_* constants in the underlying
165     /// evdev implementation. Information about the axis will be returned in u.abs.
166     AbsInfo = 0x12,
167 }
168 
169 #[repr(C)]
170 struct Config {
171     select: WriteOnly<u8>,
172     subsel: WriteOnly<u8>,
173     size: ReadOnly<u8>,
174     _reversed: [ReadOnly<u8>; 5],
175     data: ReadOnly<[u8; 128]>,
176 }
177 
178 #[repr(C)]
179 #[derive(Debug)]
180 struct AbsInfo {
181     min: u32,
182     max: u32,
183     fuzz: u32,
184     flat: u32,
185     res: u32,
186 }
187 
188 #[repr(C)]
189 #[derive(Debug)]
190 struct DevIDs {
191     bustype: u16,
192     vendor: u16,
193     product: u16,
194     version: u16,
195 }
196 
197 /// Both queues use the same `virtio_input_event` struct. `type`, `code` and `value`
198 /// are filled according to the Linux input layer (evdev) interface.
199 #[repr(C)]
200 #[derive(AsBytes, Clone, Copy, Debug, Default, FromBytes, FromZeroes)]
201 pub struct InputEvent {
202     /// Event type.
203     pub event_type: u16,
204     /// Event code.
205     pub code: u16,
206     /// Event value.
207     pub value: u32,
208 }
209 
210 const QUEUE_EVENT: u16 = 0;
211 const QUEUE_STATUS: u16 = 1;
212 const SUPPORTED_FEATURES: Feature = Feature::RING_EVENT_IDX.union(Feature::RING_INDIRECT_DESC);
213 
214 // a parameter that can change
215 const QUEUE_SIZE: usize = 32;
216