summary refs log tree commit diff
diff options
context:
space:
mode:
authorZach Reizner <zachr@google.com>2019-03-18 20:58:31 -0700
committerchrome-bot <chrome-bot@chromium.org>2019-04-27 01:36:47 -0700
commitaff94ca6da90b92503558b6ba7aa68e1180afd87 (patch)
tree74fc34c90d1f6e10e6e921dddea389bdd51ad3e6
parentd99cd0ae0b21d29d4e5145b889f97e9f6cd11b69 (diff)
downloadcrosvm-aff94ca6da90b92503558b6ba7aa68e1180afd87.tar
crosvm-aff94ca6da90b92503558b6ba7aa68e1180afd87.tar.gz
crosvm-aff94ca6da90b92503558b6ba7aa68e1180afd87.tar.bz2
crosvm-aff94ca6da90b92503558b6ba7aa68e1180afd87.tar.lz
crosvm-aff94ca6da90b92503558b6ba7aa68e1180afd87.tar.xz
crosvm-aff94ca6da90b92503558b6ba7aa68e1180afd87.tar.zst
crosvm-aff94ca6da90b92503558b6ba7aa68e1180afd87.zip
usb: support for listing attached usb devices
Originally, crosvm would list details about an attached usb device for a
given port. This change allows getting details about multiple ports at
once. This is intended to simplify command line usage and downstream
consumers like concierge.

TEST=various vmc commands
     Chrome UI for handling USB devices
BUG=chromium:831850

Change-Id: I55681a7fea7425c897a22a579dcc15567683ef54
Reviewed-on: https://chromium-review.googlesource.com/1529765
Commit-Ready: Zach Reizner <zachr@chromium.org>
Tested-by: Zach Reizner <zachr@chromium.org>
Tested-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Zach Reizner <zachr@chromium.org>
-rw-r--r--devices/src/usb/host_backend/host_backend_device_provider.rs45
-rw-r--r--src/main.rs20
-rw-r--r--vm_control/src/lib.rs32
3 files changed, 65 insertions, 32 deletions
diff --git a/devices/src/usb/host_backend/host_backend_device_provider.rs b/devices/src/usb/host_backend/host_backend_device_provider.rs
index 6a68951..8d39d80 100644
--- a/devices/src/usb/host_backend/host_backend_device_provider.rs
+++ b/devices/src/usb/host_backend/host_backend_device_provider.rs
@@ -18,7 +18,10 @@ use std::os::unix::io::{AsRawFd, RawFd};
 use std::time::Duration;
 use sys_util::net::UnixSeqpacket;
 use sys_util::{error, WatchingEvents};
-use vm_control::{UsbControlCommand, UsbControlResult, UsbControlSocket};
+use vm_control::{
+    UsbControlAttachedDevice, UsbControlCommand, UsbControlResult, UsbControlSocket,
+    USB_CONTROL_MAX_PORTS,
+};
 
 const SOCKET_TIMEOUT_MS: u64 = 2000;
 
@@ -142,6 +145,27 @@ impl ProviderInner {
         }
     }
 
+    fn handle_list_devices(&self, ports: [u8; USB_CONTROL_MAX_PORTS]) -> UsbControlResult {
+        let mut devices: [UsbControlAttachedDevice; USB_CONTROL_MAX_PORTS] = Default::default();
+        for (result_index, &port_id) in ports.iter().enumerate() {
+            match self.usb_hub.get_port(port_id).and_then(|p| {
+                p.get_backend_device()
+                    .as_ref()
+                    .map(|d| (d.get_vid(), d.get_pid()))
+            }) {
+                Some((vendor_id, product_id)) => {
+                    devices[result_index] = UsbControlAttachedDevice {
+                        port: port_id,
+                        vendor_id,
+                        product_id,
+                    }
+                }
+                None => continue,
+            }
+        }
+        UsbControlResult::Devices(devices)
+    }
+
     fn on_event_helper(&self) -> Result<()> {
         let cmd = self.sock.recv().map_err(Error::ReadControlSock)?;
         match cmd {
@@ -287,23 +311,8 @@ impl ProviderInner {
                 }
                 Ok(())
             }
-            UsbControlCommand::ListDevice { port } => {
-                let port_number = port;
-                let result = match self.usb_hub.get_port(port_number) {
-                    Some(port) => match port.get_backend_device().as_ref() {
-                        Some(device) => {
-                            let vid = device.get_vid();
-                            let pid = device.get_pid();
-                            UsbControlResult::Device {
-                                port: port_number,
-                                vid,
-                                pid,
-                            }
-                        }
-                        None => UsbControlResult::NoSuchDevice,
-                    },
-                    None => UsbControlResult::NoSuchPort,
-                };
+            UsbControlCommand::ListDevice { ports } => {
+                let result = self.handle_list_devices(ports);
                 // The send failure will be logged, but event loop still think the event is
                 // handled.
                 let _ = self.sock.send(&result).map_err(Error::WriteControlSock);
diff --git a/src/main.rs b/src/main.rs
index b5ba7e7..48c1edd 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -28,7 +28,7 @@ use sys_util::{
 };
 use vm_control::{
     BalloonControlCommand, DiskControlCommand, MaybeOwnedFd, UsbControlCommand, UsbControlResult,
-    VmControlRequestSocket, VmRequest, VmResponse,
+    VmControlRequestSocket, VmRequest, VmResponse, USB_CONTROL_MAX_PORTS,
 };
 
 use crate::argument::{print_help, set_arguments, Argument};
@@ -1105,14 +1105,12 @@ fn usb_detach(mut args: std::env::Args) -> ModifyUsbResult<UsbControlResult> {
     }
 }
 
-fn usb_list(mut args: std::env::Args) -> ModifyUsbResult<UsbControlResult> {
-    let port: u8 = args
-        .next()
-        .map_or(Err(ModifyUsbError::ArgMissing("PORT")), |p| {
-            p.parse::<u8>()
-                .map_err(|e| ModifyUsbError::ArgParseInt("PORT", p.to_owned(), e))
-        })?;
-    let request = VmRequest::UsbCommand(UsbControlCommand::ListDevice { port });
+fn usb_list(args: std::env::Args) -> ModifyUsbResult<UsbControlResult> {
+    let mut ports: [u8; USB_CONTROL_MAX_PORTS] = Default::default();
+    for (index, port) in ports.iter_mut().enumerate() {
+        *port = index as u8
+    }
+    let request = VmRequest::UsbCommand(UsbControlCommand::ListDevice { ports });
     let response = handle_request(&request, args).map_err(|_| ModifyUsbError::SocketFailed)?;
     match response {
         VmResponse::UsbResponse(usb_resp) => Ok(usb_resp),
@@ -1121,9 +1119,9 @@ fn usb_list(mut args: std::env::Args) -> ModifyUsbResult<UsbControlResult> {
 }
 
 fn modify_usb(mut args: std::env::Args) -> std::result::Result<(), ()> {
-    if args.len() < 3 {
+    if args.len() < 2 {
         print_help("crosvm usb",
-                   "[attach BUS_ID:ADDR:VENDOR_ID:PRODUCT_ID [USB_DEVICE_PATH|-] | detach PORT | list PORT] VM_SOCKET...", &[]);
+                   "[attach BUS_ID:ADDR:VENDOR_ID:PRODUCT_ID [USB_DEVICE_PATH|-] | detach PORT | list] VM_SOCKET...", &[]);
         return Err(());
     }
 
diff --git a/vm_control/src/lib.rs b/vm_control/src/lib.rs
index 25902ea..0744b53 100644
--- a/vm_control/src/lib.rs
+++ b/vm_control/src/lib.rs
@@ -88,6 +88,13 @@ impl Default for VmRunMode {
     }
 }
 
+/// The maximum number of devices that can be listed in one `UsbControlCommand`.
+///
+/// This value was set to be equal to `xhci_regs::MAX_PORTS` for convenience, but it is not
+/// necessary for correctness. Importing that value directly would be overkill because it would
+/// require adding a big dependency for a single const.
+pub const USB_CONTROL_MAX_PORTS: usize = 16;
+
 #[derive(MsgOnSocket, Debug)]
 pub enum BalloonControlCommand {
     /// Set the size of the VM's balloon.
@@ -129,10 +136,23 @@ pub enum UsbControlCommand {
         port: u8,
     },
     ListDevice {
-        port: u8,
+        ports: [u8; USB_CONTROL_MAX_PORTS],
     },
 }
 
+#[derive(MsgOnSocket, Copy, Clone, Debug, Default)]
+pub struct UsbControlAttachedDevice {
+    pub port: u8,
+    pub vendor_id: u16,
+    pub product_id: u16,
+}
+
+impl UsbControlAttachedDevice {
+    fn valid(self) -> bool {
+        self.port != 0
+    }
+}
+
 #[derive(MsgOnSocket, Debug)]
 pub enum UsbControlResult {
     Ok { port: u8 },
@@ -140,7 +160,7 @@ pub enum UsbControlResult {
     NoSuchDevice,
     NoSuchPort,
     FailedToOpenDevice,
-    Device { port: u8, vid: u16, pid: u16 },
+    Devices([UsbControlAttachedDevice; USB_CONTROL_MAX_PORTS]),
 }
 
 impl Display for UsbControlResult {
@@ -153,7 +173,13 @@ impl Display for UsbControlResult {
             NoSuchDevice => write!(f, "no_such_device"),
             NoSuchPort => write!(f, "no_such_port"),
             FailedToOpenDevice => write!(f, "failed_to_open_device"),
-            Device { port, vid, pid } => write!(f, "device {} {:04x} {:04x}", port, vid, pid),
+            Devices(devices) => {
+                write!(f, "devices")?;
+                for d in devices.iter().filter(|d| d.valid()) {
+                    write!(f, " {} {:04x} {:04x}", d.port, d.vendor_id, d.product_id)?;
+                }
+                std::result::Result::Ok(())
+            }
         }
     }
 }