[Spice-commits] 2 commits - configure.ac gtk/usb-device-manager.c

Hans de Goede jwrdegoede at kemper.freedesktop.org
Fri Jul 5 06:00:56 PDT 2013


 configure.ac             |   41 ++++---
 gtk/usb-device-manager.c |  268 ++++++++++++++++++++++++++++++++++-------------
 2 files changed, 221 insertions(+), 88 deletions(-)

New commits:
commit 53874127d2815250acff8b43916d36a3b903102e
Author: Hans de Goede <hdegoede at redhat.com>
Date:   Thu Jul 4 17:10:27 2013 +0200

    usb-device-manager: Add support for libusb hotplug API
    
    For now this makes the usb-code even more of a #ifdef party (albeit only
    slightly so). But it does give us (theoretical, untested) usb support on
    Mac OS X, and once the libusb Windows backend also gets hotplug support, we
    can bump the required libusb version to a new enough one, and drop both the
    windows gudev emulation as well as the gudev code-paths.
    
    Signed-off-by: Hans de Goede <hdegoede at redhat.com>

diff --git a/configure.ac b/configure.ac
index bd59ccf..11f11fc 100644
--- a/configure.ac
+++ b/configure.ac
@@ -367,21 +367,34 @@ else
   if test "x$have_usbredir" = "xno" && test "x$enable_usbredir" = "xyes"; then
     AC_MSG_ERROR([usbredir support explicitly requested, but some required packages are not available])
   fi
-  if test "x$have_usbredir" = "xyes"; then
-    AC_DEFINE(USE_USBREDIR, [1], [Define if supporting usbredir proxying])
-  fi
 
-  # Require gudev for non-windows
-  if test "x$os_win32" = "xno"; then
-    PKG_CHECK_MODULES([GUDEV],
-                      [gudev-1.0],
-                      [have_gudev=yes],
-                      [have_gudev=no])
-
-    if test "x$have_usbredir" = "xyes" && test "x$have_gudev" = "xno"; then
-      AC_MSG_ERROR([usbredir requested but required gudev is not available])
+  # On non windows we need either libusb hotplug support or gudev
+  if test "x$have_usbredir" = "xyes" && test "x$os_win32" = "xno"; then
+    PKG_CHECK_MODULES([LIBUSB_HOTPLUG], [libusb-1.0 >= 1.0.16],
+                      [have_libusb_hotplug=yes], [have_libusb_hotplug=no])
+    if test "x$have_libusb_hotplug" = "xyes"; then
+      AC_DEFINE(USE_LIBUSB_HOTPLUG, [1], [Define if libusb has hotplug support])
+      with_usbredir_hotplug="with libusb hotplug"
+    else
+      PKG_CHECK_MODULES([GUDEV],
+                        [gudev-1.0],
+                        [have_gudev=yes],
+                        [have_gudev=no])
+
+      if test "x$have_gudev" = "xno" && test "x$enable_usbredir" = "xyes"; then
+        AC_MSG_ERROR([usbredir requested but required gudev is not available])
+      fi
+      if test "x$have_gudev" = "xyes"; then
+        AC_DEFINE(USE_GUDEV, [1], [Define if supporting gudev])
+        with_usbredir_hotplug="with gudev hotplug"
+      else
+        have_usbredir=no
+      fi
     fi
-    AC_DEFINE(USE_GUDEV, [1], [Define if supporting gudev])
+  fi
+
+  if test "x$have_usbredir" = "xyes"; then
+    AC_DEFINE(USE_USBREDIR, [1], [Define if supporting usbredir proxying])
   fi
 fi
 AM_CONDITIONAL([WITH_USBREDIR], [test "x$have_usbredir" = "xyes"])
@@ -686,7 +699,7 @@ AC_MSG_NOTICE([
         Target:                   ${red_target}
         SASL support:             ${enable_sasl}
         Smartcard support:        ${have_smartcard}
-        USB redirection support:  ${have_usbredir}
+        USB redirection support:  ${have_usbredir} ${with_usbredir_hotplug}
         Gtk:                      $GTK_API_VERSION
 
         Now type 'make' to build $PACKAGE
diff --git a/gtk/usb-device-manager.c b/gtk/usb-device-manager.c
index 8b8bb8b..bdcfd13 100644
--- a/gtk/usb-device-manager.c
+++ b/gtk/usb-device-manager.c
@@ -34,8 +34,9 @@
 #elif defined(G_OS_WIN32)
 #include "win-usb-dev.h"
 #include "win-usb-driver-install.h"
-#else
-#warning "Expecting one of G_OS_WIN32 and USE_GUDEV to be defined"
+#define USE_GUDEV /* win-usb-dev.h provides a fake gudev interface */
+#elif !defined USE_LIBUSB_HOTPLUG
+#error "Expecting one of USE_GUDEV or USE_LIBUSB_HOTPLUG to be defined"
 #endif
 
 #include "channel-usbredir-priv.h"
@@ -106,15 +107,19 @@ struct _SpiceUsbDeviceManagerPrivate {
     gchar *redirect_on_connect;
 #ifdef USE_USBREDIR
     libusb_context *context;
-    GUdevClient *udev;
     int event_listeners;
     GThread *event_thread;
     gboolean event_thread_run;
-    libusb_device **coldplug_list; /* Avoid needless reprobing during init */
     struct usbredirfilter_rule *auto_conn_filter_rules;
     struct usbredirfilter_rule *redirect_on_connect_rules;
     int auto_conn_filter_rules_count;
     int redirect_on_connect_rules_count;
+#ifdef USE_GUDEV
+    GUdevClient *udev;
+    libusb_device **coldplug_list; /* Avoid needless reprobing during init */
+#else
+    libusb_hotplug_callback_handle hp_handle;
+#endif
 #ifdef G_OS_WIN32
     SpiceWinUsbDriver     *installer;
 #endif
@@ -153,12 +158,19 @@ static void channel_new(SpiceSession *session, SpiceChannel *channel,
                         gpointer user_data);
 static void channel_destroy(SpiceSession *session, SpiceChannel *channel,
                             gpointer user_data);
+#ifdef USE_GUDEV
 static void spice_usb_device_manager_uevent_cb(GUdevClient     *client,
                                                const gchar     *action,
                                                GUdevDevice     *udevice,
                                                gpointer         user_data);
 static void spice_usb_device_manager_add_udev(SpiceUsbDeviceManager  *self,
                                               GUdevDevice            *udev);
+#else
+static int spice_usb_device_manager_hotplug_cb(libusb_context       *ctx,
+                                               libusb_device        *device,
+                                               libusb_hotplug_event  event,
+                                               void                 *data);
+#endif
 static void spice_usb_device_manager_check_redir_on_connect(
     SpiceUsbDeviceManager *self, SpiceChannel *channel);
 
@@ -231,8 +243,10 @@ static gboolean spice_usb_device_manager_initable_init(GInitable  *initable,
     GList *list;
     GList *it;
     int rc;
+#ifdef USE_GUDEV
     const gchar *const subsystems[] = {"usb", NULL};
 #endif
+#endif
 
     g_return_val_if_fail(SPICE_IS_USB_DEVICE_MANAGER(initable), FALSE);
     g_return_val_if_fail(err == NULL || *err == NULL, FALSE);
@@ -264,10 +278,10 @@ static gboolean spice_usb_device_manager_initable_init(GInitable  *initable,
     }
 
     /* Start listening for usb devices plug / unplug */
+#ifdef USE_GUDEV
     priv->udev = g_udev_client_new(subsystems);
     g_signal_connect(G_OBJECT(priv->udev), "uevent",
                      G_CALLBACK(spice_usb_device_manager_uevent_cb), self);
-
     /* Do coldplug (detection of already connected devices) */
     libusb_get_device_list(priv->context, &priv->coldplug_list);
     list = g_udev_client_query_by_subsystem(priv->udev, "usb");
@@ -278,6 +292,21 @@ static gboolean spice_usb_device_manager_initable_init(GInitable  *initable,
     g_list_free(list);
     libusb_free_device_list(priv->coldplug_list, 1);
     priv->coldplug_list = NULL;
+#else
+    rc = libusb_hotplug_register_callback(priv->context,
+        LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT,
+        LIBUSB_HOTPLUG_ENUMERATE, LIBUSB_HOTPLUG_MATCH_ANY,
+        LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY,
+        spice_usb_device_manager_hotplug_cb, self, &priv->hp_handle);
+    if (rc < 0) {
+        const char *desc = spice_usbutil_libusb_strerror(rc);
+        g_warning("Error initializing USB hotplug support: %s [%i]", desc, rc);
+        g_set_error(err, SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
+                  "Error initializing USB hotplug support: %s [%i]", desc, rc);
+        return FALSE;
+    }
+    spice_usb_device_manager_start_event_listening(self, NULL);
+#endif
 
     /* Start listening for usb channels connect/disconnect */
     g_signal_connect(priv->session, "channel-new",
@@ -298,6 +327,31 @@ static gboolean spice_usb_device_manager_initable_init(GInitable  *initable,
 #endif
 }
 
+static void spice_usb_device_manager_dispose(GObject *gobject)
+{
+    SpiceUsbDeviceManager *self = SPICE_USB_DEVICE_MANAGER(gobject);
+    SpiceUsbDeviceManagerPrivate *priv = self->priv;
+
+#ifdef USE_USBREDIR
+#ifdef USE_LIBUSB_HOTPLUG
+    if (priv->hp_handle) {
+        spice_usb_device_manager_stop_event_listening(self);
+        /* This also wakes up the libusb_handle_events() in the event_thread */
+        libusb_hotplug_deregister_callback(priv->context, priv->hp_handle);
+        priv->hp_handle = 0;
+    }
+#endif
+    if (priv->event_thread && !priv->event_thread_run) {
+        g_thread_join(priv->event_thread);
+        priv->event_thread = NULL;
+    }
+#endif
+
+    /* Chain up to the parent class */
+    if (G_OBJECT_CLASS(spice_usb_device_manager_parent_class)->dispose)
+        G_OBJECT_CLASS(spice_usb_device_manager_parent_class)->dispose(gobject);
+}
+
 static void spice_usb_device_manager_finalize(GObject *gobject)
 {
     SpiceUsbDeviceManager *self = SPICE_USB_DEVICE_MANAGER(gobject);
@@ -308,11 +362,12 @@ static void spice_usb_device_manager_finalize(GObject *gobject)
         g_ptr_array_unref(priv->devices);
 
 #ifdef USE_USBREDIR
+#ifdef USE_GUDEV
     g_clear_object(&priv->udev);
+#endif
+    g_return_if_fail(priv->event_thread == NULL);
     if (priv->context)
         libusb_exit(priv->context);
-    if (priv->event_thread)
-        g_thread_join(priv->event_thread);
     free(priv->auto_conn_filter_rules);
     free(priv->redirect_on_connect_rules);
 #ifdef G_OS_WIN32
@@ -433,6 +488,7 @@ static void spice_usb_device_manager_class_init(SpiceUsbDeviceManagerClass *klas
     GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
     GParamSpec *pspec;
 
+    gobject_class->dispose      = spice_usb_device_manager_dispose;
     gobject_class->finalize     = spice_usb_device_manager_finalize;
     gobject_class->get_property = spice_usb_device_manager_get_property;
     gobject_class->set_property = spice_usb_device_manager_set_property;
@@ -601,6 +657,7 @@ static void spice_usb_device_manager_class_init(SpiceUsbDeviceManagerClass *klas
 /* ------------------------------------------------------------------ */
 /* gudev / libusb Helper functions                                    */
 
+#ifdef USE_GUDEV
 static gboolean spice_usb_device_manager_get_udev_bus_n_address(
     GUdevDevice *udev, int *bus, int *address)
 {
@@ -622,6 +679,7 @@ static gboolean spice_usb_device_manager_get_udev_bus_n_address(
 
     return *bus && *address;
 }
+#endif
 
 static gboolean spice_usb_device_manager_get_device_descriptor(
     libusb_device *libdev,
@@ -724,6 +782,7 @@ spice_usb_device_manager_device_match(SpiceUsbDevice *device,
             spice_usb_device_get_devaddr(device) == address);
 }
 
+#ifdef USE_GUDEV
 static gboolean
 spice_usb_device_manager_libdev_match(libusb_device *libdev,
                                       const int bus, const int address)
@@ -731,6 +790,7 @@ spice_usb_device_manager_libdev_match(libusb_device *libdev,
     return (libusb_get_bus_number(libdev) == bus &&
             libusb_get_device_address(libdev) == address);
 }
+#endif
 
 #else /* Win32 -- match functions for Windows -- match by vid:pid */
 static gboolean
@@ -846,6 +906,7 @@ static void spice_usb_device_manager_remove_dev(SpiceUsbDeviceManager *self,
     spice_usb_device_unref(device);
 }
 
+#ifdef USE_GUDEV
 static void spice_usb_device_manager_add_udev(SpiceUsbDeviceManager  *self,
                                               GUdevDevice            *udev)
 {
@@ -920,6 +981,50 @@ static void spice_usb_device_manager_uevent_cb(GUdevClient     *client,
     else if (g_str_equal (action, "remove"))
         spice_usb_device_manager_remove_udev(self, udevice);
 }
+#else
+struct hotplug_idle_cb_args {
+    SpiceUsbDeviceManager *self;
+    libusb_device *device;
+    libusb_hotplug_event event;
+};
+
+static gboolean spice_usb_device_manager_hotplug_idle_cb(gpointer user_data)
+{
+    struct hotplug_idle_cb_args *args = user_data;
+    SpiceUsbDeviceManager *self = SPICE_USB_DEVICE_MANAGER(args->self);
+
+    switch (args->event) {
+    case LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED:
+        spice_usb_device_manager_add_dev(self, args->device);
+        break;
+    case LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT:
+        spice_usb_device_manager_remove_dev(self,
+                                    libusb_get_bus_number(args->device),
+                                    libusb_get_device_address(args->device));
+        break;
+    }
+    libusb_unref_device(args->device);
+    g_object_unref(self);
+    g_free(args);
+    return FALSE;
+}
+
+/* Can be called from both the main-thread as well as the event_thread */
+static int spice_usb_device_manager_hotplug_cb(libusb_context       *ctx,
+                                               libusb_device        *device,
+                                               libusb_hotplug_event  event,
+                                               void                 *user_data)
+{
+    SpiceUsbDeviceManager *self = SPICE_USB_DEVICE_MANAGER(user_data);
+    struct hotplug_idle_cb_args *args = g_malloc(sizeof(*args));
+
+    args->self = g_object_ref(self);
+    args->device = libusb_ref_device(device);
+    args->event = event;
+    g_idle_add(spice_usb_device_manager_hotplug_idle_cb, args);
+    return 0;
+}
+#endif
 
 static void spice_usb_device_manager_channel_connect_cb(
     GObject *gobject, GAsyncResult *channel_res, gpointer user_data)
commit 45cfbe8f86ce9808e951bc1117c7ad6d91ed20d9
Author: Hans de Goede <hdegoede at redhat.com>
Date:   Thu Jul 4 12:55:21 2013 +0200

    usb-device-manager: Splitout device add / remove function
    
    Split the device add / remove functions into a gudev specific part and
    a generic part. This is a preparation patch for adding support libusb's
    new hotplug API.
    
    Signed-off-by: Hans de Goede <hdegoede at redhat.com>

diff --git a/gtk/usb-device-manager.c b/gtk/usb-device-manager.c
index 18ad3b8..8b8bb8b 100644
--- a/gtk/usb-device-manager.c
+++ b/gtk/usb-device-manager.c
@@ -157,8 +157,8 @@ static void spice_usb_device_manager_uevent_cb(GUdevClient     *client,
                                                const gchar     *action,
                                                GUdevDevice     *udevice,
                                                gpointer         user_data);
-static void spice_usb_device_manager_add_dev(SpiceUsbDeviceManager  *self,
-                                             GUdevDevice            *udev);
+static void spice_usb_device_manager_add_udev(SpiceUsbDeviceManager  *self,
+                                              GUdevDevice            *udev);
 static void spice_usb_device_manager_check_redir_on_connect(
     SpiceUsbDeviceManager *self, SpiceChannel *channel);
 
@@ -272,7 +272,7 @@ static gboolean spice_usb_device_manager_initable_init(GInitable  *initable,
     libusb_get_device_list(priv->context, &priv->coldplug_list);
     list = g_udev_client_query_by_subsystem(priv->udev, "usb");
     for (it = g_list_first(list); it; it = g_list_next(it)) {
-        spice_usb_device_manager_add_dev(self, it->data);
+        spice_usb_device_manager_add_udev(self, it->data);
         g_object_unref(it->data);
     }
     g_list_free(list);
@@ -773,78 +773,35 @@ spice_usb_device_manager_find_device(SpiceUsbDeviceManager *self,
 }
 
 static void spice_usb_device_manager_add_dev(SpiceUsbDeviceManager  *self,
-                                             GUdevDevice            *udev)
+                                             libusb_device          *libdev)
 {
     SpiceUsbDeviceManagerPrivate *priv = self->priv;
-    libusb_device *libdev = NULL, **dev_list = NULL;
-    SpiceUsbDevice *device = NULL;
-    const gchar *devtype, *devclass;
-    int i, bus, address;
-    gboolean auto_ok = FALSE;
+    struct libusb_device_descriptor desc;
+    SpiceUsbDevice *device;
 
-    devtype = g_udev_device_get_property(udev, "DEVTYPE");
-    /* Check if this is a usb device (and not an interface) */
-    if (!devtype || strcmp(devtype, "usb_device"))
+    if (!spice_usb_device_manager_get_device_descriptor(libdev, &desc))
         return;
 
     /* Skip hubs */
-    devclass = g_udev_device_get_sysfs_attr(udev, "bDeviceClass");
-    if (!devclass || !strcmp(devclass, "09"))
+    if (desc.bDeviceClass == LIBUSB_CLASS_HUB)
         return;
 
-    if (!spice_usb_device_manager_get_udev_bus_n_address(udev, &bus, &address)) {
-        g_warning("USB device without bus number or device address");
+    device = (SpiceUsbDevice*)spice_usb_device_new(libdev);
+    if (!device)
         return;
-    }
 
-    device = spice_usb_device_manager_find_device(self, bus, address);
-    if (device) {
-        SPICE_DEBUG("USB device 0x%04x:0x%04x at %d.%d already exists, ignored",
-                    spice_usb_device_get_vid(device),
-                    spice_usb_device_get_pid(device),
-                    spice_usb_device_get_busnum(device),
-                    spice_usb_device_get_devaddr(device));
-        return;
-    }
-
-    if (priv->coldplug_list)
-        dev_list = priv->coldplug_list;
-    else
-        libusb_get_device_list(priv->context, &dev_list);
+    g_ptr_array_add(priv->devices, device);
 
-    for (i = 0; dev_list && dev_list[i]; i++) {
-        if (spice_usb_device_manager_libdev_match(dev_list[i], bus, address)) {
-            libdev = dev_list[i];
-            break;
-        }
-    }
+    if (priv->auto_connect) {
+        gboolean can_redirect, auto_ok;
 
-    if (libdev)
-        device = (SpiceUsbDevice*)spice_usb_device_new(libdev);
+        can_redirect = spice_usb_device_manager_can_redirect_device(
+                                        self, device, NULL);
 
-    if (device && priv->auto_connect) {
         auto_ok = usbredirhost_check_device_filter(
                             priv->auto_conn_filter_rules,
                             priv->auto_conn_filter_rules_count,
                             libdev, 0) == 0;
-    }
-
-    if (!priv->coldplug_list)
-        libusb_free_device_list(dev_list, 1);
-
-    if (!device) {
-        g_warning("Could not find USB device to add " DEV_ID_FMT,
-                  bus, address);
-        return;
-    }
-
-    g_ptr_array_add(priv->devices, device);
-
-    if (priv->auto_connect) {
-        gboolean can_redirect;
-
-        can_redirect = spice_usb_device_manager_can_redirect_device(
-                                        self, device, NULL);
 
         if (can_redirect && auto_ok)
             spice_usb_device_manager_connect_device_async(self,
@@ -857,15 +814,11 @@ static void spice_usb_device_manager_add_dev(SpiceUsbDeviceManager  *self,
     g_signal_emit(self, signals[DEVICE_ADDED], 0, device);
 }
 
-static void spice_usb_device_manager_remove_dev(SpiceUsbDeviceManager  *self,
-                                                GUdevDevice            *udev)
+static void spice_usb_device_manager_remove_dev(SpiceUsbDeviceManager *self,
+                                                int bus, int address)
 {
     SpiceUsbDeviceManagerPrivate *priv = self->priv;
-    SpiceUsbDevice *device = NULL;
-    int bus, address;
-
-    if (!spice_usb_device_manager_get_udev_bus_n_address(udev, &bus, &address))
-        return;
+    SpiceUsbDevice *device;
 
     device = spice_usb_device_manager_find_device(self, bus, address);
     if (!device) {
@@ -893,6 +846,68 @@ static void spice_usb_device_manager_remove_dev(SpiceUsbDeviceManager  *self,
     spice_usb_device_unref(device);
 }
 
+static void spice_usb_device_manager_add_udev(SpiceUsbDeviceManager  *self,
+                                              GUdevDevice            *udev)
+{
+    SpiceUsbDeviceManagerPrivate *priv = self->priv;
+    libusb_device *libdev = NULL, **dev_list = NULL;
+    SpiceUsbDevice *device;
+    const gchar *devtype;
+    int i, bus, address;
+
+    devtype = g_udev_device_get_property(udev, "DEVTYPE");
+    /* Check if this is a usb device (and not an interface) */
+    if (!devtype || strcmp(devtype, "usb_device"))
+        return;
+
+    if (!spice_usb_device_manager_get_udev_bus_n_address(udev, &bus, &address)) {
+        g_warning("USB device without bus number or device address");
+        return;
+    }
+
+    device = spice_usb_device_manager_find_device(self, bus, address);
+    if (device) {
+        SPICE_DEBUG("USB device 0x%04x:0x%04x at %d.%d already exists, ignored",
+                    spice_usb_device_get_vid(device),
+                    spice_usb_device_get_pid(device),
+                    spice_usb_device_get_busnum(device),
+                    spice_usb_device_get_devaddr(device));
+        return;
+    }
+
+    if (priv->coldplug_list)
+        dev_list = priv->coldplug_list;
+    else
+        libusb_get_device_list(priv->context, &dev_list);
+
+    for (i = 0; dev_list && dev_list[i]; i++) {
+        if (spice_usb_device_manager_libdev_match(dev_list[i], bus, address)) {
+            libdev = dev_list[i];
+            break;
+        }
+    }
+
+    if (libdev)
+        spice_usb_device_manager_add_dev(self, libdev);
+    else
+        g_warning("Could not find USB device to add " DEV_ID_FMT,
+                  bus, address);
+
+    if (!priv->coldplug_list)
+        libusb_free_device_list(dev_list, 1);
+}
+
+static void spice_usb_device_manager_remove_udev(SpiceUsbDeviceManager  *self,
+                                                 GUdevDevice            *udev)
+{
+    int bus, address;
+
+    if (!spice_usb_device_manager_get_udev_bus_n_address(udev, &bus, &address))
+        return;
+
+    spice_usb_device_manager_remove_dev(self, bus, address);
+}
+
 static void spice_usb_device_manager_uevent_cb(GUdevClient     *client,
                                                const gchar     *action,
                                                GUdevDevice     *udevice,
@@ -901,9 +916,9 @@ static void spice_usb_device_manager_uevent_cb(GUdevClient     *client,
     SpiceUsbDeviceManager *self = SPICE_USB_DEVICE_MANAGER(user_data);
 
     if (g_str_equal(action, "add"))
-        spice_usb_device_manager_add_dev(self, udevice);
+        spice_usb_device_manager_add_udev(self, udevice);
     else if (g_str_equal (action, "remove"))
-        spice_usb_device_manager_remove_dev(self, udevice);
+        spice_usb_device_manager_remove_udev(self, udevice);
 }
 
 static void spice_usb_device_manager_channel_connect_cb(


More information about the Spice-commits mailing list