|
|
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 |
|