[Spice-commits] 5 commits - configure.ac gtk/Makefile.am gtk/channel-usbredir-priv.h gtk/channel-usbredir.c gtk/spice-channel-priv.h gtk/spice-channel.c gtk/spicy.c gtk/usb-device-manager-priv.h gtk/usb-device-manager.c gtk/usb-device-manager.h gtk/usbutil.c gtk/usbutil.h

Hans de Goede jwrdegoede at kemper.freedesktop.org
Mon Feb 20 07:13:43 PST 2012


 configure.ac                  |   23 +++
 gtk/Makefile.am               |    3 
 gtk/channel-usbredir-priv.h   |   31 ++--
 gtk/channel-usbredir.c        |  144 ++++++++++++---------
 gtk/spice-channel-priv.h      |    1 
 gtk/spice-channel.c           |    9 +
 gtk/spicy.c                   |    9 +
 gtk/usb-device-manager-priv.h |    6 
 gtk/usb-device-manager.c      |  124 +++---------------
 gtk/usb-device-manager.h      |    2 
 gtk/usbutil.c                 |  285 ++++++++++++++++++++++++++++++++++++++++++
 gtk/usbutil.h                 |   39 +++++
 12 files changed, 495 insertions(+), 181 deletions(-)

New commits:
commit dfb66888fcc65a16bc6034921e2e3104fb055b76
Author: Hans de Goede <hdegoede at redhat.com>
Date:   Sat Feb 18 18:08:41 2012 +0100

    usbredir: Shrink the usb device selection dialog when devices are unplugged
    
    Signed-off-by: Hans de Goede <hdegoede at redhat.com>

diff --git a/gtk/spicy.c b/gtk/spicy.c
index cbc702e..2757bd7 100644
--- a/gtk/spicy.c
+++ b/gtk/spicy.c
@@ -419,6 +419,11 @@ static void menu_cb_remove_smartcard(GtkAction *action, void *data)
 #endif
 
 #ifdef USE_USBREDIR
+static void remove_cb(GtkContainer *container, GtkWidget *widget, void *data)
+{
+    gtk_window_resize(GTK_WINDOW(data), 1, 1);
+}
+
 static void menu_cb_select_usb_devices(GtkAction *action, void *data)
 {
     GtkWidget *dialog, *area, *usb_device_widget;
@@ -440,6 +445,10 @@ static void menu_cb_select_usb_devices(GtkAction *action, void *data)
                      G_CALLBACK(usb_connect_failed), NULL);
     gtk_box_pack_start(GTK_BOX(area), usb_device_widget, TRUE, TRUE, 5);
 
+    /* This shrinks the dialog when USB devices are unplugged */
+    g_signal_connect(usb_device_widget, "remove",
+                     G_CALLBACK(remove_cb), dialog);
+
     /* show and run */
     gtk_widget_show_all(dialog);
     gtk_dialog_run(GTK_DIALOG(dialog));
commit 37e679aa12b5d3dc40bc664e80b8323ff880959e
Author: Hans de Goede <hdegoede at redhat.com>
Date:   Thu Feb 16 16:04:30 2012 +0100

    usbredir: make channel lifetime equal to session lifetime

diff --git a/configure.ac b/configure.ac
index 5cd49bc..7876378 100644
--- a/configure.ac
+++ b/configure.ac
@@ -344,7 +344,7 @@ if test "x$enable_usbredir" = "xno"; then
   AM_CONDITIONAL(WITH_USBREDIR, false)
 else
   PKG_CHECK_MODULES([USBREDIR],
-                    [gudev-1.0 libusb-1.0 >= 1.0.9 libusbredirhost >= 0.3.3 libusbredirparser >= 0.3.3],
+                    [gudev-1.0 libusb-1.0 >= 1.0.9 libusbredirhost >= 0.3.4 libusbredirparser >= 0.3.4],
                     [have_usbredir=yes],
                     [have_usbredir=no])
   if test "x$have_usbredir" = "xno" && test "x$enable_usbredir" = "xyes"; then
diff --git a/gtk/channel-usbredir-priv.h b/gtk/channel-usbredir-priv.h
index 5d75e26..10dd479 100644
--- a/gtk/channel-usbredir-priv.h
+++ b/gtk/channel-usbredir-priv.h
@@ -26,17 +26,26 @@
 
 G_BEGIN_DECLS
 
-void spice_usbredir_channel_connect_async(SpiceUsbredirChannel *channel,
-                                          libusb_context       *context,
-                                          libusb_device        *device,
-                                          GCancellable         *cancellable,
-                                          GAsyncReadyCallback   callback,
-                                          gpointer              user_data);
-gboolean spice_usbredir_channel_connect_finish(SpiceUsbredirChannel *channel,
-                                               GAsyncResult         *res,
-                                               GError              **err);
-
-void spice_usbredir_channel_disconnect(SpiceUsbredirChannel *channel);
+/* Note: this must be called before calling any other functions, and the
+   context should not be destroyed before the last device has been
+   disconnected */
+void spice_usbredir_channel_set_context(SpiceUsbredirChannel *channel,
+                                        libusb_context       *context);
+
+/* Note the context must be set, and the channel must be brought up
+   (through spice_channel_connect()), before calling this. */
+void spice_usbredir_channel_connect_device_async(
+                                        SpiceUsbredirChannel *channel,
+                                        libusb_device        *device,
+                                        GCancellable         *cancellable,
+                                        GAsyncReadyCallback   callback,
+                                        gpointer              user_data);
+gboolean spice_usbredir_channel_connect_device_finish(
+                                        SpiceUsbredirChannel *channel,
+                                        GAsyncResult         *res,
+                                        GError              **err);
+
+void spice_usbredir_channel_disconnect_device(SpiceUsbredirChannel *channel);
 
 libusb_device *spice_usbredir_channel_get_device(SpiceUsbredirChannel *channel);
 
diff --git a/gtk/channel-usbredir.c b/gtk/channel-usbredir.c
index ac23256..e22aa6c 100644
--- a/gtk/channel-usbredir.c
+++ b/gtk/channel-usbredir.c
@@ -59,13 +59,11 @@ enum SpiceUsbredirChannelState {
 #if USE_POLKIT
     STATE_WAITING_FOR_ACL_HELPER,
 #endif
-    STATE_CONNECTING,
     STATE_CONNECTED,
     STATE_DISCONNECTING,
 };
 
 struct _SpiceUsbredirChannelPrivate {
-    libusb_context *context;
     libusb_device *device;
     struct usbredirhost *host;
     /* To catch usbredirhost error messages and report them as a GError */
@@ -83,6 +81,7 @@ struct _SpiceUsbredirChannelPrivate {
 static void spice_usbredir_handle_msg(SpiceChannel *channel, SpiceMsgIn *msg);
 static void spice_usbredir_channel_up(SpiceChannel *channel);
 static void spice_usbredir_channel_dispose(GObject *obj);
+static void spice_usbredir_channel_finalize(GObject *obj);
 static void usbredir_handle_msg(SpiceChannel *channel, SpiceMsgIn *in);
 
 static void usbredir_log(void *user_data, int level, const char *msg);
@@ -121,9 +120,10 @@ static void spice_usbredir_channel_class_init(SpiceUsbredirChannelClass *klass)
     GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
     SpiceChannelClass *channel_class = SPICE_CHANNEL_CLASS(klass);
 
-    gobject_class->dispose      = spice_usbredir_channel_dispose;
-    channel_class->handle_msg   = spice_usbredir_handle_msg;
-    channel_class->channel_up   = spice_usbredir_channel_up;
+    gobject_class->dispose       = spice_usbredir_channel_dispose;
+    gobject_class->finalize      = spice_usbredir_channel_finalize;
+    channel_class->handle_msg    = spice_usbredir_handle_msg;
+    channel_class->channel_up    = spice_usbredir_channel_up;
     channel_class->channel_reset = spice_usbredir_channel_reset;
 
     g_type_class_add_private(klass, sizeof(SpiceUsbredirChannelPrivate));
@@ -135,7 +135,7 @@ static void spice_usbredir_channel_dispose(GObject *obj)
 {
     SpiceUsbredirChannel *channel = SPICE_USBREDIR_CHANNEL(obj);
 
-    spice_usbredir_channel_disconnect(channel);
+    spice_usbredir_channel_disconnect_device(channel);
 
     /* Chain up to the parent class */
     if (G_OBJECT_CLASS(spice_usbredir_channel_parent_class)->dispose)
@@ -143,11 +143,11 @@ static void spice_usbredir_channel_dispose(GObject *obj)
 }
 
 /*
- * Note we don't have a finalize to unref our : device / context / acl_helper /
- * result references. The reason for this is that depending on our state they
- * are either:
+ * Note we don't unref our device / acl_helper / result references in our
+ * finalize. The reason for this is that depending on our state at dispose
+ * time they are either:
  * 1) Already unreferenced
- * 2) Will be unreferenced by the disconnect call from dispose
+ * 2) Will be unreferenced by the disconnect_device call from dispose
  * 3) Will be unreferenced by spice_usbredir_channel_open_acl_cb
  *
  * Now the last one may seem like an issue, since what will happen if
@@ -162,6 +162,17 @@ static void spice_usbredir_channel_dispose(GObject *obj)
  * spice_usbredir_channel_open_acl_cb has run, all references we hold have
  * been released even in the 3th scenario.
  */
+static void spice_usbredir_channel_finalize(GObject *obj)
+{
+    SpiceUsbredirChannel *channel = SPICE_USBREDIR_CHANNEL(obj);
+
+    if (channel->priv->host)
+        usbredirhost_close(channel->priv->host);
+
+    /* Chain up to the parent class */
+    if (G_OBJECT_CLASS(spice_usbredir_channel_parent_class)->finalize)
+        G_OBJECT_CLASS(spice_usbredir_channel_parent_class)->finalize(obj);
+}
 
 static const spice_msg_handler usbredir_handlers[] = {
     [ SPICE_MSG_SPICEVMC_DATA ] = usbredir_handle_msg,
@@ -170,12 +181,37 @@ static const spice_msg_handler usbredir_handlers[] = {
 /* ------------------------------------------------------------------ */
 /* private api                                                        */
 
+G_GNUC_INTERNAL
+void spice_usbredir_channel_set_context(SpiceUsbredirChannel *channel,
+                                        libusb_context       *context)
+{
+    SpiceUsbredirChannelPrivate *priv = channel->priv;
+
+    g_return_if_fail(priv->host == NULL);
+
+    priv->host = usbredirhost_open_full(
+                                   context, NULL,
+                                   usbredir_log,
+                                   usbredir_read_callback,
+                                   usbredir_write_callback,
+                                   usbredir_write_flush_callback,
+                                   usbredir_alloc_lock,
+                                   usbredir_lock_lock,
+                                   usbredir_unlock_lock,
+                                   usbredir_free_lock,
+                                   channel, PACKAGE_STRING,
+                                   spice_util_get_debug() ? usbredirparser_debug : usbredirparser_warning,
+                                   usbredirhost_fl_write_cb_owns_buffer);
+    if (!priv->host)
+        g_error("Out of memory allocating usbredirhost");
+}
+
 static gboolean spice_usbredir_channel_open_device(
     SpiceUsbredirChannel *channel, GError **err)
 {
     SpiceUsbredirChannelPrivate *priv = channel->priv;
     libusb_device_handle *handle = NULL;
-    int rc;
+    int rc, status;
 
     g_return_val_if_fail(priv->state == STATE_DISCONNECTED
 #if USE_POLKIT
@@ -192,21 +228,9 @@ static gboolean spice_usbredir_channel_open_device(
     }
 
     priv->catch_error = err;
-    priv->host = usbredirhost_open_full(
-                                   priv->context,
-                                   handle, usbredir_log,
-                                   usbredir_read_callback,
-                                   usbredir_write_callback,
-                                   usbredir_write_flush_callback,
-                                   usbredir_alloc_lock,
-                                   usbredir_lock_lock,
-                                   usbredir_unlock_lock,
-                                   usbredir_free_lock,
-                                   channel, PACKAGE_STRING,
-                                   spice_util_get_debug() ? usbredirparser_debug : usbredirparser_warning,
-                                   usbredirhost_fl_write_cb_owns_buffer);
+    status = usbredirhost_set_device(priv->host, handle);
     priv->catch_error = NULL;
-    if (!priv->host) {
+    if (status != usb_redir_success) {
         g_return_val_if_fail(err == NULL || *err != NULL, FALSE);
         return FALSE;
     }
@@ -215,13 +239,11 @@ static gboolean spice_usbredir_channel_open_device(
             spice_usb_device_manager_get(
                 spice_channel_get_session(SPICE_CHANNEL(channel)), NULL),
             err)) {
-        usbredirhost_close(priv->host);
-        priv->host = NULL;
+        usbredirhost_set_device(priv->host, NULL);
         return FALSE;
     }
 
-    spice_channel_connect(SPICE_CHANNEL(channel));
-    priv->state = STATE_CONNECTING;
+    priv->state = STATE_CONNECTED;
 
     return TRUE;
 }
@@ -250,9 +272,8 @@ static void spice_usbredir_channel_open_acl_cb(
     if (err) {
         g_simple_async_result_take_error(priv->result, err);
         libusb_unref_device(priv->device);
-        priv->device  = NULL;
-        priv->context = NULL;
-        priv->state = STATE_DISCONNECTED;
+        priv->device = NULL;
+        priv->state  = STATE_DISCONNECTED;
     }
 
     spice_usb_acl_helper_close_acl(priv->acl_helper);
@@ -266,8 +287,8 @@ static void spice_usbredir_channel_open_acl_cb(
 #endif
 
 G_GNUC_INTERNAL
-void spice_usbredir_channel_connect_async(SpiceUsbredirChannel *channel,
-                                          libusb_context       *context,
+void spice_usbredir_channel_connect_device_async(
+                                          SpiceUsbredirChannel *channel,
                                           libusb_device        *device,
                                           GCancellable         *cancellable,
                                           GAsyncReadyCallback   callback,
@@ -277,13 +298,19 @@ void spice_usbredir_channel_connect_async(SpiceUsbredirChannel *channel,
     GSimpleAsyncResult *result;
 
     g_return_if_fail(SPICE_IS_USBREDIR_CHANNEL(channel));
-    g_return_if_fail(context != NULL);
     g_return_if_fail(device != NULL);
 
     SPICE_DEBUG("connecting usb channel %p", channel);
 
     result = g_simple_async_result_new(G_OBJECT(channel), callback, user_data,
-                                       spice_usbredir_channel_connect_async);
+                                 spice_usbredir_channel_connect_device_async);
+
+    if (!priv->host) {
+        g_simple_async_result_set_error(result,
+                            SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
+                            "Error libusb context not set");
+        goto done;
+    }
 
     if (priv->state != STATE_DISCONNECTED) {
         g_simple_async_result_set_error(result,
@@ -292,11 +319,10 @@ void spice_usbredir_channel_connect_async(SpiceUsbredirChannel *channel,
         goto done;
     }
 
-    priv->context = context;
-    priv->device  = libusb_ref_device(device);
+    priv->device = libusb_ref_device(device);
 #if USE_POLKIT
     priv->result = result;
-    priv->state = STATE_WAITING_FOR_ACL_HELPER;
+    priv->state  = STATE_WAITING_FOR_ACL_HELPER;
     priv->acl_helper = spice_usb_acl_helper_new();
     g_object_set(spice_channel_get_session(SPICE_CHANNEL(channel)),
                  "inhibit-keyboard-grab", TRUE, NULL);
@@ -312,8 +338,7 @@ void spice_usbredir_channel_connect_async(SpiceUsbredirChannel *channel,
     if (!spice_usbredir_channel_open_device(channel, &err)) {
         g_simple_async_result_take_error(result, err);
         libusb_unref_device(priv->device);
-        priv->device  = NULL;
-        priv->context = NULL;
+        priv->device = NULL;
     }
 #endif
 
@@ -323,14 +348,15 @@ done:
 }
 
 G_GNUC_INTERNAL
-gboolean spice_usbredir_channel_connect_finish(SpiceUsbredirChannel *channel,
+gboolean spice_usbredir_channel_connect_device_finish(
+                                               SpiceUsbredirChannel *channel,
                                                GAsyncResult         *res,
                                                GError              **err)
 {
     GSimpleAsyncResult *result = G_SIMPLE_ASYNC_RESULT(res);
 
     g_return_val_if_fail(g_simple_async_result_is_valid(res, G_OBJECT(channel),
-                                     spice_usbredir_channel_connect_async),
+                                 spice_usbredir_channel_connect_device_async),
                          FALSE);
 
     if (g_simple_async_result_propagate_error(result, err))
@@ -340,11 +366,11 @@ gboolean spice_usbredir_channel_connect_finish(SpiceUsbredirChannel *channel,
 }
 
 G_GNUC_INTERNAL
-void spice_usbredir_channel_disconnect(SpiceUsbredirChannel *channel)
+void spice_usbredir_channel_disconnect_device(SpiceUsbredirChannel *channel)
 {
     SpiceUsbredirChannelPrivate *priv = channel->priv;
 
-    SPICE_DEBUG("disconnecting usb channel %p", channel);
+    SPICE_DEBUG("disconnecting device from usb channel %p", channel);
 
     switch (priv->state) {
     case STATE_DISCONNECTED:
@@ -357,24 +383,21 @@ void spice_usbredir_channel_disconnect(SpiceUsbredirChannel *channel)
         spice_usb_acl_helper_close_acl(priv->acl_helper);
         break;
 #endif
-    case STATE_CONNECTING:
     case STATE_CONNECTED:
-        spice_channel_disconnect(SPICE_CHANNEL(channel), SPICE_CHANNEL_NONE);
         /*
          * This sets the usb event thread run condition to FALSE, therefor
-         * it must be done before usbredirhost_close, as usbredirhost_close
-         * will interrupt the libusb_handle_events call in the thread.
+         * it must be done before usbredirhost_set_device NULL, as
+         * usbredirhost_set_device NULL will interrupt the
+         * libusb_handle_events call in the thread.
          */
         spice_usb_device_manager_stop_event_listening(
             spice_usb_device_manager_get(
                 spice_channel_get_session(SPICE_CHANNEL(channel)), NULL));
-        /* This also closes the libusb handle we passed to its _open */
-        usbredirhost_close(priv->host);
-        priv->host = NULL;
+        /* This also closes the libusb handle we passed from open_device */
+        usbredirhost_set_device(priv->host, NULL);
         libusb_unref_device(priv->device);
-        priv->device  = NULL;
-        priv->context = NULL;
-        priv->state = STATE_DISCONNECTED;
+        priv->device = NULL;
+        priv->state  = STATE_DISCONNECTED;
         break;
     }
 }
@@ -392,7 +415,8 @@ static void usbredir_write_flush_callback(void *user_data)
     SpiceUsbredirChannel *channel = SPICE_USBREDIR_CHANNEL(user_data);
     SpiceUsbredirChannelPrivate *priv = channel->priv;
 
-    if (priv->state != STATE_CONNECTED)
+    if (spice_channel_get_state(SPICE_CHANNEL(channel)) !=
+            SPICE_CHANNEL_STATE_READY)
         return;
 
     usbredirhost_write_guest_data(priv->host);
@@ -512,9 +536,6 @@ static void spice_usbredir_channel_up(SpiceChannel *c)
     SpiceUsbredirChannel *channel = SPICE_USBREDIR_CHANNEL(c);
     SpiceUsbredirChannelPrivate *priv = channel->priv;
 
-    g_return_if_fail(priv->state == STATE_CONNECTING);
-
-    priv->state = STATE_CONNECTED;
     /* Flush any pending writes */
     usbredirhost_write_guest_data(priv->host);
 }
diff --git a/gtk/spice-channel-priv.h b/gtk/spice-channel-priv.h
index 5cd7ddb..a6a36ed 100644
--- a/gtk/spice-channel-priv.h
+++ b/gtk/spice-channel-priv.h
@@ -157,6 +157,7 @@ void spice_channel_up(SpiceChannel *channel);
 void spice_channel_wakeup(SpiceChannel *channel, gboolean cancel);
 
 SpiceSession* spice_channel_get_session(SpiceChannel *channel);
+enum spice_channel_state spice_channel_get_state(SpiceChannel *channel);
 
 /* coroutine context */
 typedef void (*handler_msg_in)(SpiceChannel *channel, SpiceMsgIn *msg, gpointer data);
diff --git a/gtk/spice-channel.c b/gtk/spice-channel.c
index feeeff2..972a3bb 100644
--- a/gtk/spice-channel.c
+++ b/gtk/spice-channel.c
@@ -2549,6 +2549,15 @@ SpiceSession* spice_channel_get_session(SpiceChannel *channel)
 }
 
 G_GNUC_INTERNAL
+enum spice_channel_state spice_channel_get_state(SpiceChannel *channel)
+{
+    g_return_val_if_fail(SPICE_IS_CHANNEL(channel),
+                         SPICE_CHANNEL_STATE_UNCONNECTED);
+
+    return channel->priv->state;
+}
+
+G_GNUC_INTERNAL
 void spice_channel_swap(SpiceChannel *channel, SpiceChannel *swap)
 {
     SpiceChannelPrivate *c = SPICE_CHANNEL_GET_PRIVATE(channel);
diff --git a/gtk/usb-device-manager.c b/gtk/usb-device-manager.c
index 495a15b..b6dfa20 100644
--- a/gtk/usb-device-manager.c
+++ b/gtk/usb-device-manager.c
@@ -466,8 +466,14 @@ static void channel_new(SpiceSession *session, SpiceChannel *channel,
 {
     SpiceUsbDeviceManager *self = user_data;
 
-    if (SPICE_IS_USBREDIR_CHANNEL(channel))
+    if (SPICE_IS_USBREDIR_CHANNEL(channel)) {
+#ifdef USE_USBREDIR
+        spice_usbredir_channel_set_context(SPICE_USBREDIR_CHANNEL(channel),
+                                           self->priv->context);
+        spice_channel_connect(channel);
+#endif
         g_ptr_array_add(self->priv->channels, channel);
+    }
 }
 
 static void channel_destroy(SpiceSession *session, SpiceChannel *channel,
@@ -612,7 +618,7 @@ static void spice_usb_device_manager_channel_connect_cb(
     GSimpleAsyncResult *result = G_SIMPLE_ASYNC_RESULT(user_data);
     GError *err = NULL;
 
-    spice_usbredir_channel_connect_finish(channel, channel_res, &err);
+    spice_usbredir_channel_connect_device_finish(channel, channel_res, &err);
     if (err) {
         g_simple_async_result_take_error(result, err);
     }
@@ -815,8 +821,7 @@ void spice_usb_device_manager_connect_device_async(SpiceUsbDeviceManager *self,
         if (spice_usbredir_channel_get_device(channel))
             continue; /* Skip already used channels */
 
-        spice_usbredir_channel_connect_async(channel,
-                                 priv->context,
+        spice_usbredir_channel_connect_device_async(channel,
                                  (libusb_device *)device,
                                  cancellable,
                                  spice_usb_device_manager_channel_connect_cb,
@@ -870,7 +875,7 @@ void spice_usb_device_manager_disconnect_device(SpiceUsbDeviceManager *self,
 
     channel = spice_usb_device_manager_get_channel_for_dev(self, device);
     if (channel)
-        spice_usbredir_channel_disconnect(channel);
+        spice_usbredir_channel_disconnect_device(channel);
 #endif
 }
 
commit e41a11d1b77bcab1bc654e5b9294264bc7df9ba7
Author: Hans de Goede <hdegoede at redhat.com>
Date:   Thu Feb 9 13:03:29 2012 +0100

    usbutil: Add support for getting strings from usb.ids
    
    Unfortunately not all device makers go to the "trouble" of adding device
    identification strings to a device's descriptors. This patch makes spice-gtk
    translate vid/pid into strings if the device lacks the strings.
    
    Signed-off-by: Hans de Goede <hdegoede at redhat.com>

diff --git a/configure.ac b/configure.ac
index 28b3418..5cd49bc 100644
--- a/configure.ac
+++ b/configure.ac
@@ -426,6 +426,27 @@ AC_ARG_WITH([usb-acl-helper-dir],
   [ACL_HELPER_DIR="${bindir}/"])
 AC_SUBST([ACL_HELPER_DIR])
 
+AC_ARG_WITH([usb-ids-path],
+  AC_HELP_STRING([--with-usb-ids-path],
+                 [Specify the path to usb.ids @<:@default=auto@:>@]),
+  [USB_IDS="$with_usb_ids_path"],
+  [USB_IDS="auto"])
+AC_MSG_CHECKING([for usb.ids])
+if test "x$USB_IDS" = "xauto"; then
+  if test -n "$PKG_CONFIG"; then
+    USB_IDS=$($PKG_CONFIG --variable=usbids usbutils)
+  else
+    USB_IDS=
+  fi
+fi
+if test -n "$USB_IDS"; then
+  AC_MSG_RESULT([$USB_IDS])
+  AC_SUBST(USB_IDS)
+  AC_DEFINE(WITH_USBIDS, [1], [Define if compiling with usb.ids support])
+else
+  AC_MSG_RESULT([not found])
+fi
+
 AC_ARG_WITH([coroutine],
   AS_HELP_STRING([--with-coroutine=@<:@ucontext/gthread/winfiber/auto@:>@],
                  [use ucontext or GThread for coroutines @<:@default=auto@:>@]),
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index 2ad4494..fd2e477 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -60,6 +60,7 @@ SPICE_COMMON_CPPFLAGS = \
 	-DSW_CANVAS_CACHE		\
 	-DSPICE_GTK_LOCALEDIR=\"${SPICE_GTK_LOCALEDIR}\" \
 	-DPNP_IDS=\""$(PNP_IDS)"\"\
+	-DUSB_IDS=\""$(USB_IDS)"\"\
 	\
 	-I$(COMMON_DIR)			\
 	-I$(CLIENT_DIR)			\
diff --git a/gtk/usbutil.c b/gtk/usbutil.c
index 0d1361e..704f973 100644
--- a/gtk/usbutil.c
+++ b/gtk/usbutil.c
@@ -23,6 +23,8 @@
 
 #include <glib-object.h>
 #include <glib/gi18n.h>
+#include <ctype.h>
+#include <stdlib.h>
 
 #include "glib-compat.h"
 
@@ -35,6 +37,27 @@
 #endif
 #include "usbutil.h"
 
+#ifdef WITH_USBIDS
+#define VENDOR_NAME_LEN (122 - sizeof(void *))
+#define PRODUCT_NAME_LEN 126
+
+typedef struct _usb_product_info {
+    guint16 product_id;
+    char name[PRODUCT_NAME_LEN];
+} usb_product_info;
+
+typedef struct _usb_vendor_info {
+    usb_product_info *product_info;
+    int product_count;
+    guint16 vendor_id;
+    char name[VENDOR_NAME_LEN];
+} usb_vendor_info;
+
+GStaticMutex usbids_parse_mutex = G_STATIC_MUTEX_INIT;
+int usbids_vendor_count;
+usb_vendor_info *usbids_vendor_info;
+#endif
+
 G_GNUC_INTERNAL
 const char *spice_usbutil_libusb_strerror(enum libusb_error error_code)
 {
@@ -98,11 +121,112 @@ static gchar *spice_usbutil_get_sysfs_attribute(int bus, int address,
 }
 #endif
 
+#ifdef WITH_USBIDS
+static void spice_usbutil_parse_usbids(void)
+{
+    gchar *contents, *line, **lines;
+    usb_product_info *product_info;
+    int i, j, id, product_count = 0;
+
+    g_static_mutex_lock(&usbids_parse_mutex);
+    if (usbids_vendor_count)
+        goto leave;
+
+    if (!g_file_get_contents(USB_IDS, &contents, NULL, NULL)) {
+        usbids_vendor_count = -1;
+        goto leave;
+    }
+    lines = g_strsplit(contents, "\n", -1);
+
+    for (i = 0; lines[i]; i++) {
+        if (!isxdigit(lines[i][0]) || !isxdigit(lines[i][1]))
+            continue;
+
+        for (j = 1; lines[i + j] &&
+                   (lines[i + j][0] == '\t' ||
+                    lines[i + j][0] == '#'  ||
+                    lines[i + j][0] == '\0'); j++) {
+            if (lines[i + j][0] == '\t' && isxdigit(lines[i + j][1]))
+                product_count++;
+        }
+        i += j - 1;
+
+        usbids_vendor_count++;
+    }
+
+    usbids_vendor_info = g_new(usb_vendor_info, usbids_vendor_count);
+    product_info = g_new(usb_product_info, product_count);
+
+    usbids_vendor_count = 0;
+    for (i = 0; lines[i]; i++) {
+        line = lines[i];
+
+        if (!isxdigit(line[0]) || !isxdigit(line[1]))
+            continue;
+
+        id = strtoul(line, &line, 16);
+        while(isspace(line[0]))
+            line++;
+        usbids_vendor_info[usbids_vendor_count].vendor_id = id;
+        snprintf(usbids_vendor_info[usbids_vendor_count].name,
+                 VENDOR_NAME_LEN, "%s", line);
+
+        product_count = 0;
+        for (j = 1; lines[i + j] &&
+                   (lines[i + j][0] == '\t' ||
+                    lines[i + j][0] == '#'  ||
+                    lines[i + j][0] == '\0'); j++) {
+            line = lines[i + j];
+
+            if (line[0] != '\t' || !isxdigit(line[1]))
+                continue;
+
+            id = strtoul(line + 1, &line, 16);
+            while(isspace(line[0]))
+                line++;
+            product_info[product_count].product_id = id;
+            snprintf(product_info[product_count].name,
+                     PRODUCT_NAME_LEN, "%s", line);
+
+            product_count++;
+        }
+        i += j - 1;
+
+        usbids_vendor_info[usbids_vendor_count].product_count = product_count;
+        usbids_vendor_info[usbids_vendor_count].product_info  = product_info;
+        product_info += product_count;
+        usbids_vendor_count++;
+    }
+
+    g_strfreev(lines);
+    g_free(contents);
+
+#if 0 /* Testing only */
+    for (i = 0; i < usbids_vendor_count; i++) {
+        printf("%04x  %s\n", usbids_vendor_info[i].vendor_id,
+               usbids_vendor_info[i].name);
+        product_info = usbids_vendor_info[i].product_info;
+        for (j = 0; j < usbids_vendor_info[i].product_count; j++) {
+            printf("\t%04x  %s\n", product_info[j].product_id,
+                   product_info[j].name);
+        }
+    }
+#endif
+leave:
+    g_static_mutex_unlock(&usbids_parse_mutex);
+}
+#endif
+
 G_GNUC_INTERNAL
 void spice_usb_util_get_device_strings(int bus, int address,
                                        int vendor_id, int product_id,
                                        gchar **manufacturer, gchar **product)
 {
+#ifdef WITH_USBIDS
+    usb_product_info *product_info;
+    int i, j;
+#endif
+
     g_return_if_fail(manufacturer != NULL);
     g_return_if_fail(product != NULL);
 
@@ -113,6 +237,32 @@ void spice_usb_util_get_device_strings(int bus, int address,
     *manufacturer = spice_usbutil_get_sysfs_attribute(bus, address, "manufacturer");
     *product = spice_usbutil_get_sysfs_attribute(bus, address, "product");
 #endif
+
+#ifdef WITH_USBIDS
+    if (!*manufacturer || !*product) {
+        spice_usbutil_parse_usbids();
+
+        for (i = 0; i < usbids_vendor_count; i++) {
+            if ((int)usbids_vendor_info[i].vendor_id != vendor_id)
+                continue;
+
+            if (!*manufacturer && usbids_vendor_info[i].name[0])
+                *manufacturer = g_strdup(usbids_vendor_info[i].name);
+
+            product_info = usbids_vendor_info[i].product_info;
+            for (j = 0; j < usbids_vendor_info[i].product_count; j++) {
+                if ((int)product_info[j].product_id != product_id)
+                    continue;
+
+                if (!*product && product_info[j].name[0])
+                    *product = g_strdup(product_info[j].name);
+
+                break;
+            }
+            break;
+        }
+    }
+#endif
     if (!*manufacturer)
         *manufacturer = g_strdup(_("USB"));
     if (!*product)
commit 97648e4d0769f01800af21e43aeffee50aca503d
Author: Hans de Goede <hdegoede at redhat.com>
Date:   Thu Feb 9 12:49:57 2012 +0100

    usbutil: Add a spice_usb_util_get_device_strings helper function
    
    Signed-off-by: Hans de Goede <hdegoede at redhat.com>

diff --git a/gtk/usb-device-manager.c b/gtk/usb-device-manager.c
index a20c18c..495a15b 100644
--- a/gtk/usb-device-manager.c
+++ b/gtk/usb-device-manager.c
@@ -907,32 +907,16 @@ gchar *spice_usb_device_get_description(SpiceUsbDevice *_device, const gchar *fo
     bus     = libusb_get_bus_number(device);
     address = libusb_get_device_address(device);
 
-#if __linux__
-    manufacturer = spice_usbutil_get_sysfs_attribute(bus, address, "manufacturer");
-    product = spice_usbutil_get_sysfs_attribute(bus, address, "product");
-#endif
-    if (!manufacturer)
-        manufacturer = g_strdup(_("USB"));
-    if (!product)
-        product = g_strdup(_("Device"));
-
-    /* Some devices have unwanted whitespace in their strings */
-    g_strstrip(manufacturer);
-    g_strstrip(product);
-
-    /* Some devices repeat the manufacturer at the beginning of product */
-    if (g_str_has_prefix(product, manufacturer) &&
-            strlen(product) > strlen(manufacturer)) {
-        gchar *tmp = g_strdup(product + strlen(manufacturer));
-        g_free(product);
-        product = tmp;
-        g_strstrip(product);
-    }
-
-    if (libusb_get_device_descriptor(device, &desc) == LIBUSB_SUCCESS)
+    if (libusb_get_device_descriptor(device, &desc) == LIBUSB_SUCCESS) {
+        spice_usb_util_get_device_strings(bus, address,
+                                          desc.idVendor, desc.idProduct,
+                                          &manufacturer, &product);
         descriptor = g_strdup_printf("[%04x:%04x]", desc.idVendor, desc.idProduct);
-    else
+    } else {
+        spice_usb_util_get_device_strings(bus, address, -1, -1,
+                                          &manufacturer, &product);
         descriptor = g_strdup("");
+    }
 
     if (!format)
         format = _("%s %s %s at %d-%d");
diff --git a/gtk/usbutil.c b/gtk/usbutil.c
index 4f9502d..0d1361e 100644
--- a/gtk/usbutil.c
+++ b/gtk/usbutil.c
@@ -74,7 +74,8 @@ const char *spice_usbutil_libusb_strerror(enum libusb_error error_code)
 #ifdef __linux__
 /* <Sigh> libusb does not allow getting the manufacturer and product strings
    without opening the device, so grab them directly from sysfs */
-gchar *spice_usbutil_get_sysfs_attribute(int bus, int address, const char *attribute)
+static gchar *spice_usbutil_get_sysfs_attribute(int bus, int address,
+                                                const char *attribute)
 {
     struct stat stat_buf;
     char filename[256];
@@ -96,4 +97,39 @@ gchar *spice_usbutil_get_sysfs_attribute(int bus, int address, const char *attri
     return contents;
 }
 #endif
+
+G_GNUC_INTERNAL
+void spice_usb_util_get_device_strings(int bus, int address,
+                                       int vendor_id, int product_id,
+                                       gchar **manufacturer, gchar **product)
+{
+    g_return_if_fail(manufacturer != NULL);
+    g_return_if_fail(product != NULL);
+
+    *manufacturer = NULL;
+    *product = NULL;
+
+#if __linux__
+    *manufacturer = spice_usbutil_get_sysfs_attribute(bus, address, "manufacturer");
+    *product = spice_usbutil_get_sysfs_attribute(bus, address, "product");
+#endif
+    if (!*manufacturer)
+        *manufacturer = g_strdup(_("USB"));
+    if (!*product)
+        *product = g_strdup(_("Device"));
+
+    /* Some devices have unwanted whitespace in their strings */
+    g_strstrip(*manufacturer);
+    g_strstrip(*product);
+
+    /* Some devices repeat the manufacturer at the beginning of product */
+    if (g_str_has_prefix(*product, *manufacturer) &&
+            strlen(*product) > strlen(*manufacturer)) {
+        gchar *tmp = g_strdup(*product + strlen(*manufacturer));
+        g_free(*product);
+        *product = tmp;
+        g_strstrip(*product);
+    }
+}
+
 #endif
diff --git a/gtk/usbutil.h b/gtk/usbutil.h
index a74f1aa..de5e92a 100644
--- a/gtk/usbutil.h
+++ b/gtk/usbutil.h
@@ -21,15 +21,17 @@
 #ifndef __SPICE_USBUTIL_H__
 #define __SPICE_USBUTIL_H__
 
+#include <glib.h>
+
 #ifdef USE_USBREDIR
 #include <libusb.h>
 
 G_BEGIN_DECLS
 
 const char *spice_usbutil_libusb_strerror(enum libusb_error error_code);
-#ifdef __linux__
-gchar *spice_usbutil_get_sysfs_attribute(int bus, int address, const char *attribute);
-#endif
+void spice_usb_util_get_device_strings(int bus, int address,
+                                       int vendor_id, int product_id,
+                                       gchar **manufacturer, gchar **product);
 
 G_END_DECLS
 
commit 05a1bf60e9805300c1ec54a4c3ea9aebf5d2f488
Author: Hans de Goede <hdegoede at redhat.com>
Date:   Tue Feb 7 15:51:52 2012 +0100

    usb: Move various helper functions into usbutil.[c,h]
    
    Signed-off-by: Hans de Goede <hdegoede at redhat.com>

diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index 788a145..2ad4494 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -224,6 +224,8 @@ libspice_client_glib_2_0_la_SOURCES =	\
 	smartcard-manager-priv.h	\
 	usb-device-manager.c		\
 	usb-device-manager-priv.h	\
+	usbutil.c			\
+	usbutil.h			\
 	$(USB_ACL_HELPER_SRCS)		\
 	\
 	decode.h			\
diff --git a/gtk/channel-usbredir.c b/gtk/channel-usbredir.c
index 10d266c..ac23256 100644
--- a/gtk/channel-usbredir.c
+++ b/gtk/channel-usbredir.c
@@ -1,6 +1,6 @@
 /* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
 /*
-   Copyright 2010-2011 Red Hat, Inc.
+   Copyright 2010-2012 Red Hat, Inc.
 
    Red Hat Authors:
    Hans de Goede <hdegoede at redhat.com>
@@ -28,6 +28,7 @@
 #endif
 #include "channel-usbredir-priv.h"
 #include "usb-device-manager-priv.h"
+#include "usbutil.h"
 #endif
 
 #include "spice-client.h"
@@ -186,7 +187,7 @@ static gboolean spice_usbredir_channel_open_device(
     if (rc != 0) {
         g_set_error(err, SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
                     "Could not open usb device: %s [%i]",
-                    spice_usb_device_manager_libusb_strerror(rc), rc);
+                    spice_usbutil_libusb_strerror(rc), rc);
         return FALSE;
     }
 
diff --git a/gtk/usb-device-manager-priv.h b/gtk/usb-device-manager-priv.h
index 7e1aea3..b0b2f74 100644
--- a/gtk/usb-device-manager-priv.h
+++ b/gtk/usb-device-manager-priv.h
@@ -1,6 +1,6 @@
 /* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
 /*
-   Copyright (C) 2011 Red Hat, Inc.
+   Copyright (C) 2011,2012 Red Hat, Inc.
 
    Red Hat Authors:
    Hans de Goede <hdegoede at redhat.com>
@@ -25,10 +25,6 @@
 
 G_BEGIN_DECLS
 
-#ifdef USE_USBREDIR
-const char *spice_usb_device_manager_libusb_strerror(enum libusb_error error_code);
-#endif
-
 gboolean spice_usb_device_manager_start_event_listening(
     SpiceUsbDeviceManager *manager, GError **err);
 
diff --git a/gtk/usb-device-manager.c b/gtk/usb-device-manager.c
index c5062b7..a20c18c 100644
--- a/gtk/usb-device-manager.c
+++ b/gtk/usb-device-manager.c
@@ -1,6 +1,6 @@
 /* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
 /*
-   Copyright (C) 2011 Red Hat, Inc.
+   Copyright (C) 2011, 2012 Red Hat, Inc.
 
    Red Hat Authors:
    Hans de Goede <hdegoede at redhat.com>
@@ -27,17 +27,12 @@
 #include "glib-compat.h"
 
 #ifdef USE_USBREDIR
-#ifdef __linux__
-#include <stdio.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#endif
 #include <errno.h>
 #include <libusb.h>
 #include <gudev/gudev.h>
 #include "channel-usbredir-priv.h"
 #include "usbredirhost.h"
+#include "usbutil.h"
 #endif
 
 #include "spice-session-priv.h"
@@ -191,7 +186,7 @@ static gboolean spice_usb_device_manager_initable_init(GInitable  *initable,
 #ifdef USE_USBREDIR
     rc = libusb_init(&priv->context);
     if (rc < 0) {
-        const char *desc = spice_usb_device_manager_libusb_strerror(rc);
+        const char *desc = spice_usbutil_libusb_strerror(rc);
         g_warning("Error initializing USB support: %s [%i]", desc, rc);
         g_set_error(err, SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
                     "Error initializing USB support: %s [%i]", desc, rc);
@@ -461,68 +456,6 @@ static gboolean spice_usb_device_manager_get_udev_bus_n_address(
 
     return *bus && *address;
 }
-
-const char *spice_usb_device_manager_libusb_strerror(enum libusb_error error_code)
-{
-    switch (error_code) {
-    case LIBUSB_SUCCESS:
-        return "Success";
-    case LIBUSB_ERROR_IO:
-        return "Input/output error";
-    case LIBUSB_ERROR_INVALID_PARAM:
-        return "Invalid parameter";
-    case LIBUSB_ERROR_ACCESS:
-        return "Access denied (insufficient permissions)";
-    case LIBUSB_ERROR_NO_DEVICE:
-        return "No such device (it may have been disconnected)";
-    case LIBUSB_ERROR_NOT_FOUND:
-        return "Entity not found";
-    case LIBUSB_ERROR_BUSY:
-        return "Resource busy";
-    case LIBUSB_ERROR_TIMEOUT:
-        return "Operation timed out";
-    case LIBUSB_ERROR_OVERFLOW:
-        return "Overflow";
-    case LIBUSB_ERROR_PIPE:
-        return "Pipe error";
-    case LIBUSB_ERROR_INTERRUPTED:
-        return "System call interrupted (perhaps due to signal)";
-    case LIBUSB_ERROR_NO_MEM:
-        return "Insufficient memory";
-    case LIBUSB_ERROR_NOT_SUPPORTED:
-        return "Operation not supported or unimplemented on this platform";
-    case LIBUSB_ERROR_OTHER:
-        return "Other error";
-    }
-    return "Unknown error";
-}
-
-#ifdef __linux__
-/* <Sigh> libusb does not allow getting the manufacturer and product strings
-   without opening the device, so grab them directly from sysfs */
-static gchar *spice_usb_device_manager_get_sysfs_attribute(
-    int bus, int address, const char *attribute)
-{
-    struct stat stat_buf;
-    char filename[256];
-    gchar *contents;
-
-    snprintf(filename, sizeof(filename), "/dev/bus/usb/%03d/%03d",
-             bus, address);
-    if (stat(filename, &stat_buf) != 0)
-        return NULL;
-
-    snprintf(filename, sizeof(filename), "/sys/dev/char/%d:%d/%s",
-             major(stat_buf.st_rdev), minor(stat_buf.st_rdev), attribute);
-    if (!g_file_get_contents(filename, &contents, NULL, NULL))
-        return NULL;
-
-    /* Remove the newline at the end */
-    contents[strlen(contents) - 1] = '\0';
-
-    return contents;
-}
-#endif
 #endif
 
 /* ------------------------------------------------------------------ */
@@ -699,7 +632,7 @@ static gpointer spice_usb_device_manager_usb_ev_thread(gpointer user_data)
     while (priv->event_thread_run) {
         rc = libusb_handle_events(priv->context);
         if (rc) {
-            const char *desc = spice_usb_device_manager_libusb_strerror(rc);
+            const char *desc = spice_usbutil_libusb_strerror(rc);
             g_warning("Error handling USB events: %s [%i]", desc, rc);
         }
     }
@@ -975,10 +908,8 @@ gchar *spice_usb_device_get_description(SpiceUsbDevice *_device, const gchar *fo
     address = libusb_get_device_address(device);
 
 #if __linux__
-    manufacturer = spice_usb_device_manager_get_sysfs_attribute(bus, address,
-                                                              "manufacturer");
-    product = spice_usb_device_manager_get_sysfs_attribute(bus, address,
-                                                           "product");
+    manufacturer = spice_usbutil_get_sysfs_attribute(bus, address, "manufacturer");
+    product = spice_usbutil_get_sysfs_attribute(bus, address, "product");
 #endif
     if (!manufacturer)
         manufacturer = g_strdup(_("USB"));
diff --git a/gtk/usb-device-manager.h b/gtk/usb-device-manager.h
index d01bc12..ec1a896 100644
--- a/gtk/usb-device-manager.h
+++ b/gtk/usb-device-manager.h
@@ -1,6 +1,6 @@
 /* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
 /*
-   Copyright (C) 2011 Red Hat, Inc.
+   Copyright (C) 2011, 2012 Red Hat, Inc.
 
    Red Hat Authors:
    Hans de Goede <hdegoede at redhat.com>
diff --git a/gtk/usbutil.c b/gtk/usbutil.c
new file mode 100644
index 0000000..4f9502d
--- /dev/null
+++ b/gtk/usbutil.c
@@ -0,0 +1,99 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+   Copyright (C) 2012 Red Hat, Inc.
+
+   Red Hat Authors:
+   Hans de Goede <hdegoede at redhat.com>
+
+   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 "config.h"
+
+#include <glib-object.h>
+#include <glib/gi18n.h>
+
+#include "glib-compat.h"
+
+#ifdef USE_USBREDIR
+#ifdef __linux__
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#endif
+#include "usbutil.h"
+
+G_GNUC_INTERNAL
+const char *spice_usbutil_libusb_strerror(enum libusb_error error_code)
+{
+    switch (error_code) {
+    case LIBUSB_SUCCESS:
+        return "Success";
+    case LIBUSB_ERROR_IO:
+        return "Input/output error";
+    case LIBUSB_ERROR_INVALID_PARAM:
+        return "Invalid parameter";
+    case LIBUSB_ERROR_ACCESS:
+        return "Access denied (insufficient permissions)";
+    case LIBUSB_ERROR_NO_DEVICE:
+        return "No such device (it may have been disconnected)";
+    case LIBUSB_ERROR_NOT_FOUND:
+        return "Entity not found";
+    case LIBUSB_ERROR_BUSY:
+        return "Resource busy";
+    case LIBUSB_ERROR_TIMEOUT:
+        return "Operation timed out";
+    case LIBUSB_ERROR_OVERFLOW:
+        return "Overflow";
+    case LIBUSB_ERROR_PIPE:
+        return "Pipe error";
+    case LIBUSB_ERROR_INTERRUPTED:
+        return "System call interrupted (perhaps due to signal)";
+    case LIBUSB_ERROR_NO_MEM:
+        return "Insufficient memory";
+    case LIBUSB_ERROR_NOT_SUPPORTED:
+        return "Operation not supported or unimplemented on this platform";
+    case LIBUSB_ERROR_OTHER:
+        return "Other error";
+    }
+    return "Unknown error";
+}
+
+#ifdef __linux__
+/* <Sigh> libusb does not allow getting the manufacturer and product strings
+   without opening the device, so grab them directly from sysfs */
+gchar *spice_usbutil_get_sysfs_attribute(int bus, int address, const char *attribute)
+{
+    struct stat stat_buf;
+    char filename[256];
+    gchar *contents;
+
+    snprintf(filename, sizeof(filename), "/dev/bus/usb/%03d/%03d",
+             bus, address);
+    if (stat(filename, &stat_buf) != 0)
+        return NULL;
+
+    snprintf(filename, sizeof(filename), "/sys/dev/char/%d:%d/%s",
+             major(stat_buf.st_rdev), minor(stat_buf.st_rdev), attribute);
+    if (!g_file_get_contents(filename, &contents, NULL, NULL))
+        return NULL;
+
+    /* Remove the newline at the end */
+    contents[strlen(contents) - 1] = '\0';
+
+    return contents;
+}
+#endif
+#endif
diff --git a/gtk/usbutil.h b/gtk/usbutil.h
new file mode 100644
index 0000000..a74f1aa
--- /dev/null
+++ b/gtk/usbutil.h
@@ -0,0 +1,37 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+   Copyright (C) 2012 Red Hat, Inc.
+
+   Red Hat Authors:
+   Hans de Goede <hdegoede at redhat.com>
+
+   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 __SPICE_USBUTIL_H__
+#define __SPICE_USBUTIL_H__
+
+#ifdef USE_USBREDIR
+#include <libusb.h>
+
+G_BEGIN_DECLS
+
+const char *spice_usbutil_libusb_strerror(enum libusb_error error_code);
+#ifdef __linux__
+gchar *spice_usbutil_get_sysfs_attribute(int bus, int address, const char *attribute);
+#endif
+
+G_END_DECLS
+
+#endif /* USE_USBREDIR */
+#endif /* __SPICE_USBUTIL_H__ */


More information about the Spice-commits mailing list