[Spice-devel] [PATCH spice-gtk 2/2] usb-device-manager: Add support for libusb hotplug API

Hans de Goede hdegoede at redhat.com
Thu Jul 4 08:13:23 PDT 2013


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>
---
 configure.ac             | 41 +++++++++++++-------
 gtk/usb-device-manager.c | 98 ++++++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 118 insertions(+), 21 deletions(-)

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..c906150 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
+    spice_usb_device_manager_start_event_listening(self, NULL);
+    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;
+    }
+#endif
 
     /* Start listening for usb channels connect/disconnect */
     g_signal_connect(priv->session, "channel-new",
@@ -308,11 +337,19 @@ 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);
-    if (priv->context)
-        libusb_exit(priv->context);
+#else
+    if (priv->context) {
+        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);
+    }
+#endif
     if (priv->event_thread)
         g_thread_join(priv->event_thread);
+    if (priv->context)
+        libusb_exit(priv->context);
     free(priv->auto_conn_filter_rules);
     free(priv->redirect_on_connect_rules);
 #ifdef G_OS_WIN32
@@ -601,6 +638,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 +660,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 +763,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 +771,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 +887,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 +962,48 @@ 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_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)
+{
+    struct hotplug_idle_cb_args *args = g_malloc(sizeof(*args));
+
+    args->self = user_data;
+    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)
-- 
1.8.3.1



More information about the Spice-devel mailing list