From 3bebfa29dc4185b2f3d2752ac16b0f6639548a4a Mon Sep 17 00:00:00 2001 From: Daniel Verkamp Date: Tue, 7 May 2019 14:19:23 -0700 Subject: 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 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/1599881 Reviewed-by: Zach Reizner Tested-by: kokoro --- devices/src/usb/xhci/device_slot.rs | 12 ++++++++++++ devices/src/usb/xhci/xhci.rs | 10 +++++++--- devices/src/usb/xhci/xhci_backend_device.rs | 2 ++ 3 files changed, 21 insertions(+), 3 deletions(-) (limited to 'devices/src/usb/xhci') 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(&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 = std::result::Result; @@ -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<(), ()>; } -- cgit 1.4.1