[Spice-devel] [PATCH spice-gtk 4/6] Add an usbredir channel
Hans de Goede
hdegoede at redhat.com
Fri Aug 26 01:42:57 PDT 2011
Signed-off-by: Hans de Goede <hdegoede at redhat.com>
---
configure.ac | 1 +
gtk/Makefile.am | 6 +
gtk/channel-usbredir-priv.h | 43 ++++++
gtk/channel-usbredir.c | 353 +++++++++++++++++++++++++++++++++++++++++++
gtk/channel-usbredir.h | 60 ++++++++
gtk/map-file | 1 +
gtk/spice-channel.c | 15 ++-
gtk/spice-client-gtk.defs | 16 ++
gtk/spice-client.h | 1 +
gtk/spice-option.c | 5 +
gtk/spice-session.c | 29 ++++
11 files changed, 529 insertions(+), 1 deletions(-)
create mode 100644 gtk/channel-usbredir-priv.h
create mode 100644 gtk/channel-usbredir.c
create mode 100644 gtk/channel-usbredir.h
diff --git a/configure.ac b/configure.ac
index 9fa2b20..98b91de 100644
--- a/configure.ac
+++ b/configure.ac
@@ -321,6 +321,7 @@ if test "x$enable_usbredir" = "xno"; then
else
PKG_CHECK_MODULES(GUDEV, gudev-1.0)
PKG_CHECK_MODULES(LIBUSB, libusb-1.0 >= 1.0.9)
+ PKG_CHECK_MODULES(LIBUSBREDIRHOST, libusbredirhost >= 0.3.1)
AC_DEFINE(USE_USBREDIR, [1], [Define if supporting usbredir proxying])
AM_CONDITIONAL(WITH_USBREDIR, true)
fi
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index 1b841e2..f11576c 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -74,6 +74,7 @@ SPICE_COMMON_CPPFLAGS = \
$(SMARTCARD_CFLAGS) \
$(GUDEV_CFLAGS) \
$(LIBUSB_CFLAGS) \
+ $(LIBUSBREDIRHOST_CFLAGS) \
$(NULL)
AM_CPPFLAGS = \
@@ -154,6 +155,7 @@ libspice_client_glib_2_0_la_LIBADD = \
$(SMARTCARD_LIBS) \
$(GUDEV_LIBS) \
$(LIBUSB_LIBS) \
+ $(LIBUSBREDIRHOST_LIBS) \
$(NULL)
if WITH_USBREDIR
@@ -208,6 +210,8 @@ libspice_client_glib_2_0_la_SOURCES = \
channel-playback.c \
channel-record.c \
channel-smartcard.c \
+ channel-usbredir.c \
+ channel-usbredir-priv.h \
smartcard-manager.c \
smartcard-manager.h \
smartcard-manager-priv.h \
@@ -259,6 +263,7 @@ libspice_client_glibinclude_HEADERS = \
channel-playback.h \
channel-record.h \
channel-smartcard.h \
+ channel-usbredir.h \
$(NULL)
# file for API compatibility, but we don't want warning during our compilation
@@ -518,6 +523,7 @@ glib_introspection_files = \
channel-playback.c \
channel-record.c \
channel-smartcard.c \
+ channel-usbredir.c \
$(NULL)
gtk_introspection_files = \
diff --git a/gtk/channel-usbredir-priv.h b/gtk/channel-usbredir-priv.h
new file mode 100644
index 0000000..05988e1
--- /dev/null
+++ b/gtk/channel-usbredir-priv.h
@@ -0,0 +1,43 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ Copyright (C) 2011 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_CLIENT_USBREDIR_CHANNEL_PRIV_H__
+#define __SPICE_CLIENT_USBREDIR_CHANNEL_PRIV_H__
+
+#include <gusb/gusb-context.h>
+#include <gusb/gusb-device.h>
+
+#include "spice-client.h"
+
+G_BEGIN_DECLS
+
+gboolean spice_usbredir_channel_connect(SpiceUsbredirChannel *channel,
+ GUsbContext *context,
+ GUsbDevice *device,
+ GError **err);
+void spice_usbredir_channel_disconnect(SpiceUsbredirChannel *channel);
+
+GUsbDevice *spice_usbredir_channel_get_device(SpiceUsbredirChannel *channel);
+
+void spice_usbredir_channel_do_write(SpiceUsbredirChannel *channel);
+
+G_END_DECLS
+
+#endif /* __SPICE_CLIENT_USBREDIR_CHANNEL_PRIV_H__ */
diff --git a/gtk/channel-usbredir.c b/gtk/channel-usbredir.c
new file mode 100644
index 0000000..2baca5f
--- /dev/null
+++ b/gtk/channel-usbredir.c
@@ -0,0 +1,353 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ Copyright 2010-2011 Red Hat, Inc.
+
+ Red Hat Authors:
+ Hans de Goede <hdegoede at redhat.com>
+ Richard Hughes <rhughes 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"
+
+#ifdef USE_USBREDIR
+#include <usbredirhost.h>
+#include <gusb/gusb-context-private.h>
+#include <gusb/gusb-device-private.h>
+#include "channel-usbredir-priv.h"
+#endif
+
+#include "spice-client.h"
+#include "spice-common.h"
+
+#include "spice-channel-priv.h"
+
+/* libusb_strerror is awaiting merging upstream */
+#define libusb_strerror(error) "unknown"
+
+/**
+ * SECTION:channel-usbredir
+ * @short_description: usb redirection
+ * @title: USB Redirection Channel
+ * @section_id:
+ * @stability: API Stable (channel in development)
+ * @include: channel-usbredir.h
+ *
+ * The Spice protocol defines a set of messages to redirect USB devices
+ * from the Spice client to the VM. This channel handles these messages.
+ */
+
+#define SPICE_USBREDIR_CHANNEL_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE((obj), SPICE_TYPE_USBREDIR_CHANNEL, SpiceUsbredirChannelPrivate))
+
+struct _SpiceUsbredirChannelPrivate {
+ int verbose;
+#ifdef USE_USBREDIR
+ GUsbContext *context;
+ GUsbDevice *device;
+ struct usbredirhost *host;
+ /* To catch usbredirhost error messages and report them as a GError */
+ GError **catch_error;
+ /* Data passed from channel handle msg to the usbredirhost read cb */
+ const uint8_t *read_buf;
+ int read_buf_size;
+ SpiceMsgOut *msg_out;
+ gboolean up;
+#endif
+};
+
+G_DEFINE_TYPE(SpiceUsbredirChannel, spice_usbredir_channel, SPICE_TYPE_CHANNEL)
+
+enum {
+
+ SPICE_USBREDIR_LAST_SIGNAL,
+};
+
+static void spice_usbredir_handle_msg(SpiceChannel *channel, SpiceMsgIn *msg);
+static void spice_usbredir_channel_up(SpiceChannel *channel);
+static void usbredir_handle_msg(SpiceChannel *channel, SpiceMsgIn *in);
+
+#ifdef USE_USBREDIR
+static void usbredir_log(void *user_data, int level, const char *msg);
+static int usbredir_read_callback(void *user_data, uint8_t *data, int count);
+static int usbredir_write_callback(void *user_data, uint8_t *data, int count);
+#endif
+
+/* ------------------------------------------------------------------ */
+
+static void spice_usbredir_channel_init(SpiceUsbredirChannel *channel)
+{
+ SpiceUsbredirChannelPrivate *priv;
+
+ channel->priv = SPICE_USBREDIR_CHANNEL_GET_PRIVATE(channel);
+ priv = channel->priv;
+
+ memset(priv, 0, sizeof(SpiceUsbredirChannelPrivate));
+#ifdef USE_USBREDIR
+ if (spice_util_get_debug()) {
+ priv->verbose = usbredirparser_debug;
+ } else {
+ priv->verbose = usbredirparser_warning;
+ }
+#endif
+}
+
+static void spice_usbredir_channel_finalize(GObject *obj)
+{
+#ifdef USE_USBREDIR
+ SpiceUsbredirChannel *channel = SPICE_USBREDIR_CHANNEL(obj);
+
+ spice_usbredir_channel_disconnect(channel);
+#endif
+
+ if (G_OBJECT_CLASS(spice_usbredir_channel_parent_class)->finalize)
+ G_OBJECT_CLASS(spice_usbredir_channel_parent_class)->finalize(obj);
+}
+
+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->finalize = spice_usbredir_channel_finalize;
+ channel_class->handle_msg = spice_usbredir_handle_msg;
+ channel_class->channel_up = spice_usbredir_channel_up;
+
+ g_type_class_add_private(klass, sizeof(SpiceUsbredirChannelPrivate));
+}
+
+static const spice_msg_handler usbredir_handlers[] = {
+ [ SPICE_MSG_SPICEVMC_DATA ] = usbredir_handle_msg,
+};
+
+GQuark spice_usbredir_channel_error_quark(void)
+{
+ static GQuark quark = 0;
+ if (!quark)
+ quark = g_quark_from_static_string ("spice-usbredir-channel-error-quark");
+ return quark;
+}
+
+#ifdef USE_USBREDIR
+
+/* ------------------------------------------------------------------ */
+/* private api */
+
+gboolean spice_usbredir_channel_connect(SpiceUsbredirChannel *channel,
+ GUsbContext *context,
+ GUsbDevice *device,
+ GError **err)
+{
+ SpiceUsbredirChannelPrivate *priv = channel->priv;
+ libusb_device_handle *handle = NULL;
+ int rc;
+
+ g_return_val_if_fail (err == NULL || *err == NULL, FALSE);
+
+ spice_usbredir_channel_disconnect(channel);
+
+ rc = libusb_open(_g_usb_device_get_device(device), &handle);
+ if (rc != 0) {
+ g_set_error(err, SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
+ "Could not open usb device: %s [%i]",
+ libusb_strerror(rc), rc);
+ return FALSE;
+ }
+
+ priv->catch_error = err;
+ priv->host = usbredirhost_open(_g_usb_context_get_context(context),
+ handle, usbredir_log,
+ usbredir_read_callback,
+ usbredir_write_callback,
+ channel, PACKAGE_STRING, priv->verbose,
+ usbredirhost_fl_write_cb_owns_buffer);
+ priv->catch_error = NULL;
+ if (!priv->host) {
+ return FALSE;
+ }
+
+ priv->context = g_object_ref(context);
+ priv->device = g_object_ref(device);
+
+ spice_channel_connect(SPICE_CHANNEL(channel));
+
+ return TRUE;
+}
+
+void spice_usbredir_channel_disconnect(SpiceUsbredirChannel *channel)
+{
+ SpiceUsbredirChannelPrivate *priv = channel->priv;
+
+ spice_channel_disconnect(SPICE_CHANNEL(channel), SPICE_CHANNEL_NONE);
+ priv->up = FALSE;
+
+ if (priv->host) {
+ /* This also closes the libusb handle we passed to its _open */
+ usbredirhost_close(priv->host);
+ priv->host = NULL;
+ g_clear_object(&priv->device);
+ g_clear_object(&priv->context);
+ }
+}
+
+GUsbDevice *spice_usbredir_channel_get_device(SpiceUsbredirChannel *channel)
+{
+ return channel->priv->device;
+}
+
+void spice_usbredir_channel_do_write(SpiceUsbredirChannel *channel)
+{
+ SpiceUsbredirChannelPrivate *priv = channel->priv;
+
+ /* No recursion allowed! */
+ g_return_if_fail(priv->msg_out == NULL);
+
+ if (!priv->up || !usbredirhost_has_data_to_write(priv->host))
+ return;
+
+ priv->msg_out = spice_msg_out_new(SPICE_CHANNEL(channel),
+ SPICE_MSGC_SPICEVMC_DATA);
+
+ /* Collect all pending writes in priv->msg_out->marshaller */
+ usbredirhost_write_guest_data(priv->host);
+
+ spice_msg_out_send(priv->msg_out);
+ spice_msg_out_unref(priv->msg_out);
+ priv->msg_out = NULL;
+}
+
+/* ------------------------------------------------------------------ */
+/* callbacks (any context) */
+
+static void usbredir_log(void *user_data, int level, const char *msg)
+{
+ SpiceUsbredirChannel *channel = user_data;
+ SpiceUsbredirChannelPrivate *priv = channel->priv;
+
+ if (priv->catch_error && level == usbredirparser_error) {
+ g_set_error_literal(priv->catch_error, SPICE_CLIENT_ERROR,
+ SPICE_CLIENT_ERROR_FAILED, msg);
+ return;
+ }
+
+ if (level > priv->verbose) {
+ return;
+ }
+
+ switch(level) {
+ case usbredirparser_error:
+ g_critical(G_STRLOC " " "%s", msg); break;
+ case usbredirparser_warning:
+ g_warning(G_STRLOC " " "%s", msg); break;
+ case usbredirparser_info:
+ g_message(G_STRLOC " " "%s", msg); break;
+ default:
+ g_debug(G_STRLOC " " "%s", msg); break;
+ }
+}
+
+static int usbredir_read_callback(void *user_data, uint8_t *data, int count)
+{
+ SpiceUsbredirChannel *channel = user_data;
+ SpiceUsbredirChannelPrivate *priv = channel->priv;
+
+ if (priv->read_buf_size < count) {
+ count = priv->read_buf_size;
+ }
+
+ memcpy(data, priv->read_buf, count);
+
+ priv->read_buf_size -= count;
+ if (priv->read_buf_size) {
+ priv->read_buf += count;
+ } else {
+ priv->read_buf = NULL;
+ }
+
+ return count;
+}
+
+static void usbredir_free_write_cb_data(uint8_t *data, void *user_data)
+{
+ SpiceUsbredirChannel *channel = user_data;
+ SpiceUsbredirChannelPrivate *priv = channel->priv;
+
+ usbredirhost_free_write_buffer(priv->host, data);
+}
+
+static int usbredir_write_callback(void *user_data, uint8_t *data, int count)
+{
+ SpiceUsbredirChannel *channel = user_data;
+ SpiceUsbredirChannelPrivate *priv = channel->priv;
+
+ spice_marshaller_add_ref_full(priv->msg_out->marshaller, data, count,
+ usbredir_free_write_cb_data, channel);
+ return count;
+}
+
+#endif /* USE_USBREDIR */
+
+/* --------------------------------------------------------------------- */
+/* coroutine context */
+static void spice_usbredir_handle_msg(SpiceChannel *c, SpiceMsgIn *msg)
+{
+ int type = spice_msg_in_type(msg);
+ SpiceChannelClass *parent_class;
+
+ g_return_if_fail(type < SPICE_N_ELEMENTS(usbredir_handlers));
+
+ parent_class = SPICE_CHANNEL_CLASS(spice_usbredir_channel_parent_class);
+
+ if (usbredir_handlers[type] != NULL)
+ usbredir_handlers[type](c, msg);
+ else if (parent_class->handle_msg)
+ parent_class->handle_msg(c, msg);
+ else
+ g_return_if_reached();
+}
+
+static void spice_usbredir_channel_up(SpiceChannel *c)
+{
+#ifdef USE_USBREDIR
+ SpiceUsbredirChannel *channel = SPICE_USBREDIR_CHANNEL(c);
+ SpiceUsbredirChannelPrivate *priv = channel->priv;
+
+ priv->up = TRUE;
+ /* Flush any pending writes */
+ spice_usbredir_channel_do_write(channel);
+#endif
+}
+
+static void usbredir_handle_msg(SpiceChannel *c, SpiceMsgIn *in)
+{
+#ifdef USE_USBREDIR
+ SpiceUsbredirChannel *channel = SPICE_USBREDIR_CHANNEL(c);
+ SpiceUsbredirChannelPrivate *priv = channel->priv;
+ int size;
+ uint8_t *buf;
+
+ g_return_if_fail(priv->host != NULL);
+
+ /* No recursion allowed! */
+ g_return_if_fail(priv->read_buf == NULL);
+
+ buf = spice_msg_in_raw(in, &size);
+ priv->read_buf = buf;
+ priv->read_buf_size = size;
+
+ usbredirhost_read_guest_data(priv->host);
+ /* Send any acks, etc. which may be queued now */
+ spice_usbredir_channel_do_write(channel);
+#endif
+}
diff --git a/gtk/channel-usbredir.h b/gtk/channel-usbredir.h
new file mode 100644
index 0000000..ca0b535
--- /dev/null
+++ b/gtk/channel-usbredir.h
@@ -0,0 +1,60 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ Copyright (C) 2011 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_CLIENT_USBREDIR_CHANNEL_H__
+#define __SPICE_CLIENT_USBREDIR_CHANNEL_H__
+
+#include "spice-client.h"
+
+G_BEGIN_DECLS
+
+#define SPICE_TYPE_USBREDIR_CHANNEL (spice_usbredir_channel_get_type())
+#define SPICE_USBREDIR_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), SPICE_TYPE_USBREDIR_CHANNEL, SpiceUsbredirChannel))
+#define SPICE_USBREDIR_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), SPICE_TYPE_USBREDIR_CHANNEL, SpiceUsbredirChannelClass))
+#define SPICE_IS_USBREDIR_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), SPICE_TYPE_USBREDIR_CHANNEL))
+#define SPICE_IS_USBREDIR_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), SPICE_TYPE_USBREDIR_CHANNEL))
+#define SPICE_USBREDIR_CHANNEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), SPICE_TYPE_USBREDIR_CHANNEL, SpiceUsbredirChannelClass))
+
+typedef struct _SpiceUsbredirChannel SpiceUsbredirChannel;
+typedef struct _SpiceUsbredirChannelClass SpiceUsbredirChannelClass;
+typedef struct _SpiceUsbredirChannelPrivate SpiceUsbredirChannelPrivate;
+
+struct _SpiceUsbredirChannel {
+ SpiceChannel parent;
+
+ /*< private >*/
+ SpiceUsbredirChannelPrivate *priv;
+ /* Do not add fields to this struct */
+};
+
+struct _SpiceUsbredirChannelClass {
+ SpiceChannelClass parent_class;
+
+ /* signals */
+
+ /*< private >*/
+ /* Do not add fields to this struct */
+};
+
+GType spice_usbredir_channel_get_type(void);
+
+G_END_DECLS
+
+#endif /* __SPICE_CLIENT_USBREDIR_CHANNEL_H__ */
diff --git a/gtk/map-file b/gtk/map-file
index f358066..669b5dd 100644
--- a/gtk/map-file
+++ b/gtk/map-file
@@ -74,6 +74,7 @@ spice_smartcard_manager_insert_card;
spice_smartcard_manager_remove_card;
spice_smartcard_reader_get_type;
spice_smartcard_reader_is_software;
+spice_usbredir_channel_get_type;
spice_util_get_debug;
spice_util_get_version_string;
spice_util_set_debug;
diff --git a/gtk/spice-channel.c b/gtk/spice-channel.c
index a89b75c..3a17204 100644
--- a/gtk/spice-channel.c
+++ b/gtk/spice-channel.c
@@ -1658,7 +1658,8 @@ const gchar* spice_channel_type_to_string(gint type)
[ SPICE_CHANNEL_PLAYBACK ] = "playback",
[ SPICE_CHANNEL_RECORD ] = "record",
[ SPICE_CHANNEL_TUNNEL ] = "tunnel",
- [ SPICE_CHANNEL_SMARTCARD ] = "smartcard"
+ [ SPICE_CHANNEL_SMARTCARD ] = "smartcard",
+ [ SPICE_CHANNEL_USBREDIR ] = "usbredir",
};
const char *str = NULL;
@@ -1717,6 +1718,18 @@ SpiceChannel *spice_channel_new(SpiceSession *s, int type, int id)
break;
}
#endif
+#ifdef USE_USBREDIR
+ case SPICE_CHANNEL_USBREDIR: {
+ gboolean enabled;
+ g_object_get(G_OBJECT(s), "enable-usbredir", &enabled, NULL);
+ if (!enabled) {
+ g_debug("usbredir channel is disabled, not creating it");
+ return NULL;
+ }
+ gtype = SPICE_TYPE_USBREDIR_CHANNEL;
+ break;
+ }
+#endif
default:
g_debug("unsupported channel kind: %s: %d",
spice_channel_type_to_string(type), type);
diff --git a/gtk/spice-client-gtk.defs b/gtk/spice-client-gtk.defs
index f718c8c..513815e 100644
--- a/gtk/spice-client-gtk.defs
+++ b/gtk/spice-client-gtk.defs
@@ -77,6 +77,13 @@
(gtype-id "SPICE_TYPE_SMARTCARD_CHANNEL")
)
+(define-object UsbredirChannel
+ (in-module "Spice")
+ (parent "SpiceChannel")
+ (c-name "SpiceUsbredirChannel")
+ (gtype-id "SPICE_TYPE_USBREDIR_CHANNEL")
+)
+
;; Enumerations and flags ...
(define-enum DisplayKeyEvent
@@ -649,3 +656,12 @@
)
+
+;; From channel-usbredir.h
+
+(define-function spice_usbredir_channel_get_type
+ (c-name "spice_usbredir_channel_get_type")
+ (return-type "GType")
+)
+
+
diff --git a/gtk/spice-client.h b/gtk/spice-client.h
index 885d81c..54284ce 100644
--- a/gtk/spice-client.h
+++ b/gtk/spice-client.h
@@ -39,6 +39,7 @@
#include "channel-playback.h"
#include "channel-record.h"
#include "channel-smartcard.h"
+#include "channel-usbredir.h"
#define SPICE_CLIENT_ERROR spice_client_error_quark()
diff --git a/gtk/spice-option.c b/gtk/spice-option.c
index 6c4e50c..4a2ba90 100644
--- a/gtk/spice-option.c
+++ b/gtk/spice-option.c
@@ -32,6 +32,7 @@ static char *host_subject = NULL;
static char *smartcard_db = NULL;
static char *smartcard_certificates = NULL;
static gboolean smartcard = FALSE;
+static gboolean disable_usbredir = FALSE;
static void option_version(void)
{
@@ -69,6 +70,8 @@ GOptionGroup* spice_get_option_group(void)
N_("Certificates to use for software smartcards (field=values separated by commas)"), N_("<certificates>") },
{ "spice-smartcard-db", '\0', 0, G_OPTION_ARG_STRING, &smartcard_db,
N_("Path to the local certificate database to use for software smartcard certificates"), N_("<certificate-db>") },
+ { "spice-disable-usbredir", '\0', 0, G_OPTION_ARG_NONE, &disable_usbredir,
+ N_("Disable USB redirection support"), NULL },
{ "spice-debug", '\0', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, option_debug,
N_("Enable Spice-GTK debugging"), NULL },
@@ -127,4 +130,6 @@ void spice_set_session_option(SpiceSession *session)
if (smartcard_db)
g_object_set(session, "smartcard-db", smartcard_db, NULL);
}
+ if (disable_usbredir)
+ g_object_set(session, "enable-usbredir", FALSE, NULL);
}
diff --git a/gtk/spice-session.c b/gtk/spice-session.c
index d6100f6..b0bbb14 100644
--- a/gtk/spice-session.c
+++ b/gtk/spice-session.c
@@ -59,6 +59,10 @@ struct _SpiceSessionPrivate {
* fallback to using a default database.
*/
char * smartcard_db;
+
+ /* whether to enable USB redirection */
+ gboolean usbredir;
+
GStrv disable_effects;
gint color_depth;
@@ -139,6 +143,7 @@ enum {
PROP_SMARTCARD,
PROP_SMARTCARD_CERTIFICATES,
PROP_SMARTCARD_DB,
+ PROP_USBREDIR,
PROP_DISABLE_EFFECTS,
PROP_COLOR_DEPTH,
};
@@ -160,6 +165,7 @@ static void spice_session_init(SpiceSession *session)
SPICE_DEBUG("New session (compiled from package " PACKAGE_STRING ")");
s = session->priv = SPICE_SESSION_GET_PRIVATE(session);
memset(s, 0, sizeof(*s));
+ s->usbredir = TRUE;
ring_init(&s->channels);
cache_init(&s->images, "image");
@@ -393,6 +399,9 @@ static void spice_session_get_property(GObject *gobject,
case PROP_SMARTCARD_DB:
g_value_set_string(value, s->smartcard_db);
break;
+ case PROP_USBREDIR:
+ g_value_set_boolean(value, s->usbredir);
+ break;
case PROP_DISABLE_EFFECTS:
g_value_set_boxed(value, s->disable_effects);
break;
@@ -479,6 +488,9 @@ static void spice_session_set_property(GObject *gobject,
g_free(s->smartcard_db);
s->smartcard_db = g_value_dup_string(value);
break;
+ case PROP_USBREDIR:
+ s->usbredir = g_value_get_boolean(value);
+ break;
case PROP_DISABLE_EFFECTS:
g_strfreev(s->disable_effects);
s->disable_effects = g_value_dup_boxed(value);
@@ -788,6 +800,23 @@ static void spice_session_class_init(SpiceSessionClass *klass)
G_PARAM_STATIC_STRINGS));
/**
+ * SpiceSession:enable-usbredir:
+ *
+ * If set to TRUE, the usbredir channel will be enabled and USB devices
+ * can be redirected to the guest
+ *
+ * Since: 0.7
+ **/
+ g_object_class_install_property
+ (gobject_class, PROP_USBREDIR,
+ g_param_spec_boolean("enable-usbredir",
+ "Enable USB device redirection",
+ "Forward USB devices to the SPICE server",
+ TRUE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
* SpiceSession::channel-new:
* @session: the session that emitted the signal
* @channel: the new #SpiceChannel
--
1.7.5.1
More information about the Spice-devel
mailing list