• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use std::collections::BTreeMap;
2 
3 use anyhow::{bail, Context, Result};
4 use log::debug;
5 use serde::Deserialize;
6 use zerocopy::byteorder::little_endian::U32 as Le32;
7 use zerocopy::{FromBytes, FromZeros, Immutable, IntoBytes};
8 
9 #[allow(dead_code)]
10 pub const VIRTIO_INPUT_CFG_UNSET: u8 = 0x00;
11 pub const VIRTIO_INPUT_CFG_ID_NAME: u8 = 0x01;
12 pub const VIRTIO_INPUT_CFG_ID_SERIAL: u8 = 0x02;
13 pub const VIRTIO_INPUT_CFG_ID_DEVIDS: u8 = 0x03;
14 pub const VIRTIO_INPUT_CFG_PROP_BITS: u8 = 0x10;
15 pub const VIRTIO_INPUT_CFG_EV_BITS: u8 = 0x11;
16 pub const VIRTIO_INPUT_CFG_ABS_INFO: u8 = 0x12;
17 
18 pub const VIRTIO_INPUT_EVENT_SIZE: usize = 8;
19 
20 /// Calculates the largest multiple of virtio input event size less than or equal to v.
trim_to_event_size_multiple(v: usize) -> usize21 pub fn trim_to_event_size_multiple(v: usize) -> usize {
22     v & !(VIRTIO_INPUT_EVENT_SIZE - 1)
23 }
24 
25 /// In memory representation of the virtio input configuration area.
26 #[derive(Copy, Clone, FromBytes, Immutable, IntoBytes)]
27 #[repr(C)]
28 struct virtio_input_config {
29     pub select: u8,
30     pub subsel: u8,
31     pub size: u8,
32     pub reserved: [u8; 5],
33     pub payload: [u8; 128],
34 }
35 
36 #[derive(Copy, Clone, FromBytes, Immutable, IntoBytes)]
37 #[repr(C)]
38 struct virtio_input_absinfo {
39     min: Le32,
40     max: Le32,
41     fuzz: Le32,
42     flat: Le32,
43 }
44 
45 impl From<&InputConfigFileAbsInfo> for virtio_input_absinfo {
from(absinfo: &InputConfigFileAbsInfo) -> virtio_input_absinfo46     fn from(absinfo: &InputConfigFileAbsInfo) -> virtio_input_absinfo {
47         virtio_input_absinfo {
48             min: Le32::from(absinfo.min),
49             max: Le32::from(absinfo.max),
50             fuzz: Le32::from(absinfo.fuzz),
51             flat: Le32::from(absinfo.flat),
52         }
53     }
54 }
55 
56 /// Bitmap used in the virtio input configuration region.
57 #[derive(Clone)]
58 struct VirtioInputBitmap {
59     pub bitmap: [u8; 128],
60 }
61 
62 impl VirtioInputBitmap {
new() -> VirtioInputBitmap63     pub fn new() -> VirtioInputBitmap {
64         VirtioInputBitmap { bitmap: [0u8; 128] }
65     }
66 
67     /// Length of the minimum array that can hold all set bits in the map.
min_size(&self) -> u868     pub fn min_size(&self) -> u8 {
69         self.bitmap.iter().rposition(|v| *v != 0).map_or(0, |i| i + 1) as u8
70     }
71 
set(&mut self, idx: u16) -> Result<()>72     fn set(&mut self, idx: u16) -> Result<()> {
73         let byte_pos = (idx / 8) as usize;
74         let bit_byte = 1u8 << (idx % 8);
75         if byte_pos >= self.bitmap.len() {
76             // This would only happen if new event codes (or types, or ABS_*, etc) are defined
77             // to be larger than or equal to 1024, in which case a new version
78             // of the virtio input protocol needs to be defined.
79             bail!("Bitmap index '{}' is out of bitmap bounds ({})", idx, 128);
80         }
81         self.bitmap[byte_pos] |= bit_byte;
82         Ok(())
83     }
84 }
85 
86 /// Configuration of a virtio input device.
87 #[derive(Clone)]
88 pub struct VirtioInputConfig {
89     name: String,
90     serial_name: String,
91     properties: VirtioInputBitmap,
92     supported_events: BTreeMap<u16, VirtioInputBitmap>,
93     axis_info: BTreeMap<u16, virtio_input_absinfo>,
94     // [select, subsel]
95     select_bytes: [u8; 2],
96 }
97 
98 impl VirtioInputConfig {
from_json(device_config_str: &str) -> Result<VirtioInputConfig>99     pub fn from_json(device_config_str: &str) -> Result<VirtioInputConfig> {
100         let config: InputConfigFile =
101             serde_json::from_str(device_config_str).context("Failed to parse JSON string")?;
102         debug!("Parsed device config: {:?}", config);
103 
104         let mut supported_events = BTreeMap::<u16, VirtioInputBitmap>::new();
105         let mut supported_event_types = VirtioInputBitmap::new();
106         for event in config.events {
107             let mut bitmap = VirtioInputBitmap::new();
108             for &event_code in event.supported_events.values() {
109                 bitmap.set(event_code)?;
110             }
111             supported_events.insert(event.event_type_code, bitmap);
112             debug!("supporting event: {}", event.event_type_code);
113             supported_event_types.set(event.event_type_code)?;
114         }
115         // zero is a special case: return all supported event types (just like EVIOCGBIT)
116         supported_events.insert(0, supported_event_types);
117 
118         let mut properties = VirtioInputBitmap::new();
119         for &property in config.properties.values() {
120             properties.set(property)?;
121         }
122 
123         let axis_info: BTreeMap<u16, virtio_input_absinfo> = config
124             .axis_info
125             .iter()
126             .map(|absinfo| (absinfo.axis_code, virtio_input_absinfo::from(absinfo)))
127             .collect();
128 
129         Ok(VirtioInputConfig {
130             name: config.name,
131             serial_name: config.serial_name,
132             properties,
133             supported_events,
134             axis_info,
135             select_bytes: [0u8; 2],
136         })
137     }
138 
set_raw(&mut self, offset: u32, buf: &[u8]) -> Result<()>139     pub fn set_raw(&mut self, offset: u32, buf: &[u8]) -> Result<()> {
140         let mut start = offset as usize;
141         let mut end = start + buf.len();
142 
143         if end > std::mem::size_of::<virtio_input_config>() {
144             bail!("Config write out of bounds: start={}, end={}", start, end);
145         }
146 
147         // The driver doesn't (and shouldn't) write past the first two bytes, but qemu always reads
148         // and writes the entire config space regardless of what the driver asks.
149         start = std::cmp::min(start, self.select_bytes.len());
150         end = std::cmp::min(end, self.select_bytes.len());
151 
152         if start == end {
153             return Ok(());
154         }
155 
156         self.select_bytes[start..end].copy_from_slice(&buf[0..end - start]);
157 
158         Ok(())
159     }
160 
get_raw(&self) -> Result<Vec<u8>>161     pub fn get_raw(&self) -> Result<Vec<u8>> {
162         let mut config = virtio_input_config::new_zeroed();
163         config.select = self.select_bytes[0];
164         config.subsel = self.select_bytes[1];
165         match config.select {
166             VIRTIO_INPUT_CFG_ID_NAME => {
167                 config.size = self.name.len() as u8;
168                 config.payload[..self.name.len()].clone_from_slice(self.name.as_bytes());
169             }
170             VIRTIO_INPUT_CFG_ID_SERIAL => {
171                 config.size = self.serial_name.len() as u8;
172                 config.payload[..self.serial_name.len()]
173                     .clone_from_slice(self.serial_name.as_bytes());
174             }
175             VIRTIO_INPUT_CFG_ID_DEVIDS => {
176                 // {0,0,0,0}
177                 config.payload = [0u8; 128];
178             }
179             VIRTIO_INPUT_CFG_PROP_BITS => {
180                 config.size = self.properties.min_size();
181                 config.payload = self.properties.bitmap;
182             }
183             VIRTIO_INPUT_CFG_EV_BITS => {
184                 if let Some(events) = self.supported_events.get(&u16::from(config.subsel)) {
185                     config.size = events.min_size();
186                     config.payload = events.bitmap;
187                 } else {
188                     // This is not an error. Some drivers don't request the full list by
189                     // setting subsel to 0 and just ask for all types of events instead.
190                     config.size = 0;
191                 }
192             }
193             VIRTIO_INPUT_CFG_ABS_INFO => {
194                 let axis_code = config.subsel as u16;
195                 if let Some(absinfo) = self.axis_info.get(&axis_code) {
196                     let size = std::mem::size_of::<virtio_input_absinfo>();
197                     config.size = size as u8;
198                     config.payload[0..size].copy_from_slice(absinfo.as_bytes());
199                 } else {
200                     config.size = 0;
201                 }
202             }
203             _ => {
204                 bail!("Unsupported config selection: {}", config.select);
205             }
206         };
207         Ok(config.as_bytes().to_vec())
208     }
209 }
210 
211 #[derive(Debug, Deserialize)]
212 struct InputConfigFile {
213     name: String,
214     serial_name: String,
215     #[serde(default)]
216     properties: BTreeMap<String, u16>,
217     events: Vec<InputConfigFileEvent>,
218     #[serde(default)]
219     axis_info: Vec<InputConfigFileAbsInfo>,
220 }
221 
222 #[derive(Debug, Deserialize)]
223 struct InputConfigFileEvent {
224     #[allow(dead_code)]
225     event_type: String,
226     event_type_code: u16,
227     supported_events: BTreeMap<String, u16>,
228 }
229 
230 #[derive(Debug, Deserialize)]
231 struct InputConfigFileAbsInfo {
232     #[allow(dead_code)]
233     axis: String,
234     axis_code: u16,
235     min: u32,
236     max: u32,
237     #[serde(default)]
238     fuzz: u32,
239     #[serde(default)]
240     flat: u32,
241 }
242