Package: libusb-1.0-0 Version: 2:1.0.23-2.1 Severity: normal Tags: patch X-Debbugs-Cc: jonathan.str...@gmx.de
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;