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