From 486cffc41d750853178aa0cfd851ac95dfa40b3b Mon Sep 17 00:00:00 2001 From: Tristan Muntsinger Date: Tue, 29 Sep 2020 22:05:41 +0000 Subject: [PATCH] Add and enable virtio multi-touch touchscreen device BUG=b:124121375 TEST=compile and run Change-Id: I795ec238cb4ba7551a98fdfd4258fcae38e8a7a8 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/2439297 Tested-by: Tristan Muntsinger Commit-Queue: Tristan Muntsinger Reviewed-by: Zach Reizner Reviewed-by: Dylan Reid Auto-Submit: Tristan Muntsinger --- devices/src/virtio/input/defaults.rs | 45 ++++++++++++++++++++++++++++ devices/src/virtio/input/mod.rs | 19 ++++++++++++ linux_input_sys/src/lib.rs | 24 +++++++++++++++ src/crosvm.rs | 2 ++ src/linux.rs | 40 +++++++++++++++++++++---- src/main.rs | 22 ++++++++++++++ 6 files changed, 146 insertions(+), 6 deletions(-) diff --git a/devices/src/virtio/input/defaults.rs b/devices/src/virtio/input/defaults.rs index 615d04a7f8..b826b477b5 100644 --- a/devices/src/virtio/input/defaults.rs +++ b/devices/src/virtio/input/defaults.rs @@ -62,6 +62,19 @@ pub fn new_single_touch_config(width: u32, height: u32) -> VirtioInputConfig { ) } +/// Instantiates a VirtioInputConfig object with the default configuration for a multitouch +/// touchscreen. +pub fn new_multi_touch_config(width: u32, height: u32) -> VirtioInputConfig { + VirtioInputConfig::new( + virtio_input_device_ids::new(0, 0, 0, 0), + b"Crosvm Virtio Multitouch Touchscreen".to_vec(), + b"virtio-touchscreen".to_vec(), + virtio_input_bitmap::from_bits(&[INPUT_PROP_DIRECT]), + default_multitouchscreen_events(), + default_multitouchscreen_absinfo(width, height, 0, 0), + ) +} + fn default_touchscreen_absinfo(width: u32, height: u32) -> BTreeMap { let mut absinfo: BTreeMap = BTreeMap::new(); absinfo.insert(ABS_X, virtio_input_absinfo::new(0, width, 0, 0)); @@ -76,6 +89,38 @@ fn default_touchscreen_events() -> BTreeMap { supported_events } +fn default_multitouchscreen_absinfo( + width: u32, + height: u32, + slot: u32, + id: u32, +) -> BTreeMap { + let mut absinfo: BTreeMap = BTreeMap::new(); + absinfo.insert(ABS_MT_SLOT, virtio_input_absinfo::new(0, slot, 0, 0)); + absinfo.insert(ABS_MT_TRACKING_ID, virtio_input_absinfo::new(0, id, 0, 0)); + absinfo.insert(ABS_MT_POSITION_X, virtio_input_absinfo::new(0, width, 0, 0)); + absinfo.insert( + ABS_MT_POSITION_Y, + virtio_input_absinfo::new(0, height, 0, 0), + ); + absinfo +} + +fn default_multitouchscreen_events() -> BTreeMap { + let mut supported_events: BTreeMap = BTreeMap::new(); + supported_events.insert(EV_KEY, virtio_input_bitmap::from_bits(&[BTN_TOUCH])); + supported_events.insert( + EV_ABS, + virtio_input_bitmap::from_bits(&[ + ABS_MT_SLOT, + ABS_MT_TRACKING_ID, + ABS_MT_POSITION_X, + ABS_MT_POSITION_Y, + ]), + ); + supported_events +} + fn default_trackpad_absinfo(width: u32, height: u32) -> BTreeMap { let mut absinfo: BTreeMap = BTreeMap::new(); absinfo.insert(ABS_X, virtio_input_absinfo::new(0, width, 0, 0)); diff --git a/devices/src/virtio/input/mod.rs b/devices/src/virtio/input/mod.rs index 91400d73e0..70e635b7a3 100644 --- a/devices/src/virtio/input/mod.rs +++ b/devices/src/virtio/input/mod.rs @@ -693,6 +693,25 @@ where }) } +/// Creates a new virtio touch device which supports multi touch. +pub fn new_multi_touch( + source: T, + width: u32, + height: u32, + virtio_features: u64, +) -> Result>> +where + T: Read + Write + AsRawDescriptor, +{ + Ok(Input { + kill_evt: None, + worker_thread: None, + config: defaults::new_multi_touch_config(width, height), + source: Some(SocketEventSource::new(source)), + virtio_features, + }) +} + /// Creates a new virtio trackpad device which supports (single) touch, primary and secondary /// buttons as well as X and Y axis. pub fn new_trackpad( diff --git a/linux_input_sys/src/lib.rs b/linux_input_sys/src/lib.rs index f70dcc9f0f..1c9e801da0 100644 --- a/linux_input_sys/src/lib.rs +++ b/linux_input_sys/src/lib.rs @@ -15,6 +15,10 @@ const SYN_REPORT: u16 = 0; const REL_X: u16 = 0x00; #[allow(dead_code)] const REL_Y: u16 = 0x01; +const ABS_MT_TRACKING_ID: u16 = 0x39; +const ABS_MT_SLOT: u16 = 0x2f; +const ABS_MT_POSITION_X: u16 = 0x35; +const ABS_MT_POSITION_Y: u16 = 0x36; const ABS_X: u16 = 0x00; const ABS_Y: u16 = 0x01; const BTN_TOUCH: u16 = 0x14a; @@ -106,6 +110,26 @@ impl virtio_input_event { } } + #[inline] + pub fn multitouch_tracking_id(id: u32) -> virtio_input_event { + Self::absolute(ABS_MT_TRACKING_ID, id) + } + + #[inline] + pub fn multitouch_slot(slot: u32) -> virtio_input_event { + Self::absolute(ABS_MT_SLOT, slot) + } + + #[inline] + pub fn multitouch_absolute_x(x: u32) -> virtio_input_event { + Self::absolute(ABS_MT_POSITION_X, x) + } + + #[inline] + pub fn multitouch_absolute_y(y: u32) -> virtio_input_event { + Self::absolute(ABS_MT_POSITION_Y, y) + } + #[inline] pub fn absolute_x(x: u32) -> virtio_input_event { Self::absolute(ABS_X, x) diff --git a/src/crosvm.rs b/src/crosvm.rs index 04e267d6cc..8721ced0b2 100644 --- a/src/crosvm.rs +++ b/src/crosvm.rs @@ -213,6 +213,7 @@ pub struct Config { pub serial_parameters: BTreeMap<(SerialHardware, u8), SerialParameters>, pub syslog_tag: Option, pub virtio_single_touch: Option, + pub virtio_multi_touch: Option, pub virtio_trackpad: Option, pub virtio_mouse: Option, pub virtio_keyboard: Option, @@ -272,6 +273,7 @@ impl Default for Config { serial_parameters: BTreeMap::new(), syslog_tag: None, virtio_single_touch: None, + virtio_multi_touch: None, virtio_trackpad: None, virtio_mouse: None, virtio_keyboard: None, diff --git a/src/linux.rs b/src/linux.rs index 2274ac0553..12feeaaaa3 100644 --- a/src/linux.rs +++ b/src/linux.rs @@ -598,6 +598,30 @@ fn create_single_touch_device(cfg: &Config, single_touch_spec: &TouchDeviceOptio }) } +fn create_multi_touch_device(cfg: &Config, multi_touch_spec: &TouchDeviceOption) -> DeviceResult { + let socket = multi_touch_spec + .get_path() + .into_unix_stream() + .map_err(|e| { + error!("failed configuring virtio multi touch: {:?}", e); + e + })?; + + let (width, height) = multi_touch_spec.get_size(); + let dev = virtio::new_multi_touch( + socket, + width, + height, + virtio::base_features(cfg.protected_vm), + ) + .map_err(Error::InputDeviceNew)?; + + Ok(VirtioDeviceStub { + dev: Box::new(dev), + jail: simple_jail(&cfg, "input_device")?, + }) +} + fn create_trackpad_device(cfg: &Config, trackpad_spec: &TouchDeviceOption) -> DeviceResult { let socket = trackpad_spec.get_path().into_unix_stream().map_err(|e| { error!("failed configuring virtio trackpad: {}", e); @@ -1298,6 +1322,10 @@ fn create_virtio_devices( devs.push(create_single_touch_device(cfg, single_touch_spec)?); } + if let Some(multi_touch_spec) = &cfg.virtio_multi_touch { + devs.push(create_multi_touch_device(cfg, multi_touch_spec)?); + } + if let Some(trackpad_spec) = &cfg.virtio_trackpad { devs.push(create_trackpad_device(cfg, trackpad_spec)?); } @@ -1382,15 +1410,15 @@ fn create_virtio_devices( if cfg.display_window_mouse { let (event_device_socket, virtio_dev_socket) = UnixStream::pair().map_err(Error::CreateSocket)?; - let (single_touch_width, single_touch_height) = cfg - .virtio_single_touch + let (multi_touch_width, multi_touch_height) = cfg + .virtio_multi_touch .as_ref() - .map(|single_touch_spec| single_touch_spec.get_size()) + .map(|multi_touch_spec| multi_touch_spec.get_size()) .unwrap_or((gpu_parameters.display_width, gpu_parameters.display_height)); - let dev = virtio::new_single_touch( + let dev = virtio::new_multi_touch( virtio_dev_socket, - single_touch_width, - single_touch_height, + multi_touch_width, + multi_touch_height, virtio::base_features(cfg.protected_vm), ) .map_err(Error::InputDeviceNew)?; diff --git a/src/main.rs b/src/main.rs index 70cd4d421a..6954f6ad11 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1357,6 +1357,24 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument:: } cfg.virtio_single_touch = Some(single_touch_spec); } + "multi-touch" => { + if cfg.virtio_multi_touch.is_some() { + return Err(argument::Error::TooManyArguments( + "`multi-touch` already given".to_owned(), + )); + } + let mut it = value.unwrap().split(':'); + + let mut multi_touch_spec = + TouchDeviceOption::new(PathBuf::from(it.next().unwrap().to_owned())); + if let Some(width) = it.next() { + multi_touch_spec.set_width(width.trim().parse().unwrap()); + } + if let Some(height) = it.next() { + multi_touch_spec.set_height(height.trim().parse().unwrap()); + } + cfg.virtio_multi_touch = Some(multi_touch_spec); + } "trackpad" => { if cfg.virtio_trackpad.is_some() { return Err(argument::Error::TooManyArguments( @@ -1523,6 +1541,9 @@ fn validate_arguments(cfg: &mut Config) -> std::result::Result<(), argument::Err { if let Some(gpu_parameters) = cfg.gpu_parameters.as_ref() { let (width, height) = (gpu_parameters.display_width, gpu_parameters.display_height); + if let Some(virtio_multi_touch) = cfg.virtio_multi_touch.as_mut() { + virtio_multi_touch.set_default_size(width, height); + } if let Some(virtio_single_touch) = cfg.virtio_single_touch.as_mut() { virtio_single_touch.set_default_size(width, height); } @@ -1665,6 +1686,7 @@ writeback=BOOL - Indicates whether the VM can use writeback caching (default: fa Argument::flag("software-tpm", "enable a software emulated trusted platform module device"), Argument::value("evdev", "PATH", "Path to an event device node. The device will be grabbed (unusable from the host) and made available to the guest with the same configuration it shows on the host"), Argument::value("single-touch", "PATH:WIDTH:HEIGHT", "Path to a socket from where to read single touch input events (such as those from a touchscreen) and write status updates to, optionally followed by width and height (defaults to 800x1280)."), + Argument::value("multi-touch", "PATH:WIDTH:HEIGHT", "Path to a socket from where to read multi touch input events (such as those from a touchscreen) and write status updates to, optionally followed by width and height (defaults to 800x1280)."), Argument::value("trackpad", "PATH:WIDTH:HEIGHT", "Path to a socket from where to read trackpad input events and write status updates to, optionally followed by screen width and height (defaults to 800x1280)."), Argument::value("mouse", "PATH", "Path to a socket from where to read mouse input events and write status updates to."), Argument::value("keyboard", "PATH", "Path to a socket from where to read keyboard input events and write status updates to."),