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;
 

Reply via email to