[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