[gnome-color-manager] Add GcmUsb, a simple object to integrate libusb-1 with the GLib event loop



commit 9a08437910446bf133e95b5d461452ac85e7c52f
Author: Richard Hughes <richard hughsie com>
Date:   Wed Jul 21 14:48:51 2010 +0100

    Add GcmUsb, a simple object to integrate libusb-1 with the GLib event loop

 libcolor-glib/Makefile.am         |    2 +
 libcolor-glib/gcm-self-test.c     |   32 +++-
 libcolor-glib/gcm-usb.c           |  491 +++++++++++++++++++++++++++++++++++++
 libcolor-glib/gcm-usb.h           |   81 ++++++
 libcolor-glib/libcolor-glib.h     |    1 +
 libcolor-glib/libcolor-glib.pc.in |    2 +-
 6 files changed, 607 insertions(+), 2 deletions(-)
---
diff --git a/libcolor-glib/Makefile.am b/libcolor-glib/Makefile.am
index 34c32d8..e5dd770 100644
--- a/libcolor-glib/Makefile.am
+++ b/libcolor-glib/Makefile.am
@@ -90,6 +90,8 @@ libcolor_glib_la_SOURCES =					\
 	gcm-enum.h						\
 	gcm-xyz.c						\
 	gcm-xyz.h						\
+	gcm-usb.c						\
+	gcm-usb.h						\
 	gcm-profile-store.c					\
 	gcm-profile-store.h					\
 	gcm-xserver.c						\
diff --git a/libcolor-glib/gcm-self-test.c b/libcolor-glib/gcm-self-test.c
index c051b6b..107435d 100644
--- a/libcolor-glib/gcm-self-test.c
+++ b/libcolor-glib/gcm-self-test.c
@@ -37,6 +37,7 @@
 #include "gcm-xyz.h"
 #include "gcm-dmi.h"
 #include "gcm-image.h"
+#include "gcm-usb.h"
 
 static void
 gcm_test_common_func (void)
@@ -577,7 +578,6 @@ gcm_test_brightness_func (void)
 	g_object_unref (brightness);
 }
 
-
 static void
 gcm_test_image_func (void)
 {
@@ -642,6 +642,35 @@ gcm_test_image_func (void)
 	gtk_widget_destroy (dialog);
 }
 
+static void
+gcm_test_usb_func (void)
+{
+	GcmUsb *usb;
+	gboolean ret;
+	GError *error = NULL;
+
+	usb = gcm_usb_new ();
+	g_assert (usb != NULL);
+	g_assert (!gcm_usb_get_connected (usb));
+	g_assert (gcm_usb_get_device_handle (usb) == NULL);
+
+	/* try to load */
+	ret = gcm_usb_load (usb, &error);
+	g_assert_no_error (error);
+	g_assert (ret);
+
+	/* attach to the default mainloop */
+	gcm_usb_attach_to_context (usb, NULL);
+
+	/* connect to a non-existant device */
+	ret = gcm_usb_connect (usb, 0xffff, 0xffff, 0x1, 0x1, &error);
+	g_assert (!ret);
+	g_assert_error (error, GCM_USB_ERROR, GCM_USB_ERROR_INTERNAL);
+	g_error_free (error);
+
+	g_object_unref (usb);
+}
+
 int
 main (int argc, char **argv)
 {
@@ -661,6 +690,7 @@ main (int argc, char **argv)
 	g_test_add_func ("/libcolor-glib/xyz", gcm_test_xyz_func);
 	g_test_add_func ("/libcolor-glib/dmi", gcm_test_dmi_func);
 	g_test_add_func ("/libcolor-glib/profile_store", gcm_test_profile_store_func);
+	g_test_add_func ("/libcolor-glib/usb", gcm_test_usb_func);
 	if (g_test_thorough ()) {
 		g_test_add_func ("/libcolor-glib/brightness", gcm_test_brightness_func);
 		g_test_add_func ("/libcolor-glib/image", gcm_test_image_func);
diff --git a/libcolor-glib/gcm-usb.c b/libcolor-glib/gcm-usb.c
new file mode 100644
index 0000000..891ff0f
--- /dev/null
+++ b/libcolor-glib/gcm-usb.c
@@ -0,0 +1,491 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2010 Richard Hughes <richard 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
+ */
+
+/**
+ * SECTION:gcm-usb
+ * @short_description: GLib mainloop integration for libusb
+ *
+ * This object can be used to integrate libusb into the GLib event loop.
+ */
+
+#include "config.h"
+
+#include <glib-object.h>
+#include <sys/poll.h>
+#include <libusb-1.0/libusb.h>
+
+#include "gcm-usb.h"
+
+#include "egg-debug.h"
+
+static void     gcm_usb_finalize	(GObject     *object);
+
+#define GCM_USB_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GCM_TYPE_USB, GcmUsbPrivate))
+
+typedef struct {
+	GSource			 source;
+	GSList			*pollfds;
+} GcmUsbSource;
+
+/**
+ * GcmUsbPrivate:
+ *
+ * Private #GcmUsb data
+ **/
+struct _GcmUsbPrivate
+{
+	gboolean			 connected;
+	GcmUsbSource			*source;
+	libusb_device_handle		*handle;
+	libusb_context			*ctx;
+};
+
+enum {
+	PROP_0,
+	PROP_CONNECTED,
+	PROP_LAST
+};
+
+G_DEFINE_TYPE (GcmUsb, gcm_usb, G_TYPE_OBJECT)
+
+/**
+ * gcm_usb_get_connected:
+ **/
+gboolean
+gcm_usb_get_connected (GcmUsb *usb)
+{
+	g_return_val_if_fail (GCM_IS_USB (usb), FALSE);
+	return usb->priv->connected;
+}
+
+/**
+ * gcm_libusb_pollfd_add:
+ **/
+static void
+gcm_libusb_pollfd_add (GcmUsb *usb, int fd, short events)
+{
+	GcmUsbSource *source = usb->priv->source;
+	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);
+}
+
+/**
+ * gcm_libusb_pollfd_added_cb:
+ **/
+static void
+gcm_libusb_pollfd_added_cb (int fd, short events, void *user_data)
+{
+	GcmUsb *usb = user_data;
+	gcm_libusb_pollfd_add (usb, fd, events);
+}
+
+/**
+ * gcm_libusb_pollfd_removed_cb:
+ **/
+static void
+gcm_libusb_pollfd_removed_cb (int fd, void *user_data)
+{
+	GcmUsb *usb = user_data;
+	GcmUsbSource *source = usb->priv->source;
+	GPollFD *pollfd;
+	GSList *elem = source->pollfds;
+
+	egg_debug ("remove pollfd %i", fd);
+
+	/* nothing to see here, move along */
+	if (elem == NULL) {
+		egg_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)));
+	egg_warning ("couldn't find fd %d in list", fd);
+}
+
+/**
+ * gcm_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
+gcm_usb_source_prepare (GSource *source, gint *timeout)
+{
+	*timeout = -1;
+	return FALSE;
+}
+
+/**
+ * gcm_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
+gcm_usb_source_check (GSource *source)
+{
+	GcmUsbSource *gcm_source = (GcmUsbSource *) source;
+	GPollFD *pollfd;
+	GSList *elem = gcm_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;
+}
+
+/**
+ * gcm_usb_source_dispatch:
+ **/
+static gboolean
+gcm_usb_source_dispatch (GSource *source, GSourceFunc callback, gpointer user_data)
+{
+	gint r;
+	struct timeval tv = { 0, 0 };
+	GcmUsb *usb = user_data;
+	r = libusb_handle_events_timeout (usb->priv->ctx, &tv);
+	return TRUE;
+}
+
+/**
+ * gcm_usb_source_finalize:
+ *
+ * Called when the source is finalized.
+ **/
+static void
+gcm_usb_source_finalize (GSource *source)
+{
+	GPollFD *pollfd;
+	GcmUsbSource *gcm_source = (GcmUsbSource *) source;
+	GSList *elem = gcm_source->pollfds;
+
+	if (elem != NULL) {
+		do {
+			pollfd = elem->data;
+			g_source_remove_poll ((GSource *) gcm_source, pollfd);
+			g_slice_free (GPollFD, pollfd);
+			gcm_source->pollfds = g_slist_delete_link (gcm_source->pollfds, elem);
+		} while ((elem = g_slist_next (elem)));
+	}
+
+	g_slist_free (gcm_source->pollfds);
+}
+
+static GSourceFuncs gcm_usb_source_funcs = {
+	gcm_usb_source_prepare,
+	gcm_usb_source_check,
+	gcm_usb_source_dispatch,
+	gcm_usb_source_finalize,
+	NULL, NULL
+	};
+
+/**
+ * gcm_usb_attach_to_context:
+ * @usb:  a #GcmUsb instance
+ * @context: a #GMainContext or %NULL
+ *
+ * Connects up usb-1 with the GLib event loop. This functionality
+ * allows you to submit async requests using usb, and the callbacks
+ * just kinda happen at the right time.
+ *
+ * Return value: %TRUE for success
+ **/
+void
+gcm_usb_attach_to_context (GcmUsb *usb, GMainContext *context)
+{
+	guint i;
+	const struct libusb_pollfd **pollfds;
+	GcmUsbPrivate *priv = usb->priv;
+
+	/* already connected */
+	if (priv->source != NULL) {
+		egg_warning ("already attached to a context");
+		return;
+	}
+
+	/* create new GcmUsbSource */
+	priv->source = (GcmUsbSource *) g_source_new (&gcm_usb_source_funcs, sizeof(GcmUsbSource));
+	priv->source->pollfds = NULL;
+
+	/* assign user_data */
+	g_source_set_callback ((GSource *)priv->source, NULL, usb, NULL);
+
+	/* watch the fd's already created */
+	pollfds = libusb_get_pollfds (usb->priv->ctx);
+	for (i=0; pollfds[i] != NULL; i++)
+		gcm_libusb_pollfd_add (usb, pollfds[i]->fd, pollfds[i]->events);
+
+	/* watch for PollFD changes */
+	libusb_set_pollfd_notifiers (priv->ctx,
+				     gcm_libusb_pollfd_added_cb,
+				     gcm_libusb_pollfd_removed_cb,
+				     usb);
+
+	/* attach to the mainloop */
+	g_source_attach ((GSource *)priv->source, context);
+}
+
+/**
+ * gcm_usb_load:
+ * @usb:  a #GcmUsb instance
+ * @error:  a #GError, or %NULL
+ *
+ * Connects to libusb. You normally don't have to call this method manually.
+ *
+ * Return value: %TRUE for success
+ **/
+gboolean
+gcm_usb_load (GcmUsb *usb, GError **error)
+{
+	gboolean ret = FALSE;
+	gint retval;
+	GcmUsbPrivate *priv = usb->priv;
+
+	/* already done */
+	if (priv->ctx != NULL) {
+		ret = TRUE;
+		goto out;
+	}
+
+	/* init */
+	retval = libusb_init (&priv->ctx);
+	if (retval < 0) {
+		g_set_error (error, GCM_USB_ERROR,
+			     GCM_USB_ERROR_INTERNAL,
+			     "failed to init libusb: %s",
+			     libusb_strerror (retval));
+		goto out;
+	}
+
+	/* enable logging */
+	libusb_set_debug (priv->ctx, 3);
+
+	/* success */
+	ret = TRUE;
+	priv->connected = TRUE;
+out:
+	return ret;
+}
+
+/**
+ * gcm_usb_get_device_handle:
+ * @usb:  a #GcmUsb instance
+ *
+ * Gets the low-level device handle
+ *
+ * Return value: The #libusb_device_handle or %NULL. Do not unref this value.
+ **/
+libusb_device_handle *
+gcm_usb_get_device_handle (GcmUsb *usb)
+{
+	return usb->priv->handle;
+}
+
+/**
+ * gcm_usb_connect:
+ * @usb:  a #GcmUsb instance
+ * @error:  a #GError, or %NULL
+ *
+ * Connects to a specific device.
+ *
+ * Return value: %TRUE for success
+ **/
+gboolean
+gcm_usb_connect (GcmUsb *usb, guint vendor_id, guint product_id, guint configuration, guint interface, GError **error)
+{
+	gint retval;
+	gboolean ret = FALSE;
+	GcmUsbPrivate *priv = usb->priv;
+
+	/* already connected */
+	if (priv->handle != NULL) {
+		g_set_error_literal (error, GCM_USB_ERROR,
+				     GCM_USB_ERROR_INTERNAL,
+				     "already connected to a device");
+		goto out;
+	}
+
+	/* load libusb if we've not done this already */
+	ret = gcm_usb_load (usb, error);
+	if (!ret)
+		goto out;
+
+	/* open device */
+	priv->handle = libusb_open_device_with_vid_pid (priv->ctx, vendor_id, product_id);
+	if (priv->handle == NULL) {
+		g_set_error (error, GCM_USB_ERROR,
+			     GCM_USB_ERROR_INTERNAL,
+			     "failed to find device %04x:%04x",
+			     vendor_id, product_id);
+		ret = FALSE;
+		goto out;
+	}
+
+	/* set configuration and interface */
+	retval = libusb_set_configuration (priv->handle, configuration);
+	if (retval < 0) {
+		g_set_error (error, GCM_USB_ERROR,
+			     GCM_USB_ERROR_INTERNAL,
+			     "failed to set configuration 0x%02x: %s",
+			     configuration, libusb_strerror (retval));
+		ret = FALSE;
+		goto out;
+	}
+	retval = libusb_claim_interface (priv->handle, 0);
+	if (retval < 0) {
+		g_set_error (error, GCM_USB_ERROR,
+			     GCM_USB_ERROR_INTERNAL,
+			     "failed to claim interface 0x%02x: %s",
+			     interface, libusb_strerror (retval));
+		ret = FALSE;
+		goto out;
+	}
+out:
+	return ret;
+}
+
+/**
+ * gcm_usb_get_property:
+ **/
+static void
+gcm_usb_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
+{
+	GcmUsb *usb = GCM_USB (object);
+	GcmUsbPrivate *priv = usb->priv;
+
+	switch (prop_id) {
+	case PROP_CONNECTED:
+		g_value_set_boolean (value, priv->connected);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+/**
+ * gcm_usb_set_property:
+ **/
+static void
+gcm_usb_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
+{
+	switch (prop_id) {
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+/**
+ * gcm_usb_class_init:
+ **/
+static void
+gcm_usb_class_init (GcmUsbClass *klass)
+{
+	GParamSpec *pspec;
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	object_class->finalize = gcm_usb_finalize;
+	object_class->get_property = gcm_usb_get_property;
+	object_class->set_property = gcm_usb_set_property;
+
+	/**
+	 * GcmUsb:connected:
+	 */
+	pspec = g_param_spec_boolean ("connected", NULL, NULL,
+				      FALSE,
+				      G_PARAM_READWRITE);
+	g_object_class_install_property (object_class, PROP_CONNECTED, pspec);
+
+	g_type_class_add_private (klass, sizeof (GcmUsbPrivate));
+}
+
+/**
+ * gcm_usb_init:
+ **/
+static void
+gcm_usb_init (GcmUsb *usb)
+{
+	usb->priv = GCM_USB_GET_PRIVATE (usb);
+}
+
+/**
+ * gcm_usb_finalize:
+ **/
+static void
+gcm_usb_finalize (GObject *object)
+{
+	GcmUsb *usb = GCM_USB (object);
+	GcmUsbPrivate *priv = usb->priv;
+
+	if (priv->ctx != NULL) {
+		libusb_set_pollfd_notifiers (usb->priv->ctx, NULL, NULL, NULL);
+		libusb_exit (priv->ctx);
+	}
+	if (priv->handle != NULL)
+		libusb_close (priv->handle);
+
+	G_OBJECT_CLASS (gcm_usb_parent_class)->finalize (object);
+}
+
+/**
+ * gcm_usb_new:
+ *
+ * Return value: a new #GcmUsb object.
+ **/
+GcmUsb *
+gcm_usb_new (void)
+{
+	GcmUsb *usb;
+	usb = g_object_new (GCM_TYPE_USB, NULL);
+	return GCM_USB (usb);
+}
+
diff --git a/libcolor-glib/gcm-usb.h b/libcolor-glib/gcm-usb.h
new file mode 100644
index 0000000..3d3f33b
--- /dev/null
+++ b/libcolor-glib/gcm-usb.h
@@ -0,0 +1,81 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2010 Richard Hughes <richard 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 __GCM_USB_H
+#define __GCM_USB_H
+
+#include <glib-object.h>
+
+#include <libusb-1.0/libusb.h>
+
+G_BEGIN_DECLS
+
+#define GCM_TYPE_USB			(gcm_usb_get_type ())
+#define GCM_USB(o)			(G_TYPE_CHECK_INSTANCE_CAST ((o), GCM_TYPE_USB, GcmUsb))
+#define GCM_IS_USB(o)			(G_TYPE_CHECK_INSTANCE_TYPE ((o), GCM_TYPE_USB))
+
+typedef struct _GcmUsbPrivate		GcmUsbPrivate;
+typedef struct _GcmUsb			GcmUsb;
+typedef struct _GcmUsbClass		GcmUsbClass;
+
+/* dummy */
+#define GCM_USB_ERROR	1
+
+/**
+ * GcmSensorError:
+ *
+ * The error code.
+ **/
+typedef enum {
+	GCM_USB_ERROR_INTERNAL
+} GcmUsbError;
+
+struct _GcmUsb
+{
+	 GObject			 parent;
+	 GcmUsbPrivate			*priv;
+};
+
+struct _GcmUsbClass
+{
+	GObjectClass	parent_class;
+};
+
+GType			 gcm_usb_get_type			(void);
+gboolean		 gcm_usb_load				(GcmUsb		*usb,
+								 GError		**error);
+gboolean		 gcm_usb_connect			(GcmUsb		*usb,
+								 guint		 vendor_id,
+								 guint		 product_id,
+								 guint		 configuration,
+								 guint		 interface,
+								 GError		**error);
+gboolean		 gcm_usb_get_connected			(GcmUsb		*usb);
+
+void			 gcm_usb_attach_to_context		(GcmUsb		*usb,
+								 GMainContext	*context);
+libusb_device_handle	*gcm_usb_get_device_handle		(GcmUsb		*usb);
+GcmUsb			*gcm_usb_new				(void);
+
+G_END_DECLS
+
+#endif /* __GCM_USB_H */
+
diff --git a/libcolor-glib/libcolor-glib.h b/libcolor-glib/libcolor-glib.h
index d5e94f6..8cdeecb 100644
--- a/libcolor-glib/libcolor-glib.h
+++ b/libcolor-glib/libcolor-glib.h
@@ -46,6 +46,7 @@
 #include <gcm-xserver.h>
 #include <gcm-brightness.h>
 #include <gcm-profile-store.h>
+#include <gcm-usb.h>
 
 #undef __LIBCOLOR_GLIB_H_INSIDE__
 
diff --git a/libcolor-glib/libcolor-glib.pc.in b/libcolor-glib/libcolor-glib.pc.in
index 8e7ac7f..5cb68ce 100644
--- a/libcolor-glib/libcolor-glib.pc.in
+++ b/libcolor-glib/libcolor-glib.pc.in
@@ -7,6 +7,6 @@ Name: libcolor-glib
 Description: libcolor-glib is a userspace color library.
 Version: @VERSION@
 Requires.private: gthread-2.0
-Requires: glib-2.0, gobject-2.0, libusb-1.0 lcms2 gudev-1.0
+Requires: glib-2.0, gobject-2.0, libusb-1.0 lcms2 gudev-1.0 libusb-1.0
 Libs: -L${libdir} -llibcolor-glib
 Cflags: -I${includedir}/libcolor-glib



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]