summary refs log tree commit diff
path: root/devices/src/usb
diff options
context:
space:
mode:
authorDaniel Verkamp <dverkamp@chromium.org>2019-05-07 14:19:23 -0700
committerCommit Bot <commit-bot@chromium.org>2019-06-05 19:19:10 +0000
commit3bebfa29dc4185b2f3d2752ac16b0f6639548a4a (patch)
tree61c5a481f92b2791e68740078ddf9d79dfae096b /devices/src/usb
parenta3411eaac1ad3d51d5282d1462d6764ad539ce13 (diff)
downloadcrosvm-3bebfa29dc4185b2f3d2752ac16b0f6639548a4a.tar
crosvm-3bebfa29dc4185b2f3d2752ac16b0f6639548a4a.tar.gz
crosvm-3bebfa29dc4185b2f3d2752ac16b0f6639548a4a.tar.bz2
crosvm-3bebfa29dc4185b2f3d2752ac16b0f6639548a4a.tar.lz
crosvm-3bebfa29dc4185b2f3d2752ac16b0f6639548a4a.tar.xz
crosvm-3bebfa29dc4185b2f3d2752ac16b0f6639548a4a.tar.zst
crosvm-3bebfa29dc4185b2f3d2752ac16b0f6639548a4a.zip
usb: reset backend device on port reset
This enables the full firmware update/reset/use device in application
mode sequence for Edge TPU USB Accelerator.

There is a bit of a UI hiccup: once the firmware update and reset is
complete, the device re-enumerates with a different VID/PID, and the
"Connect to Linux" prompt shows up again.  The user must re-affirm that
the device should be connected to Linux to proceed with using the Edge
TPU.  This may be unavoidable - I'm not sure if we can tell the
difference between a newly-inserted device and a reset one.

Allowing USBDEVFS_DISCONNECT_CLAIM should be safe, since it can only
operate on file descriptors passed into the xhci device jail.

BUG=chromium:831850
TEST=Run Edge TPU Accelerator demo and verify that it can update FW

Change-Id: I3d61c7bd914830ce25448b1ae4d60e1c16f10aed
Signed-off-by: Daniel Verkamp <dverkamp@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/1599881
Reviewed-by: Zach Reizner <zachr@chromium.org>
Tested-by: kokoro <noreply+kokoro@google.com>
Diffstat (limited to 'devices/src/usb')
-rw-r--r--devices/src/usb/host_backend/host_device.rs15
-rw-r--r--devices/src/usb/xhci/device_slot.rs12
-rw-r--r--devices/src/usb/xhci/xhci.rs10
-rw-r--r--devices/src/usb/xhci/xhci_backend_device.rs2
4 files changed, 36 insertions, 3 deletions
diff --git a/devices/src/usb/host_backend/host_device.rs b/devices/src/usb/host_backend/host_device.rs
index 61a7c7a..8841f00 100644
--- a/devices/src/usb/host_backend/host_device.rs
+++ b/devices/src/usb/host_backend/host_device.rs
@@ -485,4 +485,19 @@ impl XhciBackendDevice for HostDevice {
             _address
         );
     }
+
+    fn reset(&mut self) -> std::result::Result<(), ()> {
+        usb_debug!("resetting host device");
+        let result = self.device_handle.lock().reset();
+        match result {
+            Err(LibUsbError::NotFound) => {
+                // libusb will return NotFound if it fails to re-claim
+                // the interface after the reset.
+                Ok(())
+            }
+            _ => result.map_err(|e| {
+                error!("failed to reset device: {:?}", e);
+            }),
+        }
+    }
 }
diff --git a/devices/src/usb/xhci/device_slot.rs b/devices/src/usb/xhci/device_slot.rs
index c6738f5..e2b5e5d 100644
--- a/devices/src/usb/xhci/device_slot.rs
+++ b/devices/src/usb/xhci/device_slot.rs
@@ -125,6 +125,18 @@ impl DeviceSlots {
         }
     }
 
+    /// Reset the device connected to a specific port.
+    pub fn reset_port(&self, port_id: u8) -> std::result::Result<(), ()> {
+        if let Some(port) = self.hub.get_port(port_id) {
+            if let Some(backend_device) = port.get_backend_device().as_mut() {
+                backend_device.reset()?;
+            }
+        }
+
+        // No device on port, so nothing to reset.
+        Ok(())
+    }
+
     /// Stop all device slots and reset them.
     pub fn stop_all_and_reset<C: FnMut() + 'static + Send>(&self, mut callback: C) {
         usb_debug!("stopping all device slots and resetting host hub");
diff --git a/devices/src/usb/xhci/xhci.rs b/devices/src/usb/xhci/xhci.rs
index 55dd50e..47ebe85 100644
--- a/devices/src/usb/xhci/xhci.rs
+++ b/devices/src/usb/xhci/xhci.rs
@@ -30,6 +30,7 @@ pub enum Error {
     StartProvider,
     RingDoorbell(DeviceSlotError),
     CreateCommandRingController(CommandRingControllerError),
+    ResetPort,
 }
 
 type Result<T> = std::result::Result<T, Error>;
@@ -52,6 +53,7 @@ impl Display for Error {
             CreateCommandRingController(e) => {
                 write!(f, "failed to create command ring controller: {}", e)
             }
+            ResetPort => write!(f, "failed to reset port"),
         }
     }
 }
@@ -267,17 +269,19 @@ impl Xhci {
             index,
             value
         );
+        let port_id = (index + 1) as u8;
         // xHCI spec 4.19.5. Note: we might want to change this logic if we support USB 3.0.
         if (value & PORTSC_PORT_RESET) > 0 || (value & PORTSC_WARM_PORT_RESET) > 0 {
-            // Libusb onlys support blocking call to reset and "usually incurs a noticeable
-            // delay.". We are faking a reset now.
+            self.device_slots
+                .reset_port(port_id)
+                .map_err(|_| Error::ResetPort)?;
             value &= !PORTSC_PORT_LINK_STATE_MASK;
             value &= !PORTSC_PORT_RESET;
             value |= PORTSC_PORT_ENABLED;
             value |= PORTSC_PORT_RESET_CHANGE;
             self.interrupter
                 .lock()
-                .send_port_status_change_trb((index + 1) as u8)
+                .send_port_status_change_trb(port_id)
                 .map_err(Error::SendInterrupt)?;
         }
         Ok(value)
diff --git a/devices/src/usb/xhci/xhci_backend_device.rs b/devices/src/usb/xhci/xhci_backend_device.rs
index 51fefa2..e104cdc 100644
--- a/devices/src/usb/xhci/xhci_backend_device.rs
+++ b/devices/src/usb/xhci/xhci_backend_device.rs
@@ -30,4 +30,6 @@ pub trait XhciBackendDevice: Send {
     fn submit_transfer(&mut self, transfer: XhciTransfer) -> std::result::Result<(), ()>;
     /// Set address of this backend.
     fn set_address(&mut self, address: UsbDeviceAddress);
+    /// Reset the backend device.
+    fn reset(&mut self) -> std::result::Result<(), ()>;
 }