diff --git a/devices/src/usb/host_backend/host_device.rs b/devices/src/usb/host_backend/host_device.rs index 61a7c7a5ba..8841f00697 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 c6738f543d..e2b5e5d06c 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 55dd50e784..47ebe85e12 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 51fefa2a9d..e104cdcee3 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<(), ()>; } diff --git a/seccomp/arm/xhci.policy b/seccomp/arm/xhci.policy index 14009b79b9..239eafd132 100644 --- a/seccomp/arm/xhci.policy +++ b/seccomp/arm/xhci.policy @@ -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 diff --git a/seccomp/x86_64/xhci.policy b/seccomp/x86_64/xhci.policy index 9eafb5aaf4..19a46eb562 100644 --- a/seccomp/x86_64/xhci.policy +++ b/seccomp/x86_64/xhci.policy @@ -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