[Spice-devel] [PATCH spice-gtk] usb-device-manager: Add support for libusb hotplug API
Hans de Goede
hdegoede at redhat.com
Fri Jul 5 03:40:37 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 | 119 ++++++++++++++++++++++++++++++++++++++++++++---
2 files changed, 139 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..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)
--
1.8.3.1
More information about the Spice-devel
mailing list