[gnome-color-manager] Add GcmUsb, a simple object to integrate libusb-1 with the GLib event loop
- From: Richard Hughes <rhughes src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-color-manager] Add GcmUsb, a simple object to integrate libusb-1 with the GLib event loop
- Date: Thu, 22 Jul 2010 11:43:14 +0000 (UTC)
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]