While working on usb redirection support for spice-gtk I needed some code to integrate libusb into glib's mainloop amongst other things. I ended up borrowing code from colord for this. Richard (the colord author) and I quickly agreed that doing generic glib bindings for libusb is a good idea, akin to the gudev bindings for libudev we've called our WIP on this gusb: https://gitorious.org/gusb
Since this very much is a WIP, the API is nowere near stable, so for now we bundle a copy of this code with spice-gtk. When gusb has an official release out the door with a stable API we should switch to that. Signed-off-by: Hans de Goede <hdego...@redhat.com> --- configure.ac | 16 ++ gtk/Makefile.am | 23 +++ gtk/gusb/README.txt | 5 + gtk/gusb/gusb-context-private.h | 35 ++++ gtk/gusb/gusb-context.c | 292 +++++++++++++++++++++++++++++++ gtk/gusb/gusb-context.h | 63 +++++++ gtk/gusb/gusb-device-list.c | 358 +++++++++++++++++++++++++++++++++++++++ gtk/gusb/gusb-device-list.h | 72 ++++++++ gtk/gusb/gusb-device-private.h | 34 ++++ gtk/gusb/gusb-device.c | 232 +++++++++++++++++++++++++ gtk/gusb/gusb-device.h | 71 ++++++++ gtk/gusb/gusb-marshal.h | 7 + gtk/gusb/gusb-source.c | 309 +++++++++++++++++++++++++++++++++ gtk/gusb/gusb-source.h | 58 +++++++ gtk/spice-marshal.txt | 1 + 15 files changed, 1576 insertions(+), 0 deletions(-) create mode 100644 gtk/gusb/README.txt create mode 100644 gtk/gusb/gusb-context-private.h create mode 100644 gtk/gusb/gusb-context.c create mode 100644 gtk/gusb/gusb-context.h create mode 100644 gtk/gusb/gusb-device-list.c create mode 100644 gtk/gusb/gusb-device-list.h create mode 100644 gtk/gusb/gusb-device-private.h create mode 100644 gtk/gusb/gusb-device.c create mode 100644 gtk/gusb/gusb-device.h create mode 100644 gtk/gusb/gusb-marshal.h create mode 100644 gtk/gusb/gusb-source.c create mode 100644 gtk/gusb/gusb-source.h diff --git a/configure.ac b/configure.ac index e60ed78..9fa2b20 100644 --- a/configure.ac +++ b/configure.ac @@ -310,6 +310,21 @@ else AM_CONDITIONAL(WITH_SMARTCARD, true) fi +AC_ARG_ENABLE([usbredir], + AS_HELP_STRING([--enable-usbredir=@<:@yes/no@:>@], + [Enable usbredir support @<:@default=yes@:>@]), + [], + [enable_usbredir="yes"]) + +if test "x$enable_usbredir" = "xno"; then + AM_CONDITIONAL(WITH_USBREDIR, false) +else + PKG_CHECK_MODULES(GUDEV, gudev-1.0) + PKG_CHECK_MODULES(LIBUSB, libusb-1.0 >= 1.0.9) + AC_DEFINE(USE_USBREDIR, [1], [Define if supporting usbredir proxying]) + AM_CONDITIONAL(WITH_USBREDIR, true) +fi + AC_ARG_WITH([coroutine], AS_HELP_STRING([--with-coroutine=@<:@ucontext/gthread/winfiber/auto@:>@], [use ucontext or GThread for coroutines @<:@default=auto@:>@]), @@ -544,6 +559,7 @@ AC_MSG_NOTICE([ Target: ${red_target} SASL support: ${enable_sasl} Smartcard support: ${enable_smartcard} + USB redirection support: ${enable_usbredir} Gtk: $GTK_API_VERSION Now type 'make' to build $PACKAGE diff --git a/gtk/Makefile.am b/gtk/Makefile.am index cbcaa79..1b841e2 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -15,6 +15,7 @@ EXTRA_DIST = \ coroutine_winfibers.c \ continuation.h continuation.c \ map-file \ + gusb/README.txt \ $(NULL) bin_PROGRAMS = spicy snappy spicy-stats @@ -71,6 +72,8 @@ SPICE_COMMON_CPPFLAGS = \ $(SASL_CFLAGS) \ $(GST_CFLAGS) \ $(SMARTCARD_CFLAGS) \ + $(GUDEV_CFLAGS) \ + $(LIBUSB_CFLAGS) \ $(NULL) AM_CPPFLAGS = \ @@ -149,8 +152,27 @@ libspice_client_glib_2_0_la_LIBADD = \ $(GST_LIBS) \ $(SASL_LIBS) \ $(SMARTCARD_LIBS) \ + $(GUDEV_LIBS) \ + $(LIBUSB_LIBS) \ $(NULL) +if WITH_USBREDIR +GUSB_SRCS = \ + gusb/gusb-context.c \ + gusb/gusb-context.h \ + gusb/gusb-context-private.h \ + gusb/gusb-device.c \ + gusb/gusb-device.h \ + gusb/gusb-device-private.h \ + gusb/gusb-device-list.c \ + gusb/gusb-device-list.h \ + gusb/gusb-marshal.h \ + gusb/gusb-source.c \ + gusb/gusb-source.h +else +GUSB_SRCS = +endif + libspice_client_glib_2_0_la_SOURCES = \ spice-audio.c \ spice-common.h \ @@ -189,6 +211,7 @@ libspice_client_glib_2_0_la_SOURCES = \ smartcard-manager.c \ smartcard-manager.h \ smartcard-manager-priv.h \ + $(GUSB_SRCS) \ \ decode.h \ decode-glz.c \ diff --git a/gtk/gusb/README.txt b/gtk/gusb/README.txt new file mode 100644 index 0000000..8d76ed7 --- /dev/null +++ b/gtk/gusb/README.txt @@ -0,0 +1,5 @@ +HDG: this is a private copy of gusb, from: +https://gitorious.org/gusb + +This is a temporary solution until gusb upstream has a stable release out +the door. diff --git a/gtk/gusb/gusb-context-private.h b/gtk/gusb/gusb-context-private.h new file mode 100644 index 0000000..4069a4d --- /dev/null +++ b/gtk/gusb/gusb-context-private.h @@ -0,0 +1,35 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2011 Richard Hughes <rich...@hughsie.com> + * + * Licensed under the GNU Lesser General Public License Version 2.1 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __GUSB_CONTEXT_PRIVATE_H__ +#define __GUSB_CONTEXT_PRIVATE_H__ + +#include <libusb-1.0/libusb.h> + +#include <gusb/gusb-context.h> + +G_BEGIN_DECLS + +libusb_context *_g_usb_context_get_context (GUsbContext *context); + +G_END_DECLS + +#endif /* __GUSB_CONTEXT_PRIVATE_H__ */ diff --git a/gtk/gusb/gusb-context.c b/gtk/gusb/gusb-context.c new file mode 100644 index 0000000..339b821 --- /dev/null +++ b/gtk/gusb/gusb-context.c @@ -0,0 +1,292 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2011 Richard Hughes <rich...@hughsie.com> + * Copyright (C) 2011 Hans de Goede <hdego...@redhat.com> + * + * Licensed under the GNU Lesser General Public License Version 2.1 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +/** + * SECTION:gusb-context + * @short_description: Per-thread instance integration for libusb + * + * This object is used to get a context that is thread safe. + */ + +#include "config.h" + +#include <libusb-1.0/libusb.h> + +#include "gusb-context.h" +#include "gusb-context-private.h" + +/* libusb_strerror is awaiting merging upstream */ +#define libusb_strerror(error) "unknown" + +static void g_usb_context_finalize (GObject *object); + +#define G_USB_CONTEXT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), G_USB_TYPE_CONTEXT, GUsbContextPrivate)) + +enum { + PROP_0, + PROP_LIBUSB_CONTEXT, + PROP_DEBUG_LEVEL, +}; + +/** + * GUsbContextPrivate: + * + * Private #GUsbContext data + **/ +struct _GUsbContextPrivate +{ + libusb_context *context; + int debug_level; +}; + +G_DEFINE_TYPE (GUsbContext, g_usb_context, G_TYPE_OBJECT) + +/** + * usb_context_get_property: + **/ +static void +g_usb_context_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GUsbContext *context = G_USB_CONTEXT (object); + GUsbContextPrivate *priv = context->priv; + + switch (prop_id) { + case PROP_LIBUSB_CONTEXT: + g_value_set_pointer (value, priv->context); + break; + case PROP_DEBUG_LEVEL: + g_value_set_int (value, priv->debug_level); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +/** + * usb_context_set_property: + **/ +static void +g_usb_context_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GUsbContext *context = G_USB_CONTEXT (object); + GUsbContextPrivate *priv = context->priv; + + switch (prop_id) { + case PROP_LIBUSB_CONTEXT: + priv->context = g_value_get_pointer (value); + break; + case PROP_DEBUG_LEVEL: + priv->debug_level = g_value_get_int (value); + libusb_set_debug (priv->context, priv->debug_level); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static GObject * +g_usb_context_constructor (GType gtype, + guint n_properties, + GObjectConstructParam *properties) +{ + GObject *obj; + GUsbContext *context; + GUsbContextPrivate *priv; + + { + /* Always chain up to the parent constructor */ + GObjectClass *parent_class; + parent_class = G_OBJECT_CLASS (g_usb_context_parent_class); + obj = parent_class->constructor (gtype, n_properties, + properties); + } + + context = G_USB_CONTEXT (obj); + priv = context->priv; + + /* + * Yes you're reading this right the sole reason for this constructor + * is to check the context has been set (for now). + */ + if (!priv->context) + g_error("constructed without a context"); + + return obj; +} + +/** + * usb_context_class_init: + **/ +static void +g_usb_context_class_init (GUsbContextClass *klass) +{ + GParamSpec *pspec; + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->constructor = g_usb_context_constructor; + object_class->finalize = g_usb_context_finalize; + object_class->get_property = g_usb_context_get_property; + object_class->set_property = g_usb_context_set_property; + + /** + * GUsbContext:libusb_context: + */ + pspec = g_param_spec_pointer ("libusb_context", NULL, NULL, + G_PARAM_CONSTRUCT_ONLY| + G_PARAM_READWRITE); + g_object_class_install_property (object_class, PROP_LIBUSB_CONTEXT, + pspec); + + /** + * GUsbContext:debug_level: + */ + pspec = g_param_spec_int ("debug_level", NULL, NULL, + 0, 3, 0, + G_PARAM_READWRITE); + g_object_class_install_property (object_class, PROP_DEBUG_LEVEL, + pspec); + + g_type_class_add_private (klass, sizeof (GUsbContextPrivate)); +} + +/** + * g_usb_context_init: + **/ +static void +g_usb_context_init (GUsbContext *context) +{ + context->priv = G_USB_CONTEXT_GET_PRIVATE (context); +} + +/** + * g_usb_context_finalize: + **/ +static void +g_usb_context_finalize (GObject *object) +{ + GUsbContext *context = G_USB_CONTEXT (object); + GUsbContextPrivate *priv = context->priv; + + libusb_exit (priv->context); + + G_OBJECT_CLASS (g_usb_context_parent_class)->finalize (object); +} + +/** + * g_usb_context_error_quark: + * + * Return value: Our personal error quark. + * + * Since: 0.0.1 + **/ +GQuark +g_usb_context_error_quark (void) +{ + static GQuark quark = 0; + if (!quark) + quark = g_quark_from_static_string ("g_usb_context_error"); + return quark; +} + +/** + * _g_usb_context_get_context: + * @context: a #GUsbContext + * + * Gets the internal libusb_context. + * + * Return value: (transfer none): the libusb_context + * + * Since: 0.0.1 + **/ +libusb_context * +_g_usb_context_get_context (GUsbContext *context) +{ + return context->priv->context; +} + +/** + * g_usb_context_set_debug: + * @context: a #GUsbContext + * @flags: a GLogLevelFlags such as %G_LOG_LEVEL_ERROR | %G_LOG_LEVEL_INFO, or 0 + * + * Sets the debug flags which control what is logged to the console. + * + * Using %G_LOG_LEVEL_INFO will output to standard out, and everything + * else logs to standard error. + * + * Since: 0.0.1 + **/ +void +g_usb_context_set_debug (GUsbContext *context, GLogLevelFlags flags) +{ + GUsbContextPrivate *priv = context->priv; + + if (flags & (G_LOG_LEVEL_DEBUG | G_LOG_LEVEL_INFO)) + priv->debug_level = 3; + else if (flags & (G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_WARNING)) + priv->debug_level = 2; + else if (flags & (G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_ERROR)) + priv->debug_level = 1; + else + priv->debug_level = 0; + + libusb_set_debug (priv->context, priv->debug_level); +} + +/** + * g_usb_context_new: + * @error: a #GError, or %NULL + * + * Creates a new context for accessing USB devices. + * + * Return value: a new %GUsbContext object or %NULL on error. + * + * Since: 0.0.1 + **/ +GUsbContext * +g_usb_context_new (GError **error) +{ + gint rc; + GObject *obj; + libusb_context *context; + + rc = libusb_init (&context); + if (rc < 0) { + g_set_error (error, + G_USB_CONTEXT_ERROR, + G_USB_CONTEXT_ERROR_INTERNAL, + "failed to init libusb: %s [%i]", + libusb_strerror (rc), rc); + return NULL; + } + + obj = g_object_new (G_USB_TYPE_CONTEXT, "libusb_context", context, + NULL); + return G_USB_CONTEXT (obj); +} diff --git a/gtk/gusb/gusb-context.h b/gtk/gusb/gusb-context.h new file mode 100644 index 0000000..ca9d28c --- /dev/null +++ b/gtk/gusb/gusb-context.h @@ -0,0 +1,63 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2011 Richard Hughes <rich...@hughsie.com> + * Copyright (C) 2011 Hans de Goede <hdego...@redhat.com> + * + * Licensed under the GNU Lesser General Public License Version 2.1 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __GUSB_CONTEXT_H__ +#define __GUSB_CONTEXT_H__ + +#include <glib-object.h> + +G_BEGIN_DECLS + +#define G_USB_TYPE_CONTEXT (g_usb_context_get_type ()) +#define G_USB_CONTEXT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_USB_TYPE_CONTEXT, GUsbContext)) +#define G_USB_IS_CONTEXT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_USB_TYPE_CONTEXT)) +#define G_USB_CONTEXT_ERROR (g_usb_context_error_quark ()) + +typedef struct _GUsbContextPrivate GUsbContextPrivate; +typedef struct _GUsbContext GUsbContext; +typedef struct _GUsbContextClass GUsbContextClass; + +struct _GUsbContext +{ + GObject parent; + GUsbContextPrivate *priv; +}; + +struct _GUsbContextClass +{ + GObjectClass parent_class; +}; + +typedef enum { + G_USB_CONTEXT_ERROR_INTERNAL +} GUsbContextError; + +GType g_usb_context_get_type (void); +GQuark g_usb_context_error_quark (void); + +GUsbContext *g_usb_context_new (GError **error); + +void g_usb_context_set_debug (GUsbContext *context, + GLogLevelFlags flags); + +G_END_DECLS + +#endif /* __GUSB_CONTEXT_H__ */ diff --git a/gtk/gusb/gusb-device-list.c b/gtk/gusb/gusb-device-list.c new file mode 100644 index 0000000..dbe73f8 --- /dev/null +++ b/gtk/gusb/gusb-device-list.c @@ -0,0 +1,358 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2011 Hans de Goede <hdego...@redhat.com> + * + * Licensed under the GNU Lesser General Public License Version 2.1 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <string.h> +#include <stdlib.h> +#include <gudev/gudev.h> +#include <libusb-1.0/libusb.h> + +#include "gusb-marshal.h" +#include "gusb-context.h" +#include "gusb-context-private.h" +#include "gusb-device.h" +#include "gusb-device-private.h" + +#include "gusb-device-list.h" + +#define G_USB_DEVICE_LIST_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), G_USB_TYPE_DEVICE_LIST, GUsbDeviceListPrivate)) + +enum { + PROP_0, + PROP_CONTEXT, +}; + +enum +{ + DEVICE_ADDED_SIGNAL, + DEVICE_REMOVED_SIGNAL, + LAST_SIGNAL, +}; + +struct _GUsbDeviceListPrivate { + GUsbContext *context; + GUdevClient *udev; + GPtrArray *devices; + libusb_device **coldplug_list; +}; + +static void g_usb_device_list_uevent_cb (GUdevClient *client, + const gchar *action, + GUdevDevice *udevice, + gpointer user_data); + +static guint signals[LAST_SIGNAL] = { 0, }; + +G_DEFINE_TYPE (GUsbDeviceList, g_usb_device_list, G_TYPE_OBJECT); + +static void +g_usb_device_list_finalize (GObject *object) +{ + GUsbDeviceList *list = G_USB_DEVICE_LIST (object); + GUsbDeviceListPrivate *priv = list->priv; + + g_object_unref (priv->udev); + g_ptr_array_unref (priv->devices); +} + +/** + * g_usb_device_list_get_property: + **/ +static void +g_usb_device_list_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GUsbDeviceList *list = G_USB_DEVICE_LIST (object); + GUsbDeviceListPrivate *priv = list->priv; + + switch (prop_id) { + case PROP_CONTEXT: + g_value_set_object (value, priv->context); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +/** + * usb_device_list_set_property: + **/ +static void +g_usb_device_list_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GUsbDeviceList *list = G_USB_DEVICE_LIST (object); + GUsbDeviceListPrivate *priv = list->priv; + + switch (prop_id) { + case PROP_CONTEXT: + priv->context = g_value_get_object (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static GObject * +g_usb_device_list_constructor (GType gtype, + guint n_properties, + GObjectConstructParam *properties) +{ + GObject *obj; + GUsbDeviceList *list; + GUsbDeviceListPrivate *priv; + const gchar *const subsystems[] = {"usb", NULL}; + + { + /* Always chain up to the parent constructor */ + GObjectClass *parent_class; + parent_class = G_OBJECT_CLASS (g_usb_device_list_parent_class); + obj = parent_class->constructor (gtype, n_properties, + properties); + } + + list = G_USB_DEVICE_LIST (obj); + priv = list->priv; + + if (!priv->context) + g_error("constructed without a context"); + + priv->udev = g_udev_client_new (subsystems); + g_signal_connect (G_OBJECT (priv->udev), "uevent", + G_CALLBACK (g_usb_device_list_uevent_cb), list); + + priv->devices = g_ptr_array_new_with_free_func ((GDestroyNotify) + g_object_unref); + + priv->coldplug_list = NULL; + + return obj; +} + +/** + * g_usb_device_list_class_init: + **/ +static void +g_usb_device_list_class_init (GUsbDeviceListClass *klass) +{ + GParamSpec *pspec; + GObjectClass *object_class = (GObjectClass *) klass; + + object_class->constructor = g_usb_device_list_constructor; + object_class->finalize = g_usb_device_list_finalize; + object_class->get_property = g_usb_device_list_get_property; + object_class->set_property = g_usb_device_list_set_property; + + /** + * GUsbDeviceList:context: + */ + pspec = g_param_spec_object ("context", NULL, NULL, + G_USB_TYPE_CONTEXT, + G_PARAM_CONSTRUCT_ONLY| + G_PARAM_READWRITE); + g_object_class_install_property (object_class, PROP_CONTEXT, pspec); + + signals[DEVICE_ADDED_SIGNAL] = g_signal_new ("device_added", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GUsbDeviceListClass, device_added), + NULL, + NULL, + g_cclosure_user_marshal_VOID__OBJECT_OBJECT, + G_TYPE_NONE, + 2, + G_TYPE_OBJECT, + G_TYPE_OBJECT); + + signals[DEVICE_REMOVED_SIGNAL] = g_signal_new ("device_removed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GUsbDeviceListClass, device_removed), + NULL, + NULL, + g_cclosure_user_marshal_VOID__OBJECT_OBJECT, + G_TYPE_NONE, + 2, + G_TYPE_OBJECT, + G_TYPE_OBJECT); + + g_type_class_add_private (klass, sizeof (GUsbDeviceListPrivate)); +} + +static void +g_usb_device_list_init (GUsbDeviceList *list) +{ + list->priv = G_USB_DEVICE_LIST_GET_PRIVATE (list); +} + +static gboolean +g_usb_device_list_get_bus_n_address (GUdevDevice *udev, + gint *bus, + gint *address) +{ + const gchar *bus_str, *address_str; + + *bus = *address = 0; + + bus_str = g_udev_device_get_property (udev, "BUSNUM"); + address_str = g_udev_device_get_property (udev, "DEVNUM"); + if (bus_str) + *bus = atoi(bus_str); + if (address_str) + *address = atoi(address_str); + + return *bus && *address; +} + +static void +g_usb_device_list_add_dev (GUsbDeviceList *list, GUdevDevice *udev) +{ + GUsbDeviceListPrivate *priv = list->priv; + GUsbDevice *device = NULL; + libusb_device **dev_list = NULL; + const gchar *devtype, *devclass; + gint i, bus, address; + libusb_context *ctx = _g_usb_context_get_context (priv->context); + + 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; + + /* Skip hubs */ + devclass = g_udev_device_get_sysfs_attr(udev, "bDeviceClass"); + if (!devclass || !strcmp(devclass, "09")) + return; + + if (!g_usb_device_list_get_bus_n_address (udev, &bus, &address)) { + g_warning ("usb-device without bus number or device address"); + return; + } + + if (priv->coldplug_list) + dev_list = priv->coldplug_list; + else + libusb_get_device_list(ctx, &dev_list); + + for (i = 0; dev_list && dev_list[i]; i++) { + if (libusb_get_bus_number (dev_list[i]) == bus && + libusb_get_device_address (dev_list[i]) == address) { + device = _g_usb_device_new (dev_list[i]); + break; + } + } + + if (!priv->coldplug_list) + libusb_free_device_list (dev_list, 1); + + if (!device) { + g_warning ("Could not find usb dev at busnum %d devaddr %d", + bus, address); + return; + } + + g_ptr_array_add (priv->devices, device); + g_signal_emit (list, signals[DEVICE_ADDED_SIGNAL], 0, device, udev); +} + +static void +g_usb_device_list_remove_dev (GUsbDeviceList *list, GUdevDevice *udev) +{ + GUsbDeviceListPrivate *priv = list->priv; + GUsbDevice *device; + gint bus, address; + + if (!g_usb_device_list_get_bus_n_address (udev, &bus, &address)) + return; + + device = g_usb_device_list_get_dev_by_bus_n_address (list, bus, + address); + if (!device) + return; + + g_signal_emit (list, signals[DEVICE_REMOVED_SIGNAL], 0, device, udev); + g_ptr_array_remove (priv->devices, device); +} + +static void +g_usb_device_list_uevent_cb (GUdevClient *client, + const gchar *action, + GUdevDevice *udevice, + gpointer user_data) +{ + GUsbDeviceList *list = G_USB_DEVICE_LIST (user_data); + + if (g_str_equal (action, "add")) + g_usb_device_list_add_dev (list, udevice); + else if (g_str_equal (action, "remove")) + g_usb_device_list_remove_dev (list, udevice); +} + +void +g_usb_device_list_coldplug (GUsbDeviceList *list) +{ + GUsbDeviceListPrivate *priv = list->priv; + GList *devices, *elem; + libusb_context *ctx = _g_usb_context_get_context (priv->context); + + libusb_get_device_list(ctx, &priv->coldplug_list); + devices = g_udev_client_query_by_subsystem (priv->udev, "usb"); + for (elem = g_list_first (devices); elem; elem = g_list_next (elem)) { + g_usb_device_list_add_dev (list, elem->data); + g_object_unref (elem->data); + } + g_list_free (devices); + libusb_free_device_list (priv->coldplug_list, 1); + priv->coldplug_list = NULL; +} + +GUsbDevice * +g_usb_device_list_get_dev_by_bus_n_address (GUsbDeviceList *list, + guint8 bus, + guint8 address) +{ + GUsbDeviceListPrivate *priv = list->priv; + GUsbDevice *device = NULL; + guint i; + + for (i = 0; i < priv->devices->len; i++) { + GUsbDevice *curr = g_ptr_array_index (priv->devices, i); + if (g_usb_device_get_bus (curr) == bus && + g_usb_device_get_address (curr) == address) { + device = curr; + break; + } + } + + return device; +} + +GUsbDeviceList * +g_usb_device_list_new (GUsbContext *context) +{ + GObject *obj; + obj = g_object_new (G_USB_TYPE_DEVICE_LIST, "context", context, NULL); + return G_USB_DEVICE_LIST (obj); +} diff --git a/gtk/gusb/gusb-device-list.h b/gtk/gusb/gusb-device-list.h new file mode 100644 index 0000000..430984f --- /dev/null +++ b/gtk/gusb/gusb-device-list.h @@ -0,0 +1,72 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2011 Hans de Goede <hdego...@redhat.com> + * + * Licensed under the GNU Lesser General Public License Version 2.1 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __GUSB_DEVICE_LIST_H__ +#define __GUSB_DEVICE_LIST_H__ + +#include <glib-object.h> +#include <gudev/gudev.h> + +#include <gusb/gusb-context.h> +#include <gusb/gusb-device.h> + +G_BEGIN_DECLS + +#define G_USB_TYPE_DEVICE_LIST (g_usb_device_list_get_type ()) +#define G_USB_DEVICE_LIST(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_USB_TYPE_DEVICE_LIST, GUsbDeviceList)) +#define G_USB_IS_DEVICE_LIST(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_USB_TYPE_DEVICE_LIST)) + +typedef struct _GUsbDeviceListPrivate GUsbDeviceListPrivate; +typedef struct _GUsbDeviceList GUsbDeviceList; +typedef struct _GUsbDeviceListClass GUsbDeviceListClass; + +struct _GUsbDeviceList +{ + GObject parent; + GUsbDeviceListPrivate *priv; +}; + +struct _GUsbDeviceListClass +{ + GObjectClass parent_class; + /* Signals */ + void (*device_added) (GUsbDeviceList *list, + GUsbDevice *device, + GUdevDevice *udev); + void (*device_removed) (GUsbDeviceList *list, + GUsbDevice *device, + GUdevDevice *udev); +}; + +GType g_usb_device_list_get_type (void); + +GUsbDeviceList *g_usb_device_list_new (GUsbContext *context); + +void g_usb_device_list_coldplug (GUsbDeviceList *list); + +GUsbDevice *g_usb_device_list_get_dev_by_bus_n_address ( + GUsbDeviceList *list, + guint8 bus, + guint8 address); + + +G_END_DECLS + +#endif /* __GUSB_DEVICE_LIST_H__ */ diff --git a/gtk/gusb/gusb-device-private.h b/gtk/gusb/gusb-device-private.h new file mode 100644 index 0000000..c93b51a --- /dev/null +++ b/gtk/gusb/gusb-device-private.h @@ -0,0 +1,34 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2011 Hans de Goede <hdego...@redhat.com> + * + * Licensed under the GNU Lesser General Public License Version 2.1 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __GUSB_DEVICE_PRIVATE_H__ +#define __GUSB_DEVICE_PRIVATE_H__ + +#include <gusb/gusb-device.h> + +G_BEGIN_DECLS + +GUsbDevice *_g_usb_device_new (libusb_device *device); + +libusb_device *_g_usb_device_get_device (GUsbDevice *device); + +G_END_DECLS + +#endif /* __GUSB_DEVICE_PRIVATE_H__ */ diff --git a/gtk/gusb/gusb-device.c b/gtk/gusb/gusb-device.c new file mode 100644 index 0000000..26a25f3 --- /dev/null +++ b/gtk/gusb/gusb-device.c @@ -0,0 +1,232 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2010-2011 Richard Hughes <rich...@hughsie.com> + * Copyright (C) 2011 Hans de Goede <hdego...@redhat.com> + * + * Licensed under the GNU Lesser General Public License Version 2.1 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +/** + * SECTION:usb-device + * @short_description: GLib device integration for libusb + * + * This object is a thin glib wrapper around a libusb_device + */ + +#include "config.h" + +#include <libusb-1.0/libusb.h> + +#include "gusb-device.h" +#include "gusb-device-private.h" + +static void g_usb_device_finalize (GObject *object); + +#define G_USB_DEVICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), G_USB_TYPE_DEVICE, GUsbDevicePrivate)) + +/** + * GUsbDevicePrivate: + * + * Private #GUsbDevice data + **/ +struct _GUsbDevicePrivate +{ + libusb_device *device; +}; + +enum { + PROP_0, + PROP_LIBUSB_DEVICE, +}; + +G_DEFINE_TYPE (GUsbDevice, g_usb_device, G_TYPE_OBJECT) + + +/** + * g_usb_device_error_quark: + * + * Return value: Our personal error quark. + * + * Since: 0.0.1 + **/ +GQuark +g_usb_device_error_quark (void) +{ + static GQuark quark = 0; + if (!quark) + quark = g_quark_from_static_string ("g_usb_device_error"); + return quark; +} + +/** + * usb_device_get_property: + **/ +static void +g_usb_device_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GUsbDevice *device = G_USB_DEVICE (object); + GUsbDevicePrivate *priv = device->priv; + + switch (prop_id) { + case PROP_LIBUSB_DEVICE: + g_value_set_pointer (value, priv->device); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +/** + * usb_device_set_property: + **/ +static void +g_usb_device_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GUsbDevice *device = G_USB_DEVICE (object); + GUsbDevicePrivate *priv = device->priv; + + switch (prop_id) { + case PROP_LIBUSB_DEVICE: + priv->device = g_value_get_pointer (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static GObject * +g_usb_device_constructor (GType gtype, + guint n_properties, + GObjectConstructParam *properties) +{ + GObject *obj; + GUsbDevice *device; + GUsbDevicePrivate *priv; + + { + /* Always chain up to the parent constructor */ + GObjectClass *parent_class; + parent_class = G_OBJECT_CLASS (g_usb_device_parent_class); + obj = parent_class->constructor (gtype, n_properties, + properties); + } + + device = G_USB_DEVICE (obj); + priv = device->priv; + + if (!priv->device) + g_error("constructed without a libusb_device"); + + libusb_ref_device(priv->device); + + return obj; +} + +/** + * usb_device_class_init: + **/ +static void +g_usb_device_class_init (GUsbDeviceClass *klass) +{ + GParamSpec *pspec; + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->constructor = g_usb_device_constructor; + object_class->finalize = g_usb_device_finalize; + object_class->get_property = g_usb_device_get_property; + object_class->set_property = g_usb_device_set_property; + + /** + * GUsbDevice:libusb_device: + */ + pspec = g_param_spec_pointer ("libusb_device", NULL, NULL, + G_PARAM_CONSTRUCT_ONLY| + G_PARAM_READWRITE); + g_object_class_install_property (object_class, PROP_LIBUSB_DEVICE, + pspec); + + g_type_class_add_private (klass, sizeof (GUsbDevicePrivate)); +} + +/** + * g_usb_device_init: + **/ +static void +g_usb_device_init (GUsbDevice *device) +{ + device->priv = G_USB_DEVICE_GET_PRIVATE (device); +} + +/** + * g_usb_device_finalize: + **/ +static void +g_usb_device_finalize (GObject *object) +{ + GUsbDevice *device = G_USB_DEVICE (object); + GUsbDevicePrivate *priv = device->priv; + + libusb_unref_device(priv->device); + + G_OBJECT_CLASS (g_usb_device_parent_class)->finalize (object); +} + +/** + * _g_usb_device_new: + * + * Return value: a new #GUsbDevice object. + **/ +GUsbDevice * +_g_usb_device_new (libusb_device *device) +{ + GObject *obj; + obj = g_object_new (G_USB_TYPE_DEVICE, "libusb_device", device, NULL); + return G_USB_DEVICE (obj); +} + +/** + * _g_usb_device_get_device: + * @device: a #GUsbDevice instance + * + * Gets the low-level libusb_device + * + * Return value: The #libusb_device or %NULL. Do not unref this value. + **/ +libusb_device * +_g_usb_device_get_device (GUsbDevice *device) +{ + return device->priv->device; +} + +guint8 +g_usb_device_get_bus (GUsbDevice *device) +{ + return libusb_get_bus_number (device->priv->device); +} + +guint8 +g_usb_device_get_address (GUsbDevice *device) +{ + return libusb_get_device_address (device->priv->device); +} diff --git a/gtk/gusb/gusb-device.h b/gtk/gusb/gusb-device.h new file mode 100644 index 0000000..a504182 --- /dev/null +++ b/gtk/gusb/gusb-device.h @@ -0,0 +1,71 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2010-2011 Richard Hughes <rich...@hughsie.com> + * Copyright (C) 2011 Hans de Goede <hdego...@redhat.com> + * + * Licensed under the GNU Lesser General Public License Version 2.1 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __GUSB_DEVICE_H__ +#define __GUSB_DEVICE_H__ + +#include <glib-object.h> + +G_BEGIN_DECLS + +#define G_USB_TYPE_DEVICE (g_usb_device_get_type ()) +#define G_USB_DEVICE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_USB_TYPE_DEVICE, GUsbDevice)) +#define G_USB_IS_DEVICE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_USB_TYPE_DEVICE)) +#define G_USB_DEVICE_ERROR (g_usb_device_error_quark ()) + +typedef struct _GUsbDevicePrivate GUsbDevicePrivate; +typedef struct _GUsbDevice GUsbDevice; +typedef struct _GUsbDeviceClass GUsbDeviceClass; + +/** + * GUsbDeviceError: + * + * The error code. + **/ +typedef enum { + G_USB_DEVICE_ERROR_INTERNAL +} GUsbDeviceError; + +struct _GUsbDevice +{ + GObject parent; + GUsbDevicePrivate *priv; +}; + +struct _GUsbDeviceClass +{ + GObjectClass parent_class; +}; + +GType g_usb_device_get_type (void); +GQuark g_usb_device_error_quark (void); + +guint8 g_usb_device_get_bus (GUsbDevice *device); +guint8 g_usb_device_get_address (GUsbDevice *device); + +#if 0 /* TODO */ +GUsbDeviceHandle *g_usb_device_get_device_handle (GUsbDevice *device, + GError **err); +#endif + +G_END_DECLS + +#endif /* __GUSB_DEVICE_H__ */ diff --git a/gtk/gusb/gusb-marshal.h b/gtk/gusb/gusb-marshal.h new file mode 100644 index 0000000..b00fd65 --- /dev/null +++ b/gtk/gusb/gusb-marshal.h @@ -0,0 +1,7 @@ +/* + * HDG: hack + * Important: Don't forget to remove VOID:OBJECT,OBJECT from spice-marshal.txt + * when we remove our bundled gusb. + */ + +#include "spice-marshal.h" diff --git a/gtk/gusb/gusb-source.c b/gtk/gusb/gusb-source.c new file mode 100644 index 0000000..0324e68 --- /dev/null +++ b/gtk/gusb/gusb-source.c @@ -0,0 +1,309 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2010-2011 Richard Hughes <rich...@hughsie.com> + * Copyright (C) 2011 Hans de Goede <hdego...@redhat.com> + * + * Licensed under the GNU Lesser General Public License Version 2.1 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * SECTION:gusb-source + * @short_description: GSource integration for libusb + * + * This object can be used to integrate libusb into the GLib main loop. + */ + +#include "config.h" + +#include <libusb-1.0/libusb.h> +#include <poll.h> +#include <stdlib.h> + +#include "gusb-context.h" +#include "gusb-context-private.h" +#include "gusb-source.h" + +/** + * g_usb_source_error_quark: + * + * Return value: Our personal error quark. + * + * Since: 0.0.1 + **/ +GQuark +g_usb_source_error_quark (void) +{ + static GQuark quark = 0; + if (!quark) + quark = g_quark_from_static_string ("g_usb_source_error"); + return quark; +} + +/* libusb_strerror is awaiting merging upstream */ +#define libusb_strerror(error) "unknown" + +struct _GUsbSource { + GSource source; + GSList *pollfds; + GUsbContext *usbcontext; + libusb_context *ctx; +}; + +static void +g_usb_source_pollfd_add (GUsbSource *source, int fd, short events) +{ + GPollFD *pollfd = g_slice_new(GPollFD); + pollfd->fd = fd; + pollfd->events = 0; + pollfd->revents = 0; + if (events & POLLIN) + pollfd->events |= G_IO_IN; + if (events & POLLOUT) + pollfd->events |= G_IO_OUT; + + source->pollfds = g_slist_prepend(source->pollfds, pollfd); + g_source_add_poll((GSource *)source, pollfd); +} + +static void +g_usb_source_pollfd_added_cb (int fd, short events, void *user_data) +{ + GUsbSource *source = user_data; + g_usb_source_pollfd_add (source, fd, events); +} + +static void +g_usb_source_pollfd_remove (GUsbSource *source, int fd) +{ + GPollFD *pollfd; + GSList *elem = source->pollfds; + + /* nothing to see here, move along */ + if (elem == NULL) { + g_warning("cannot remove from list as list is empty?"); + return; + } + + /* find the pollfd in the list */ + do { + pollfd = elem->data; + if (pollfd->fd != fd) + continue; + + g_source_remove_poll((GSource *)source, pollfd); + g_slice_free(GPollFD, pollfd); + source->pollfds = g_slist_delete_link(source->pollfds, elem); + return; + } while ((elem = g_slist_next(elem))); + g_warning ("couldn't find fd %d in list", fd); +} + +static void +g_usb_source_pollfd_removed_cb(int fd, void *user_data) +{ + GUsbSource *source = user_data; + + g_usb_source_pollfd_remove (source, fd); +} + +static void +g_usb_source_pollfd_remove_all (GUsbSource *source) +{ + GPollFD *pollfd; + GSList *curr, *next; + + next = source->pollfds; + while (next) { + curr = next; + next = g_slist_next(curr); + pollfd = curr->data; + g_source_remove_poll((GSource *)source, pollfd); + g_slice_free (GPollFD, pollfd); + source->pollfds = g_slist_delete_link(source->pollfds, curr); + } +} + +/** + * g_usb_source_prepare: + * + * Called before all the file descriptors are polled. + * As we are a file descriptor source, the prepare function returns FALSE. + * It sets the returned timeout to -1 to indicate that it doesn't mind + * how long the poll() call blocks. + * + * No, we're not going to support FreeBSD. + **/ +static gboolean +g_usb_source_prepare (GSource *source, gint *timeout) +{ + *timeout = -1; + return FALSE; +} + +/** + * g_usb_source_check: + * + * In the check function, it tests the results of the poll() call to see + * if the required condition has been met, and returns TRUE if so. + **/ +static gboolean +g_usb_source_check (GSource *source) +{ + GUsbSource *usb_source = (GUsbSource *)source; + GPollFD *pollfd; + GSList *elem = usb_source->pollfds; + + /* no fds */ + if (elem == NULL) + return FALSE; + + /* check each pollfd */ + do { + pollfd = elem->data; + if (pollfd->revents) + return TRUE; + } while ((elem = g_slist_next(elem))); + + return FALSE; +} + +static gboolean +g_usb_source_dispatch (GSource *source, + GSourceFunc callback, + gpointer user_data) +{ + GUsbSource *usb_source = (GUsbSource *)source; + struct timeval tv = { 0, 0 }; + gint rc; + + rc = libusb_handle_events_timeout (usb_source->ctx, &tv); + if (rc < 0) { + g_warning ("failed to handle event: %s [%i]", + libusb_strerror (rc), rc); + } + + if (callback) + callback(user_data); + + return TRUE; +} + +static void +g_usb_source_finalize (GSource *source) +{ + GUsbSource *usb_source = (GUsbSource *)source; + g_object_unref (usb_source->usbcontext); + g_slist_free (usb_source->pollfds); +} + +static GSourceFuncs usb_source_funcs = { + g_usb_source_prepare, + g_usb_source_check, + g_usb_source_dispatch, + g_usb_source_finalize, + NULL, NULL +}; + +/** + * g_usb_source_new: + * @main_ctx: a #GMainContext, or %NULL + * @gusb_ctx: a #GUsbContext + * @error: a #GError, or %NULL + * + * Creates a source for integration into libusb1. + * + * Return value: (transfer none): the #GUsbSource, or %NULL. Use g_usb_source_destroy() to unref. + * + * Since: 0.0.1 + **/ +GUsbSource * +g_usb_source_new (GMainContext *main_ctx, + GUsbContext *gusb_ctx, + GError **error) +{ + guint i; + const struct libusb_pollfd **pollfds; + GUsbSource *gusb_source; + + gusb_source = (GUsbSource *)g_source_new (&usb_source_funcs, + sizeof(GUsbSource)); + gusb_source->pollfds = NULL; + gusb_source->usbcontext = g_object_ref (gusb_ctx); + gusb_source->ctx = _g_usb_context_get_context (gusb_ctx); + + /* watch the fd's already created */ + pollfds = libusb_get_pollfds (gusb_source->ctx); + if (pollfds == NULL) { + g_set_error_literal (error, + G_USB_SOURCE_ERROR, + G_USB_SOURCE_ERROR_INTERNAL, + "failed to allocate memory"); + g_free (gusb_source); + gusb_source = NULL; + goto out; + } + for (i=0; pollfds[i] != NULL; i++) + g_usb_source_pollfd_add (gusb_source, + pollfds[i]->fd, + pollfds[i]->events); + free (pollfds); + + /* watch for PollFD changes */ + g_source_attach ((GSource *)gusb_source, main_ctx); + libusb_set_pollfd_notifiers (gusb_source->ctx, + g_usb_source_pollfd_added_cb, + g_usb_source_pollfd_removed_cb, + gusb_source); +out: + return gusb_source; +} + +/** + * g_usb_source_destroy: + * @source: a #GUsbSource + * + * Destroys a #GUsbSource + * + * Since: 0.0.1 + **/ +void +g_usb_source_destroy (GUsbSource *source) +{ + libusb_set_pollfd_notifiers (source->ctx, NULL, NULL, NULL); + g_usb_source_pollfd_remove_all (source); + g_source_destroy ((GSource *)source); +} + +/** + * g_usb_source_set_callback: + * @source: a #GUsbSource + * @func: a function to call + * @data: data to pass to @func + * @notify: a #GDestroyNotify + * + * Set a callback to be called when the source is dispatched. + * + * Since: 0.0.1 + **/ +void +g_usb_source_set_callback (GUsbSource *source, + GSourceFunc func, + gpointer data, + GDestroyNotify notify) +{ + g_source_set_callback ((GSource *)source, func, data, notify); +} diff --git a/gtk/gusb/gusb-source.h b/gtk/gusb/gusb-source.h new file mode 100644 index 0000000..40f6880 --- /dev/null +++ b/gtk/gusb/gusb-source.h @@ -0,0 +1,58 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2010-2011 Richard Hughes <rich...@hughsie.com> + * Copyright (C) 2011 Hans de Goede <hdego...@redhat.com> + * + * Licensed under the GNU Lesser General Public License Version 2.1 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __GUSB_SOURCE_H__ +#define __GUSB_SOURCE_H__ + +#include <glib.h> + +#include <gusb/gusb-context.h> + +G_BEGIN_DECLS + +#define G_USB_SOURCE_ERROR (g_usb_source_error_quark ()) + +typedef struct _GUsbSource GUsbSource; + +/** + * GUsbSourceError: + * + * The error code. + **/ +typedef enum { + G_USB_SOURCE_ERROR_INTERNAL +} GUsbSourceError; + +GQuark g_usb_source_error_quark (void); +GUsbSource *g_usb_source_new (GMainContext *main_ctx, + GUsbContext *gusb_ctx, + GError **error); +void g_usb_source_destroy (GUsbSource *source); + +void g_usb_source_set_callback (GUsbSource *source, + GSourceFunc func, + gpointer data, + GDestroyNotify notify); + +G_END_DECLS + +#endif /* __GUSB_SOURCE_H__ */ diff --git a/gtk/spice-marshal.txt b/gtk/spice-marshal.txt index 8e56394..b9630eb 100644 --- a/gtk/spice-marshal.txt +++ b/gtk/spice-marshal.txt @@ -11,3 +11,4 @@ VOID:UINT,POINTER,UINT VOID:UINT,UINT,POINTER,UINT BOOLEAN:UINT,POINTER,UINT BOOLEAN:UINT,UINT +VOID:OBJECT,OBJECT -- 1.7.5.1 _______________________________________________ Spice-devel mailing list Spice-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/spice-devel