mirror of
https://chromium.googlesource.com/crosvm/crosvm
synced 2025-02-05 18:20:34 +00:00
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>
This commit is contained in:
parent
a3411eaac1
commit
3bebfa29dc
6 changed files with 40 additions and 5 deletions
|
@ -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);
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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<(), ()>;
|
||||
}
|
||||
|
|
|
@ -34,7 +34,8 @@ uname: 1
|
|||
# 0xc0105500 == USBDEVFS_CONTROL
|
||||
# 0x5514 == USBDEVFS_RESET
|
||||
# 0x80045505 == USBDEVFS_SETCONFIGURATION
|
||||
ioctl: arg1 == 0xc0105500 || arg1 == 0x802c550a || arg1 == 0x8004551a || arg1 == 0x4004550d || arg1 == 0x8004550f || arg1 == 0x80045510 || arg1 == 0x550b || arg1 == 0x5514 || arg1 == 0x80045505
|
||||
# 0x8108551b == USBDEVFS_DISCONNECT_CLAIM
|
||||
ioctl: arg1 == 0xc0105500 || arg1 == 0x802c550a || arg1 == 0x8004551a || arg1 == 0x4004550d || arg1 == 0x8004550f || arg1 == 0x80045510 || arg1 == 0x550b || arg1 == 0x5514 || arg1 == 0x80045505 || arg1 == 0x8108551b
|
||||
fstat: 1
|
||||
sigaltstack: 1
|
||||
recvmsg: 1
|
||||
|
|
|
@ -32,7 +32,8 @@ uname: 1
|
|||
# 0xc0185500 == USBDEVFS_CONTROL
|
||||
# 0x5514 == USBDEVFS_RESET
|
||||
# 0x80045505 == USBDEVFS_SETCONFIGURATION
|
||||
ioctl: arg1 == 0xc0185500 || arg1 == 0x41045508 || arg1 == 0x8004550f || arg1 == 0x4008550d || arg1 == 0x8004551a || arg1 == 0x550b || arg1 == 0x80045510 || arg1 == 0x8038550a || arg1 == 0x5514 || arg1 == 0x80045505
|
||||
# 0x8108551b == USBDEVFS_DISCONNECT_CLAIM
|
||||
ioctl: arg1 == 0xc0185500 || arg1 == 0x41045508 || arg1 == 0x8004550f || arg1 == 0x4008550d || arg1 == 0x8004551a || arg1 == 0x550b || arg1 == 0x80045510 || arg1 == 0x8038550a || arg1 == 0x5514 || arg1 == 0x80045505 || arg1 == 0x8108551b
|
||||
fstat: 1
|
||||
sigaltstack: 1
|
||||
recvmsg: 1
|
||||
|
|
Loading…
Reference in a new issue