mirror of
https://chromium.googlesource.com/crosvm/crosvm
synced 2025-02-05 18:20:34 +00:00
utilize EventDevices to make virtio-input devices
BUG=chromium:1023975 TEST=./build_test Change-Id: I10267e535d4d1dae90b2b5f30db046c388791a16 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/1930409 Reviewed-by: Zach Reizner <zachr@chromium.org> Commit-Queue: Zach Reizner <zachr@chromium.org> Tested-by: Zach Reizner <zachr@chromium.org> Tested-by: kokoro <noreply+kokoro@google.com>
This commit is contained in:
parent
2767223fdb
commit
65b98f1ccc
5 changed files with 132 additions and 25 deletions
|
@ -11,6 +11,8 @@ use std::os::unix::io::AsRawFd;
|
|||
use std::rc::Rc;
|
||||
use std::usize;
|
||||
|
||||
use libc::EINVAL;
|
||||
|
||||
use data_model::*;
|
||||
|
||||
use msg_socket::{MsgReceiver, MsgSender};
|
||||
|
@ -19,7 +21,8 @@ use sys_util::{error, GuestAddress, GuestMemory};
|
|||
|
||||
use gpu_display::*;
|
||||
use gpu_renderer::{
|
||||
Box3, Context as RendererContext, Renderer, Resource as GpuRendererResource, ResourceCreateArgs,
|
||||
Box3, Context as RendererContext, Error as GpuRendererError, Renderer,
|
||||
Resource as GpuRendererResource, ResourceCreateArgs,
|
||||
};
|
||||
|
||||
use super::protocol::{
|
||||
|
@ -73,6 +76,7 @@ impl VirtioGpuResource {
|
|||
|
||||
let (query, dmabuf) = match self.gpu_resource.export() {
|
||||
Ok(export) => (export.0, export.1),
|
||||
Err(GpuRendererError::Virglrenderer(e)) if e == -EINVAL => return None,
|
||||
Err(e) => {
|
||||
error!("failed to query resource: {}", e);
|
||||
return None;
|
||||
|
@ -160,6 +164,8 @@ pub struct Backend {
|
|||
renderer: Renderer,
|
||||
resources: Map<u32, VirtioGpuResource>,
|
||||
contexts: Map<u32, RendererContext>,
|
||||
// Maps event devices to scanout number.
|
||||
event_devices: Map<u32, u32>,
|
||||
gpu_device_socket: VmMemoryControlRequestSocket,
|
||||
scanout_surface: Option<u32>,
|
||||
cursor_surface: Option<u32>,
|
||||
|
@ -187,9 +193,10 @@ impl Backend {
|
|||
display_width,
|
||||
display_height,
|
||||
renderer,
|
||||
gpu_device_socket,
|
||||
resources: Default::default(),
|
||||
contexts: Default::default(),
|
||||
event_devices: Default::default(),
|
||||
gpu_device_socket,
|
||||
scanout_surface: None,
|
||||
cursor_surface: None,
|
||||
scanout_resource: 0,
|
||||
|
@ -203,6 +210,25 @@ impl Backend {
|
|||
&self.display
|
||||
}
|
||||
|
||||
pub fn import_event_device(&mut self, event_device: EventDevice, scanout: u32) {
|
||||
// TODO(zachr): support more than one scanout.
|
||||
if scanout != 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut display = self.display.borrow_mut();
|
||||
let event_device_id = match display.import_event_device(event_device) {
|
||||
Ok(id) => id,
|
||||
Err(e) => {
|
||||
error!("error importing event device: {}", e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
self.scanout_surface
|
||||
.map(|s| display.attach_event_device(s, event_device_id));
|
||||
self.event_devices.insert(event_device_id, scanout);
|
||||
}
|
||||
|
||||
/// Processes the internal `display` events and returns `true` if the main display was closed.
|
||||
pub fn process_display(&mut self) -> bool {
|
||||
let mut display = self.display.borrow_mut();
|
||||
|
@ -281,9 +307,9 @@ impl Backend {
|
|||
}
|
||||
|
||||
/// Sets the given resource id as the source of scanout to the display.
|
||||
pub fn set_scanout(&mut self, id: u32) -> GpuResponse {
|
||||
pub fn set_scanout(&mut self, _scanout_id: u32, resource_id: u32) -> GpuResponse {
|
||||
let mut display = self.display.borrow_mut();
|
||||
if id == 0 {
|
||||
if resource_id == 0 {
|
||||
if let Some(surface) = self.scanout_surface.take() {
|
||||
display.release_surface(surface);
|
||||
}
|
||||
|
@ -293,12 +319,19 @@ impl Backend {
|
|||
}
|
||||
self.cursor_resource = 0;
|
||||
GpuResponse::OkNoData
|
||||
} else if self.resources.get_mut(&id).is_some() {
|
||||
self.scanout_resource = id;
|
||||
} else if self.resources.get_mut(&resource_id).is_some() {
|
||||
self.scanout_resource = resource_id;
|
||||
|
||||
if self.scanout_surface.is_none() {
|
||||
match display.create_surface(None, self.display_width, self.display_height) {
|
||||
Ok(surface) => self.scanout_surface = Some(surface),
|
||||
Ok(surface) => {
|
||||
// TODO(zachr): do not assume every event device belongs to this scanout_id;
|
||||
// in other words, support multiple display outputs.
|
||||
for &event_device in self.event_devices.keys() {
|
||||
display.attach_event_device(surface, event_device);
|
||||
}
|
||||
self.scanout_surface = Some(surface);
|
||||
}
|
||||
Err(e) => error!("failed to create display surface: {}", e),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ use sys_util::{
|
|||
debug, error, warn, Error, EventFd, GuestAddress, GuestMemory, PollContext, PollToken,
|
||||
};
|
||||
|
||||
pub use gpu_display::EventDevice;
|
||||
use gpu_display::*;
|
||||
use gpu_renderer::{Renderer, RendererFlags};
|
||||
|
||||
|
@ -119,7 +120,9 @@ impl Frontend {
|
|||
GpuCommand::ResourceUnref(info) => {
|
||||
self.backend.unref_resource(info.resource_id.to_native())
|
||||
}
|
||||
GpuCommand::SetScanout(info) => self.backend.set_scanout(info.resource_id.to_native()),
|
||||
GpuCommand::SetScanout(info) => self
|
||||
.backend
|
||||
.set_scanout(info.scanout_id.to_native(), info.resource_id.to_native()),
|
||||
GpuCommand::ResourceFlush(info) => self.backend.flush_resource(
|
||||
info.resource_id.to_native(),
|
||||
info.r.x.to_native(),
|
||||
|
@ -672,6 +675,7 @@ fn build_backend(
|
|||
possible_displays: &[DisplayBackend],
|
||||
display_width: u32,
|
||||
display_height: u32,
|
||||
event_devices: Vec<EventDevice>,
|
||||
gpu_device_socket: VmMemoryControlRequestSocket,
|
||||
pci_bar: Alloc,
|
||||
) -> Option<Backend> {
|
||||
|
@ -717,22 +721,29 @@ fn build_backend(
|
|||
}
|
||||
};
|
||||
|
||||
Some(Backend::new(
|
||||
let mut backend = Backend::new(
|
||||
display,
|
||||
display_width,
|
||||
display_height,
|
||||
renderer,
|
||||
gpu_device_socket,
|
||||
pci_bar,
|
||||
))
|
||||
);
|
||||
|
||||
for event_device in event_devices {
|
||||
backend.import_event_device(event_device, 0);
|
||||
}
|
||||
|
||||
Some(backend)
|
||||
}
|
||||
|
||||
pub struct Gpu {
|
||||
config_event: bool,
|
||||
exit_evt: EventFd,
|
||||
gpu_device_socket: Option<VmMemoryControlRequestSocket>,
|
||||
resource_bridges: Vec<ResourceResponseSocket>,
|
||||
event_devices: Vec<EventDevice>,
|
||||
kill_evt: Option<EventFd>,
|
||||
config_event: bool,
|
||||
worker_thread: Option<thread::JoinHandle<()>>,
|
||||
num_scanouts: NonZeroU8,
|
||||
display_backends: Vec<DisplayBackend>,
|
||||
|
@ -749,15 +760,17 @@ impl Gpu {
|
|||
resource_bridges: Vec<ResourceResponseSocket>,
|
||||
display_backends: Vec<DisplayBackend>,
|
||||
gpu_parameters: &GpuParameters,
|
||||
event_devices: Vec<EventDevice>,
|
||||
) -> Gpu {
|
||||
Gpu {
|
||||
config_event: false,
|
||||
exit_evt,
|
||||
gpu_device_socket,
|
||||
num_scanouts,
|
||||
resource_bridges,
|
||||
event_devices,
|
||||
config_event: false,
|
||||
kill_evt: None,
|
||||
worker_thread: None,
|
||||
num_scanouts,
|
||||
display_backends,
|
||||
display_width: gpu_parameters.display_width,
|
||||
display_height: gpu_parameters.display_height,
|
||||
|
@ -881,6 +894,7 @@ impl VirtioDevice for Gpu {
|
|||
let display_backends = self.display_backends.clone();
|
||||
let display_width = self.display_width;
|
||||
let display_height = self.display_height;
|
||||
let event_devices = self.event_devices.split_off(0);
|
||||
if let (Some(gpu_device_socket), Some(pci_bar)) =
|
||||
(self.gpu_device_socket.take(), self.pci_bar.take())
|
||||
{
|
||||
|
@ -892,6 +906,7 @@ impl VirtioDevice for Gpu {
|
|||
&display_backends,
|
||||
display_width,
|
||||
display_height,
|
||||
event_devices,
|
||||
gpu_device_socket,
|
||||
pci_bar,
|
||||
) {
|
||||
|
|
|
@ -155,6 +155,8 @@ pub struct Config {
|
|||
pub cras_audio: bool,
|
||||
pub cras_capture: bool,
|
||||
pub null_audio: bool,
|
||||
pub display_window_keyboard: bool,
|
||||
pub display_window_mouse: bool,
|
||||
pub serial_parameters: BTreeMap<u8, SerialParameters>,
|
||||
pub syslog_tag: Option<String>,
|
||||
pub virtio_single_touch: Option<TouchDeviceOption>,
|
||||
|
@ -194,6 +196,8 @@ impl Default for Config {
|
|||
wayland_socket_path: None,
|
||||
wayland_dmabuf: false,
|
||||
x_display: None,
|
||||
display_window_keyboard: false,
|
||||
display_window_mouse: false,
|
||||
shared_dirs: Vec::new(),
|
||||
sandbox: !cfg!(feature = "default-no-sandbox"),
|
||||
seccomp_policy_dir: PathBuf::from(SECCOMP_POLICY_DIR),
|
||||
|
|
71
src/linux.rs
71
src/linux.rs
|
@ -28,6 +28,8 @@ use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
|||
use libc::{self, c_int, gid_t, uid_t};
|
||||
|
||||
use audio_streams::DummyStreamSource;
|
||||
#[cfg(feature = "gpu")]
|
||||
use devices::virtio::EventDevice;
|
||||
use devices::virtio::{self, VirtioDevice};
|
||||
use devices::{
|
||||
self, HostBackendDeviceProvider, PciDevice, VfioDevice, VfioPciDevice, VirtioPciDevice,
|
||||
|
@ -456,7 +458,7 @@ fn create_tpm_device(cfg: &Config) -> DeviceResult {
|
|||
}
|
||||
|
||||
fn create_single_touch_device(cfg: &Config, single_touch_spec: &TouchDeviceOption) -> DeviceResult {
|
||||
let socket = create_input_socket(&single_touch_spec.path).map_err(|e| {
|
||||
let socket = single_touch_spec.path.into_unix_stream().map_err(|e| {
|
||||
error!("failed configuring virtio single touch: {:?}", e);
|
||||
e
|
||||
})?;
|
||||
|
@ -470,7 +472,7 @@ fn create_single_touch_device(cfg: &Config, single_touch_spec: &TouchDeviceOptio
|
|||
}
|
||||
|
||||
fn create_trackpad_device(cfg: &Config, trackpad_spec: &TouchDeviceOption) -> DeviceResult {
|
||||
let socket = create_input_socket(&trackpad_spec.path).map_err(|e| {
|
||||
let socket = trackpad_spec.path.into_unix_stream().map_err(|e| {
|
||||
error!("failed configuring virtio trackpad: {}", e);
|
||||
e
|
||||
})?;
|
||||
|
@ -484,8 +486,8 @@ fn create_trackpad_device(cfg: &Config, trackpad_spec: &TouchDeviceOption) -> De
|
|||
})
|
||||
}
|
||||
|
||||
fn create_mouse_device(cfg: &Config, mouse_socket: &Path) -> DeviceResult {
|
||||
let socket = create_input_socket(&mouse_socket).map_err(|e| {
|
||||
fn create_mouse_device<T: IntoUnixStream>(cfg: &Config, mouse_socket: T) -> DeviceResult {
|
||||
let socket = mouse_socket.into_unix_stream().map_err(|e| {
|
||||
error!("failed configuring virtio mouse: {}", e);
|
||||
e
|
||||
})?;
|
||||
|
@ -498,8 +500,8 @@ fn create_mouse_device(cfg: &Config, mouse_socket: &Path) -> DeviceResult {
|
|||
})
|
||||
}
|
||||
|
||||
fn create_keyboard_device(cfg: &Config, keyboard_socket: &Path) -> DeviceResult {
|
||||
let socket = create_input_socket(&keyboard_socket).map_err(|e| {
|
||||
fn create_keyboard_device<T: IntoUnixStream>(cfg: &Config, keyboard_socket: T) -> DeviceResult {
|
||||
let socket = keyboard_socket.into_unix_stream().map_err(|e| {
|
||||
error!("failed configuring virtio keyboard: {}", e);
|
||||
e
|
||||
})?;
|
||||
|
@ -589,6 +591,7 @@ fn create_gpu_device(
|
|||
gpu_sockets: Vec<virtio::resource_bridge::ResourceResponseSocket>,
|
||||
wayland_socket_path: Option<PathBuf>,
|
||||
x_display: Option<String>,
|
||||
event_devices: Vec<EventDevice>,
|
||||
) -> DeviceResult {
|
||||
let jailed_wayland_path = Path::new("/wayland-0");
|
||||
|
||||
|
@ -615,6 +618,7 @@ fn create_gpu_device(
|
|||
gpu_sockets,
|
||||
display_backends,
|
||||
cfg.gpu_parameters.as_ref().unwrap(),
|
||||
event_devices,
|
||||
);
|
||||
|
||||
let jail = match simple_jail(&cfg, "gpu_device.policy")? {
|
||||
|
@ -986,6 +990,31 @@ fn create_virtio_devices(
|
|||
#[cfg(feature = "gpu")]
|
||||
{
|
||||
if cfg.gpu_parameters.is_some() {
|
||||
let mut event_devices = Vec::new();
|
||||
if cfg.display_window_mouse {
|
||||
let (event_device_socket, virtio_dev_socket) =
|
||||
UnixStream::pair().map_err(Error::CreateSocket)?;
|
||||
// TODO(nkgold): the width/height here should match the display's height/width. When
|
||||
// those settings are available as CLI options, we should use the CLI options here
|
||||
// as well.
|
||||
let dev = virtio::new_single_touch(virtio_dev_socket, 1280, 1024)
|
||||
.map_err(Error::InputDeviceNew)?;
|
||||
devs.push(VirtioDeviceStub {
|
||||
dev: Box::new(dev),
|
||||
jail: simple_jail(&cfg, "input_device.policy")?,
|
||||
});
|
||||
event_devices.push(EventDevice::touchscreen(event_device_socket));
|
||||
}
|
||||
if cfg.display_window_keyboard {
|
||||
let (event_device_socket, virtio_dev_socket) =
|
||||
UnixStream::pair().map_err(Error::CreateSocket)?;
|
||||
let dev = virtio::new_keyboard(virtio_dev_socket).map_err(Error::InputDeviceNew)?;
|
||||
devs.push(VirtioDeviceStub {
|
||||
dev: Box::new(dev),
|
||||
jail: simple_jail(&cfg, "input_device.policy")?,
|
||||
});
|
||||
event_devices.push(EventDevice::keyboard(event_device_socket));
|
||||
}
|
||||
devs.push(create_gpu_device(
|
||||
cfg,
|
||||
_exit_evt,
|
||||
|
@ -993,6 +1022,7 @@ fn create_virtio_devices(
|
|||
resource_bridges,
|
||||
cfg.wayland_socket_path.clone(),
|
||||
cfg.x_display.clone(),
|
||||
event_devices,
|
||||
)?);
|
||||
}
|
||||
}
|
||||
|
@ -1160,12 +1190,29 @@ fn raw_fd_from_path(path: &Path) -> Result<RawFd> {
|
|||
validate_raw_fd(raw_fd).map_err(Error::ValidateRawFd)
|
||||
}
|
||||
|
||||
fn create_input_socket(path: &Path) -> Result<UnixStream> {
|
||||
if path.parent() == Some(Path::new("/proc/self/fd")) {
|
||||
// Safe because we will validate |raw_fd|.
|
||||
unsafe { Ok(UnixStream::from_raw_fd(raw_fd_from_path(path)?)) }
|
||||
} else {
|
||||
UnixStream::connect(path).map_err(Error::InputEventsOpen)
|
||||
trait IntoUnixStream {
|
||||
fn into_unix_stream(self) -> Result<UnixStream>;
|
||||
}
|
||||
|
||||
impl<'a> IntoUnixStream for &'a Path {
|
||||
fn into_unix_stream(self) -> Result<UnixStream> {
|
||||
if self.parent() == Some(Path::new("/proc/self/fd")) {
|
||||
// Safe because we will validate |raw_fd|.
|
||||
unsafe { Ok(UnixStream::from_raw_fd(raw_fd_from_path(self)?)) }
|
||||
} else {
|
||||
UnixStream::connect(self).map_err(Error::InputEventsOpen)
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<'a> IntoUnixStream for &'a PathBuf {
|
||||
fn into_unix_stream(self) -> Result<UnixStream> {
|
||||
self.as_path().into_unix_stream()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoUnixStream for UnixStream {
|
||||
fn into_unix_stream(self) -> Result<UnixStream> {
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -511,6 +511,12 @@ fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::
|
|||
}
|
||||
cfg.x_display = Some(value.unwrap().to_owned());
|
||||
}
|
||||
"display-window-keyboard" => {
|
||||
cfg.display_window_keyboard = true;
|
||||
}
|
||||
"display-window-mouse" => {
|
||||
cfg.display_window_mouse = true;
|
||||
}
|
||||
"socket" => {
|
||||
if cfg.socket_path.is_some() {
|
||||
return Err(argument::Error::TooManyArguments(
|
||||
|
@ -931,6 +937,8 @@ fn run_vm(args: std::env::Args) -> std::result::Result<(), ()> {
|
|||
"),
|
||||
Argument::value("syslog-tag", "TAG", "When logging to syslog, use the provided tag."),
|
||||
Argument::value("x-display", "DISPLAY", "X11 display name to use."),
|
||||
Argument::flag("display-window-keyboard", "Capture keyboard input from the display window."),
|
||||
Argument::flag("display-window-mouse", "Capture keyboard input from the display window."),
|
||||
Argument::value("wayland-sock", "PATH", "Path to the Wayland socket to use."),
|
||||
#[cfg(feature = "wl-dmabuf")]
|
||||
Argument::flag("wayland-dmabuf", "Enable support for DMABufs in Wayland device."),
|
||||
|
|
Loading…
Reference in a new issue