Re-binding XHCI driver to make USB responsive again after resume
After updating my Ubuntu 20.04 LTS installation’s Linux Kernel to 5.8 from 5.4, I started experiencing irregular problems with unresponsive USB devices after resuming from sleep.
Once in every few wakeups, the USB mouse and Bluetooth transciever would ‘freeze’ and the devices vanish from the lsusb
list.
After some playing around, I found that unbinding and rebinding the XHCI driver resolved the issue for me.
When all works fine, the output of the lsusb
command would look something like this:
→ lsusb -t
/: Bus 08.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/4p, 10000M
/: Bus 07.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/4p, 480M
/: Bus 06.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/4p, 10000M
/: Bus 05.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/2p, 480M
/: Bus 04.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/4p, 10000M
/: Bus 03.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/6p, 480M
|__ Port 1: Dev 2, If 0, Class=Wireless, Driver=btusb, 12M
|__ Port 1: Dev 2, If 1, Class=Wireless, Driver=btusb, 12M
|__ Port 5: Dev 3, If 2, Class=Human Interface Device, Driver=usbhid, 12M
|__ Port 5: Dev 3, If 0, Class=Human Interface Device, Driver=usbhid, 12M
|__ Port 5: Dev 3, If 1, Class=Human Interface Device, Driver=usbhid, 12M
|__ Port 6: Dev 4, If 0, Class=Vendor Specific Class, Driver=, 12M
|__ Port 6: Dev 4, If 2, Class=Human Interface Device, Driver=usbhid, 12M
/: Bus 02.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/4p, 10000M
/: Bus 01.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/6p, 480M
|__ Port 1: Dev 2, If 0, Class=Human Interface Device, Driver=usbhid, 1.5M
|__ Port 2: Dev 3, If 0, Class=Human Interface Device, Driver=usbhid, 1.5M
|__ Port 2: Dev 3, If 1, Class=Human Interface Device, Driver=usbhid, 1.5M
When the issue occurs, all entries under Bus 03.Port 1: Dev 1
would vanish.
Rebooting the system would make the devices available again but this is hardly a solution.
Luckily, another remedy that does not require restarting the system worked out: rebinding the driver.
Note the driver of the problematic root_hub
: xhci_hcd
, and the bus number: 03
.
First, find out the PCI device identifier of the bus.
In /sys/bus/usb/drivers/usb
you will find links usbN
where N
is the bus number.
Read the destination of the bus to find out the PCI device id. E.g., for bus 3:
→ lsusb readlink /sys/bus/usb/drivers/usb/usb3
../../../../devices/pci0000:00/0000:00:01.2/0000:02:00.0/0000:03:08.0/0000:06:00.3/usb3
Note the last identifier in the format nnnn:nn:nn.n
: 0000:06:00.3
.
Now, rebind the driver:
You will notice that the xhci_hcd
driver directory in the device tree contains a link to the previously discovered PCI device
and files bind
and unbind
.
→ ls /sys/bus/pci/drivers/xhci_hcd
0000:06:00.1
0000:06:00.3
... etc ...
bind
unbind
To unbind and bind, write the PCI device ID into the unbind
and bind
files.
You have to have permission to do that, so typically you would use sudo
:
echo 0000:06:00.3 | sudo tee /sys/bus/pci/drivers/xhci_hcd/unbind
echo 0000:06:00.3 | sudo tee /sys/bus/pci/drivers/xhci_hcd/bind
Put this is a script somewhere in your path (e.g., ~/.local/bin/rebind-usb
when ~/.local/bin
is in your PATH
).
Next time that your devices are unresponsive: run rebind-usb
and you should be good to go.