Package: libusb-1.0-0
Version: 2:1.0.23-2.1
Severity: normal
Tags: patch
X-Debbugs-Cc: [email protected]
Dear Maintainer,
after upgrading libvirt to version 6.9.0, passthrough of USB devices into QEMU
virtual machines broke.
The guest kernel was erroneously reporting the USB device as a "low-speed"
device, even though it was
full-speed, and failed initialization of the device with the following error:
"invalid ep0 maxpacket: 64"
After some digging, I found out that this was a side-effect libvirt 6.9.0
passing different (but valid)
options to QEMU for passing through host USB devices.
Old QEMU option: -device
usb-host,hostbus=3,hostaddr=4,id=hostdev0,bus=usb.0,port=4
New QEMU option: -device
usb-host,hostdevice=/dev/bus/usb/003/004,id=hostdev0,bus=usb.0,port=4
This, in turn, causes QEMU to use libusb_wrap_sys_device() to obtain a libusb
device handle to the
host USB device, if my research is correct. After that, QEMU calls
libusb_get_device_speed() to get
the speed of the USB device to pass through. This function returns an incorrect
speed value for
wrapped devices in the current version of libusb, however, causing QEMU to
report that incorrect speed
value to the guest operating system, which then results in the guest OS not
being able to initialize
the USB device.
While a workaround patch for QEMU exists, I believe it is better to fix the
underlying cause in libusb
and make libusb_get_device_speed() work for wrapped devices. There is a bug
report on RedHat's
bugzilla (bug 1871818) that contains a patch for libusb, which has been
upstreamed in libusb in commit
f6068e83c4f5e5fba16b23b6a87f1f6d7ab7200a. I have tested this patch on i386 and
amd64 and can confirm
that it works.
I've attached a version of the patch that applies to Debian's libusb-1.0
(2:1.0.23-2) using quilt.
-- System Information:
Debian Release: bullseye/sid
APT prefers testing
APT policy: (500, 'testing')
Architecture: amd64 (x86_64)
Foreign Architectures: i386
Kernel: Linux 5.9.0-3-amd64 (SMP w/48 CPU threads)
Locale: LANG=de_DE.UTF-8, LC_CTYPE=de_DE.UTF-8 (charmap=UTF-8),
LANGUAGE=de:en_US
Shell: /bin/sh linked to /usr/bin/dash
Init: systemd (via /run/systemd/system)
LSM: AppArmor: enabled
Versions of packages libusb-1.0-0 depends on:
ii libc6 2.31-4
ii libudev1 246.6-4
libusb-1.0-0 recommends no packages.
libusb-1.0-0 suggests no packages.
-- no debconf information
Index: libusb-1.0-1.0.23/libusb/os/linux_usbfs.c
===================================================================
--- libusb-1.0-1.0.23.orig/libusb/os/linux_usbfs.c
+++ libusb-1.0-1.0.23/libusb/os/linux_usbfs.c
@@ -964,6 +964,26 @@ static int usbfs_get_active_config(struc
return LIBUSB_SUCCESS;
}
+static enum libusb_speed usbfs_get_speed(struct libusb_context *ctx, int fd)
+{
+ int r;
+
+ r = ioctl(fd, IOCTL_USBFS_GET_SPEED, NULL);
+ switch (r) {
+ case USBFS_SPEED_UNKNOWN: return LIBUSB_SPEED_UNKNOWN;
+ case USBFS_SPEED_LOW: return LIBUSB_SPEED_LOW;
+ case USBFS_SPEED_FULL: return LIBUSB_SPEED_FULL;
+ case USBFS_SPEED_HIGH: return LIBUSB_SPEED_HIGH;
+ case USBFS_SPEED_WIRELESS: return LIBUSB_SPEED_HIGH;
+ case USBFS_SPEED_SUPER: return LIBUSB_SPEED_SUPER;
+ case USBFS_SPEED_SUPER_PLUS: return LIBUSB_SPEED_SUPER_PLUS;
+ default:
+ usbi_warn(ctx, "Error getting device speed: %d", r);
+ }
+
+ return LIBUSB_SPEED_UNKNOWN;
+}
+
static int initialize_device(struct libusb_device *dev, uint8_t busnum,
uint8_t devaddr, const char *sysfs_dir, int wrapped_fd)
{
@@ -995,6 +1015,8 @@ static int initialize_device(struct libu
usbi_warn(DEVICE_CTX(dev), "Unknown device
speed: %d Mbps", speed);
}
}
+ } else if (wrapped_fd >= 0) {
+ dev->speed = usbfs_get_speed(ctx, wrapped_fd);
}
/* cache descriptors in memory */
Index: libusb-1.0-1.0.23/libusb/os/linux_usbfs.h
===================================================================
--- libusb-1.0-1.0.23.orig/libusb/os/linux_usbfs.h
+++ libusb-1.0-1.0.23/libusb/os/linux_usbfs.h
@@ -143,6 +143,14 @@ struct usbfs_streams {
unsigned char eps[0];
};
+#define USBFS_SPEED_UNKNOWN 0
+#define USBFS_SPEED_LOW 1
+#define USBFS_SPEED_FULL 2
+#define USBFS_SPEED_HIGH 3
+#define USBFS_SPEED_WIRELESS 4
+#define USBFS_SPEED_SUPER 5
+#define USBFS_SPEED_SUPER_PLUS 6
+
#define IOCTL_USBFS_CONTROL _IOWR('U', 0, struct usbfs_ctrltransfer)
#define IOCTL_USBFS_BULK _IOWR('U', 2, struct usbfs_bulktransfer)
#define IOCTL_USBFS_RESETEP _IOR('U', 3, unsigned int)
@@ -168,6 +176,8 @@ struct usbfs_streams {
#define IOCTL_USBFS_DISCONNECT_CLAIM _IOR('U', 27, struct
usbfs_disconnect_claim)
#define IOCTL_USBFS_ALLOC_STREAMS _IOR('U', 28, struct usbfs_streams)
#define IOCTL_USBFS_FREE_STREAMS _IOR('U', 29, struct usbfs_streams)
+#define IOCTL_USBFS_DROP_PRIVILEGES _IOW('U', 30, __u32)
+#define IOCTL_USBFS_GET_SPEED _IO('U', 31)
extern usbi_mutex_static_t linux_hotplug_lock;