[Spice-devel] [PATCH spice-gtk] usb-device-manager: Add support for libusb hotplug API
Marc-André Lureau
mlureau at redhat.com
Fri Jul 5 04:02:20 PDT 2013
ack
----- Mensaje original -----
> 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
>
> _______________________________________________
> Spice-devel mailing list
> Spice-devel at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/spice-devel
>
More information about the Spice-devel
mailing list