Blame SOURCES/0007-usbredirect-allow-multiple-devices-by-vendor-product.patch

536eb0
From 813afe206c8ff10cbbe715bf94980bf8ba6be70f Mon Sep 17 00:00:00 2001
536eb0
From: Victor Toso <victortoso@redhat.com>
536eb0
Date: Thu, 22 Dec 2022 16:13:08 +0100
536eb0
Subject: [PATCH 7/8] usbredirect: allow multiple devices by vendor:product
536eb0
536eb0
Currently, if an user tries to redirect two devices with the same
536eb0
vendor:product info, the second instance of usbredirect will not
536eb0
succeed, leading to a segmentation fault.
536eb0
536eb0
The core of the problem is that usbredirect is using
536eb0
libusb_open_device_with_vid_pid() which always returns the first
536eb0
instance of a given vendor:product, leading the second instance of
536eb0
usbredirect to give an usb device that is in use to usbredirhost.
536eb0
536eb0
This patch fixes the situation, making it possible to run usbredirect
536eb0
with --device $vendor:$product multiple times. We do a early check
536eb0
that we can claim the usb device, prior to handle it over to
536eb0
usbredirhost.
536eb0
536eb0
Related: https://gitlab.freedesktop.org/spice/usbredir/-/issues/29
536eb0
Signed-off-by: Victor Toso <victortoso@redhat.com>
536eb0
---
536eb0
 tools/usbredirect.c | 90 ++++++++++++++++++++++++++++++++++++++++++---
536eb0
 1 file changed, 85 insertions(+), 5 deletions(-)
536eb0
536eb0
diff --git a/tools/usbredirect.c b/tools/usbredirect.c
536eb0
index 95f3580..0b04418 100644
536eb0
--- a/tools/usbredirect.c
536eb0
+++ b/tools/usbredirect.c
536eb0
@@ -466,6 +466,90 @@ signal_handler(gpointer user_data)
536eb0
 }
536eb0
 #endif
536eb0
 
536eb0
+static bool
536eb0
+can_claim_usb_device(libusb_device *dev, libusb_device_handle **handle)
536eb0
+{
536eb0
+    int ret = libusb_open(dev, handle);
536eb0
+    if (ret != 0) {
536eb0
+        g_debug("Failed to open device");
536eb0
+        return false;
536eb0
+    }
536eb0
+
536eb0
+    /* Opening is not enough. We need to check if device can be claimed
536eb0
+     * for I/O operations */
536eb0
+    struct libusb_config_descriptor *config = NULL;
536eb0
+    ret = libusb_get_active_config_descriptor(dev, &config);
536eb0
+    if (ret != 0 || config == NULL) {
536eb0
+        g_debug("Failed to get active descriptor");
536eb0
+        *handle = NULL;
536eb0
+        return false;
536eb0
+    }
536eb0
+
536eb0
+#if LIBUSBX_API_VERSION >= 0x01000102
536eb0
+    libusb_set_auto_detach_kernel_driver(*handle, 1);
536eb0
+#endif
536eb0
+
536eb0
+    int i;
536eb0
+    for (i = 0; i < config->bNumInterfaces; i++) {
536eb0
+        int interface_num = config->interface[i].altsetting[0].bInterfaceNumber;
536eb0
+#if LIBUSBX_API_VERSION < 0x01000102
536eb0
+        ret = libusb_detach_kernel_driver(handle, interface_num);
536eb0
+        if (ret != 0 && ret != LIBUSB_ERROR_NOT_FOUND
536eb0
+            && ret != LIBUSB_ERROR_NOT_SUPPORTED) {
536eb0
+            g_error("failed to detach driver from interface %d: %s",
536eb0
+                    interface_num, libusb_error_name(ret));
536eb0
+            *handle = NULL;
536eb0
+            break
536eb0
+        }
536eb0
+#endif
536eb0
+        ret = libusb_claim_interface(*handle, interface_num);
536eb0
+        if (ret != 0) {
536eb0
+            g_debug("Could not claim interface");
536eb0
+            *handle = NULL;
536eb0
+            break;
536eb0
+        }
536eb0
+        ret = libusb_release_interface(*handle, interface_num);
536eb0
+        if (ret != 0) {
536eb0
+            g_debug("Could not release interface");
536eb0
+            *handle = NULL;
536eb0
+            break;
536eb0
+        }
536eb0
+    }
536eb0
+
536eb0
+    libusb_free_config_descriptor(config);
536eb0
+    return *handle != NULL;
536eb0
+}
536eb0
+
536eb0
+static libusb_device_handle *
536eb0
+open_usb_device(redirect *self)
536eb0
+{
536eb0
+    struct libusb_device **devs;
536eb0
+    struct libusb_device_handle *dev_handle = NULL;
536eb0
+    size_t i, ndevices;
536eb0
+
536eb0
+    ndevices = libusb_get_device_list(NULL, &devs);
536eb0
+    for (i = 0; i < ndevices; i++) {
536eb0
+        struct libusb_device_descriptor desc;
536eb0
+        if (libusb_get_device_descriptor(devs[i], &desc) != 0) {
536eb0
+            g_warning("Failed to get descriptor");
536eb0
+            continue;
536eb0
+        }
536eb0
+
536eb0
+        if (self->device.vendor != desc.idVendor ||
536eb0
+            self->device.product != desc.idProduct) {
536eb0
+            continue;
536eb0
+        }
536eb0
+
536eb0
+        if (can_claim_usb_device(devs[i], &dev_handle)) {
536eb0
+            break;
536eb0
+        }
536eb0
+    }
536eb0
+
536eb0
+    libusb_free_device_list(devs, 1);
536eb0
+    return dev_handle;
536eb0
+}
536eb0
+
536eb0
+
536eb0
 static gboolean
536eb0
 connection_incoming_cb(GSocketService    *service,
536eb0
                        GSocketConnection *client_connection,
536eb0
@@ -515,11 +599,7 @@ main(int argc, char *argv[])
536eb0
     g_unix_signal_add(SIGTERM, signal_handler, self);
536eb0
 #endif
536eb0
 
536eb0
-    /* This is binary is not meant to support plugins so it is safe to pass
536eb0
-     * NULL as libusb_context here and all subsequent calls */
536eb0
-    libusb_device_handle *device_handle = libusb_open_device_with_vid_pid(NULL,
536eb0
-            self->device.vendor,
536eb0
-            self->device.product);
536eb0
+    libusb_device_handle *device_handle = open_usb_device(self);
536eb0
     if (!device_handle) {
536eb0
         g_printerr("Failed to open device!\n");
536eb0
         goto err_init;
536eb0
-- 
536eb0
2.39.0
536eb0