[Spice-devel] [spice-gtk v4 13/13] cd-sharing: Implementation of CD sharing feature
Yuri Benditovich
yuri.benditovich at daynix.com
Mon Sep 17 13:23:03 UTC 2018
This commit contains modified files related to CD sharing feature.
The feature adds ability to share one or more CD images
(or local CD-DVD devices) with the guest by emulating
USB CD devices connected to guest system.
Prerequisites:
Guest system shall have USB2 or USB3 controller and one or more
USB redirection channels. Each emulated CD device currently
uses one USB redirection channel.
Usage from GUI:
New USB device redirection widget includes 'Add CD' button that
allows selection of ISO file which will be used for emulation
of USB CD drive.
Usage from command line:
Added command line option '--spice-share-cd=filename' which
allows to create one or more emulated CD drives.
Sharing local CD drive:
For sharing local CD drive use file name of respective device,
it depends on the operating system of client machine.
Example for Windows clients - d:
Example for Linux clients - /dev/cdrom
Build backward compatibility:
The feature can be disabled by build configuration:
--enable-cdsharing=no disables CD sharing (USB redirection
GUI uses new USB redirection widget)
--enable-newusbwidget=no also disables CD sharing (USB
redirection GUI uses old USB widget)
Notes for Windows build of 'VirtViewer':
In order to show proper icons in new USB redirection widget
the installation package of VirtViewer shall include
following icons:
"media-optical", "network-transmit-receive", "network-offline",
"dialog-warning", "dialog-information",
"preferences-system","system-lock-screen","media-eject",
"edit-delete", "list-add"
Automatic redirection of emulated CD devices:
Same as one for local USB drives:
- shared CD devices created by command line redirected
according to 'redirect-on-connect' filter
- shared CD devices created during session redirected
according to 'auto-redirect' filter
Disable redirection of emulated CD devices on server side:
Same as for redirection of local USB drives:
-device usb-redir,filter=<filter> option of qemu
Signed-off-by: Yuri Benditovich <yuri.benditovich at daynix.com>
Signed-off-by: Alexander Nezhinsky <alexander at daynix.com>
---
configure.ac | 27 ++
src/Makefile.am | 15 +-
src/channel-usbredir-priv.h | 9 +-
src/channel-usbredir.c | 267 +++++----------
src/map-file | 9 +
src/spice-option.c | 15 +
src/usb-device-manager-priv.h | 5 +-
src/usb-device-manager.c | 743 ++++++++++++++++++++++++++++--------------
src/usb-device-manager.h | 107 +++++-
src/usb-device-widget.c | 5 +
src/usbutil.c | 52 +--
src/usbutil.h | 5 +-
src/win-usb-dev.c | 66 ++--
src/win-usb-dev.h | 2 +
14 files changed, 807 insertions(+), 520 deletions(-)
diff --git a/configure.ac b/configure.ac
index 7b32e29..1e977b3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -349,6 +349,32 @@ else
fi
AM_CONDITIONAL([WITH_USBREDIR], [test "x$have_usbredir" = "xyes"])
+AC_ARG_ENABLE([newusbwidget],
+ AS_HELP_STRING([--enable-newusbwidget=@<:@yes/no@:>@],
+ [Use new USB devices widget @<:@default=yes@:>@]),
+ [],
+ [enable_newusbwidget="yes"])
+
+AC_ARG_ENABLE([cdsharing],
+ AS_HELP_STRING([--enable-cdsharing=@<:@auto/yes/no@:>@],
+ [Enable CD charing feature @<:@default=auto@:>@]),
+ [],
+ [enable_cdsharing="auto"])
+
+if test "x$enable_newusbwidget" = "xno" || test "x$have_usbredir" != "xyes" ; then
+ have_newusbwidget="no"
+else
+ have_newusbwidget="yes"
+ AC_DEFINE([USE_NEW_USB_WIDGET], [1], [Define if new USB widget should be used])
+fi
+
+if test "x$enable_cdsharing" = "xno" || test "x$have_newusbwidget" = "xno"; then
+ have_cdsharing="no"
+else
+ have_cdsharing="yes"
+ AC_DEFINE([USE_CD_SHARING], [1], [Define if supporting cd sharing via usbredir])
+fi
+
AC_ARG_ENABLE([polkit],
AS_HELP_STRING([--enable-polkit=@<:@auto/yes/no@:>@],
[Enable PolicyKit support (for the usb acl helper)@<:@default=auto@:>@]),
@@ -605,6 +631,7 @@ AC_MSG_NOTICE([
SASL support: ${have_sasl}
Smartcard support: ${have_smartcard}
USB redirection support: ${have_usbredir} ${with_usbredir_hotplug}
+ CD sharing support: ${have_cdsharing} new widget: ${have_newusbwidget}
DBus: ${have_dbus}
WebDAV support: ${have_phodav}
LZ4 support: ${have_lz4}
diff --git a/src/Makefile.am b/src/Makefile.am
index 4dd657d..c23e7da 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -127,7 +127,8 @@ SPICE_GTK_SOURCES_COMMON = \
spice-grabsequence-priv.h \
desktop-integration.c \
desktop-integration.h \
- usb-device-widget.c \
+ usb-device-redir-widget.c \
+ usb-device-widget.c \
$(NULL)
nodist_SPICE_GTK_SOURCES_COMMON = \
@@ -249,6 +250,15 @@ libspice_client_glib_2_0_la_SOURCES = \
spice-uri-priv.h \
usb-device-manager.c \
usb-device-manager-priv.h \
+ usb-backend-common.c \
+ usb-backend.h \
+ cd-usb-bulk-msd.c \
+ cd-usb-bulk-msd.h \
+ cd-scsi.c \
+ cd-scsi.h \
+ cd-device.h \
+ cd-device-win.c \
+ cd-device-linux.c \
usbutil.c \
usbutil.h \
$(USB_ACL_HELPER_SRCS) \
@@ -536,7 +546,8 @@ gtk_introspection_files = \
spice-gtk-session.c \
spice-widget.c \
spice-grabsequence.c \
- usb-device-widget.c \
+ usb-device-redir-widget.c \
+ usb-device-widget.c \
$(NULL)
SpiceClientGLib-2.0.gir: libspice-client-glib-2.0.la
diff --git a/src/channel-usbredir-priv.h b/src/channel-usbredir-priv.h
index 17e9716..2c3e705 100644
--- a/src/channel-usbredir-priv.h
+++ b/src/channel-usbredir-priv.h
@@ -21,9 +21,8 @@
#ifndef __SPICE_CLIENT_USBREDIR_CHANNEL_PRIV_H__
#define __SPICE_CLIENT_USBREDIR_CHANNEL_PRIV_H__
-#include <libusb.h>
-#include <usbredirfilter.h>
#include "spice-client.h"
+#include "usb-backend.h"
G_BEGIN_DECLS
@@ -31,7 +30,7 @@ G_BEGIN_DECLS
context should not be destroyed before the last device has been
disconnected */
void spice_usbredir_channel_set_context(SpiceUsbredirChannel *channel,
- libusb_context *context);
+ SpiceUsbBackend *context);
void spice_usbredir_channel_disconnect_device_async(SpiceUsbredirChannel *channel,
GCancellable *cancellable,
@@ -46,7 +45,7 @@ gboolean spice_usbredir_channel_disconnect_device_finish(SpiceUsbredirChannel *c
(through spice_channel_connect()), before calling this. */
void spice_usbredir_channel_connect_device_async(
SpiceUsbredirChannel *channel,
- libusb_device *device,
+ SpiceUsbBackendDevice *device,
SpiceUsbDevice *spice_device,
GCancellable *cancellable,
GAsyncReadyCallback callback,
@@ -58,7 +57,7 @@ gboolean spice_usbredir_channel_connect_device_finish(
void spice_usbredir_channel_disconnect_device(SpiceUsbredirChannel *channel);
-libusb_device *spice_usbredir_channel_get_device(SpiceUsbredirChannel *channel);
+SpiceUsbBackendDevice *spice_usbredir_channel_get_device(SpiceUsbredirChannel *channel);
void spice_usbredir_channel_lock(SpiceUsbredirChannel *channel);
diff --git a/src/channel-usbredir.c b/src/channel-usbredir.c
index 1d9c380..ffdf9b1 100644
--- a/src/channel-usbredir.c
+++ b/src/channel-usbredir.c
@@ -23,7 +23,6 @@
#ifdef USE_USBREDIR
#include <glib/gi18n-lib.h>
-#include <usbredirhost.h>
#ifdef USE_LZ4
#include <lz4.h>
#endif
@@ -66,15 +65,12 @@ enum SpiceUsbredirChannelState {
};
struct _SpiceUsbredirChannelPrivate {
- libusb_device *device;
+ SpiceUsbBackendDevice *device;
SpiceUsbDevice *spice_device;
- libusb_context *context;
- struct usbredirhost *host;
+ SpiceUsbBackend *context;
+ SpiceUsbBackendChannel *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;
enum SpiceUsbredirChannelState state;
#ifdef USE_POLKIT
GTask *task;
@@ -90,18 +86,10 @@ 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);
-static int usbredir_read_callback(void *user_data, uint8_t *data, int count);
+static void usbredir_log(void *user_data, const char *msg, gboolean error);
static int usbredir_write_callback(void *user_data, uint8_t *data, int count);
-static void usbredir_write_flush_callback(void *user_data);
-#if USBREDIR_VERSION >= 0x000701
-static uint64_t usbredir_buffered_output_size_callback(void *user_data);
-#endif
-
-static void *usbredir_alloc_lock(void);
-static void usbredir_lock_lock(void *user_data);
-static void usbredir_unlock_lock(void *user_data);
-static void usbredir_free_lock(void *user_data);
+static gboolean usbredir_is_channel_ready(void *user_data);
+static uint64_t usbredir_get_queue_size(void *user_data);
#else
struct _SpiceUsbredirChannelPrivate {
@@ -128,7 +116,7 @@ static void _channel_reset_finish(SpiceUsbredirChannel *channel)
spice_usbredir_channel_lock(channel);
- usbredirhost_close(priv->host);
+ spice_usb_backend_channel_finalize(priv->host);
priv->host = NULL;
/* Call set_context to re-create the host */
@@ -228,7 +216,7 @@ static void spice_usbredir_channel_finalize(GObject *obj)
SpiceUsbredirChannel *channel = SPICE_USBREDIR_CHANNEL(obj);
if (channel->priv->host)
- usbredirhost_close(channel->priv->host);
+ spice_usb_backend_channel_finalize(channel->priv->host);
#ifdef USE_USBREDIR
g_mutex_clear(&channel->priv->device_connect_mutex);
#endif
@@ -252,33 +240,24 @@ static void channel_set_handlers(SpiceChannelClass *klass)
/* private api */
G_GNUC_INTERNAL
-void spice_usbredir_channel_set_context(SpiceUsbredirChannel *channel,
- libusb_context *context)
+void spice_usbredir_channel_set_context(
+ SpiceUsbredirChannel *channel,
+ SpiceUsbBackend *context)
{
SpiceUsbredirChannelPrivate *priv = channel->priv;
+ SpiceUsbBackendChannelInitData init_data;
+ init_data.user_data = channel;
+ init_data.get_queue_size = usbredir_get_queue_size;
+ init_data.is_channel_ready = usbredir_is_channel_ready;
+ init_data.log = usbredir_log;
+ init_data.write_callback = usbredir_write_callback;
+ init_data.debug = spice_util_get_debug();
g_return_if_fail(priv->host == NULL);
priv->context = context;
- 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");
+ priv->host = spice_usb_backend_channel_initialize(context, &init_data);
-#if USBREDIR_VERSION >= 0x000701
- usbredirhost_set_buffered_output_size_cb(priv->host, usbredir_buffered_output_size_callback);
-#endif
#ifdef USE_LZ4
spice_channel_set_capability(channel, SPICE_SPICEVMC_CAP_DATA_COMPRESS_LZ4);
#endif
@@ -289,9 +268,8 @@ static gboolean spice_usbredir_channel_open_device(
{
SpiceUsbredirChannelPrivate *priv = channel->priv;
SpiceSession *session;
- libusb_device_handle *handle = NULL;
- int rc, status;
SpiceUsbDeviceManager *manager;
+ const char *msg = NULL;
g_return_val_if_fail(priv->state == STATE_DISCONNECTED
#ifdef USE_POLKIT
@@ -299,29 +277,28 @@ static gboolean spice_usbredir_channel_open_device(
#endif
, FALSE);
- rc = libusb_open(priv->device, &handle);
- if (rc != 0) {
- g_set_error(err, SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
- "Could not open usb device: %s [%i]",
- spice_usbutil_libusb_strerror(rc), rc);
- return FALSE;
- }
-
priv->catch_error = err;
- status = usbredirhost_set_device(priv->host, handle);
- priv->catch_error = NULL;
- if (status != usb_redir_success) {
- g_return_val_if_fail(err == NULL || *err != NULL, FALSE);
+ if (!spice_usb_backend_channel_attach(priv->host, priv->device, &msg)) {
+ priv->catch_error = NULL;
+ if (*err == NULL) {
+ if (!msg) {
+ msg = "Exact error not reported";
+ }
+ g_set_error(err, SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
+ "Error attaching device: %s", msg);
+ }
return FALSE;
}
+ priv->catch_error = NULL;
session = spice_channel_get_session(SPICE_CHANNEL(channel));
manager = spice_usb_device_manager_get(session, NULL);
g_return_val_if_fail(manager != NULL, FALSE);
priv->usb_device_manager = g_object_ref(manager);
- if (!spice_usb_device_manager_start_event_listening(priv->usb_device_manager, err)) {
- usbredirhost_set_device(priv->host, NULL);
+ if (spice_usb_backend_device_need_thread(priv->device) &&
+ !spice_usb_device_manager_start_event_listening(priv->usb_device_manager, err)) {
+ spice_usb_backend_channel_attach(priv->host, NULL, NULL);
return FALSE;
}
@@ -352,8 +329,7 @@ static void spice_usbredir_channel_open_acl_cb(
spice_usbredir_channel_open_device(channel, &err);
}
if (err) {
- libusb_unref_device(priv->device);
- priv->device = NULL;
+ g_clear_pointer(&priv->device, spice_usb_backend_device_release);
g_boxed_free(spice_usb_device_get_type(), priv->spice_device);
priv->spice_device = NULL;
priv->state = STATE_DISCONNECTED;
@@ -370,7 +346,6 @@ static void spice_usbredir_channel_open_acl_cb(
}
#endif
-#ifndef USE_POLKIT
static void
_open_device_async_cb(GTask *task,
gpointer object,
@@ -384,8 +359,7 @@ _open_device_async_cb(GTask *task,
spice_usbredir_channel_lock(channel);
if (!spice_usbredir_channel_open_device(channel, &err)) {
- libusb_unref_device(priv->device);
- priv->device = NULL;
+ g_clear_pointer(&priv->device, spice_usb_backend_device_release);
g_boxed_free(spice_usb_device_get_type(), priv->spice_device);
priv->spice_device = NULL;
}
@@ -398,18 +372,20 @@ _open_device_async_cb(GTask *task,
g_task_return_boolean(task, TRUE);
}
}
-#endif
G_GNUC_INTERNAL
void spice_usbredir_channel_connect_device_async(
SpiceUsbredirChannel *channel,
- libusb_device *device,
+ SpiceUsbBackendDevice *device,
SpiceUsbDevice *spice_device,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
SpiceUsbredirChannelPrivate *priv = channel->priv;
+#ifdef USE_POLKIT
+ const UsbDeviceInformation *info = spice_usb_backend_device_get_info(device);
+#endif
GTask *task;
g_return_if_fail(SPICE_IS_USBREDIR_CHANNEL(channel));
@@ -436,25 +412,28 @@ void spice_usbredir_channel_connect_device_async(
goto done;
}
- priv->device = libusb_ref_device(device);
+ spice_usb_backend_device_acquire(device);
+ priv->device = device;
priv->spice_device = g_boxed_copy(spice_usb_device_get_type(),
spice_device);
#ifdef USE_POLKIT
- priv->task = task;
- 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);
- spice_usb_acl_helper_open_acl_async(priv->acl_helper,
- libusb_get_bus_number(device),
- libusb_get_device_address(device),
- cancellable,
- spice_usbredir_channel_open_acl_cb,
- channel);
- return;
-#else
- g_task_run_in_thread(task, _open_device_async_cb);
+ // avoid calling ACL helper for emulated CD devices
+ if (info->max_luns == 0) {
+ priv->task = task;
+ 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);
+ spice_usb_acl_helper_open_acl_async(priv->acl_helper,
+ info->bus,
+ info->address,
+ cancellable,
+ spice_usbredir_channel_open_acl_cb,
+ channel);
+ return;
+ }
#endif
+ g_task_run_in_thread(task, _open_device_async_cb);
done:
g_object_unref(task);
@@ -501,13 +480,14 @@ void spice_usbredir_channel_disconnect_device(SpiceUsbredirChannel *channel)
* libusb_handle_events call in the thread.
*/
g_warn_if_fail(priv->usb_device_manager != NULL);
- spice_usb_device_manager_stop_event_listening(priv->usb_device_manager);
+ if (spice_usb_backend_device_need_thread(priv->device)) {
+ spice_usb_device_manager_stop_event_listening(priv->usb_device_manager);
+ }
g_clear_object(&priv->usb_device_manager);
/* 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;
+ spice_usb_backend_channel_attach(priv->host, NULL, NULL);
+ g_clear_pointer(&priv->device, spice_usb_backend_device_release);
g_boxed_free(spice_usb_device_get_type(), priv->spice_device);
priv->spice_device = NULL;
priv->state = STATE_DISCONNECTED;
@@ -558,7 +538,7 @@ spice_usbredir_channel_get_spice_usb_device(SpiceUsbredirChannel *channel)
#endif
G_GNUC_INTERNAL
-libusb_device *spice_usbredir_channel_get_device(SpiceUsbredirChannel *channel)
+SpiceUsbBackendDevice *spice_usbredir_channel_get_device(SpiceUsbredirChannel *channel)
{
return channel->priv->device;
}
@@ -573,85 +553,46 @@ void spice_usbredir_channel_get_guest_filter(
g_return_if_fail(priv->host != NULL);
- usbredirhost_get_guest_filter(priv->host, rules_ret, rules_count_ret);
+ spice_usb_backend_channel_get_guest_filter(priv->host, rules_ret, rules_count_ret);
}
/* ------------------------------------------------------------------ */
/* callbacks (any context) */
-#if USBREDIR_VERSION >= 0x000701
-static uint64_t usbredir_buffered_output_size_callback(void *user_data)
+static uint64_t usbredir_get_queue_size(void *user_data)
{
g_return_val_if_fail(SPICE_IS_USBREDIR_CHANNEL(user_data), 0);
return spice_channel_get_queue_size(SPICE_CHANNEL(user_data));
}
-#endif
-/* Note that this function must be re-entrant safe, as it can get called
- from both the main thread as well as from the usb event handling thread */
-static void usbredir_write_flush_callback(void *user_data)
+static gboolean usbredir_is_channel_ready(void *user_data)
{
SpiceUsbredirChannel *channel = SPICE_USBREDIR_CHANNEL(user_data);
SpiceUsbredirChannelPrivate *priv = channel->priv;
-
- if (spice_channel_get_state(SPICE_CHANNEL(channel)) !=
- SPICE_CHANNEL_STATE_READY)
- return;
-
+ if (spice_channel_get_state(SPICE_CHANNEL(channel)) != SPICE_CHANNEL_STATE_READY)
+ return FALSE;
if (!priv->host)
- return;
+ return FALSE;
- usbredirhost_write_guest_data(priv->host);
+ return TRUE;
}
-static void usbredir_log(void *user_data, int level, const char *msg)
+static void usbredir_log(void *user_data, const char *msg, gboolean error)
{
SpiceUsbredirChannel *channel = user_data;
SpiceUsbredirChannelPrivate *priv = channel->priv;
- if (priv->catch_error && level == usbredirparser_error) {
- CHANNEL_DEBUG(channel, "%s", msg);
+ CHANNEL_DEBUG(channel, "%s", msg);
+ if (priv->catch_error && error) {
/* Remove "usbredirhost: " prefix from usbredirhost messages */
if (strncmp(msg, "usbredirhost: ", 14) == 0)
g_set_error_literal(priv->catch_error, SPICE_CLIENT_ERROR,
- SPICE_CLIENT_ERROR_FAILED, msg + 14);
+ SPICE_CLIENT_ERROR_FAILED, msg + 14);
else
g_set_error_literal(priv->catch_error, SPICE_CLIENT_ERROR,
- SPICE_CLIENT_ERROR_FAILED, msg);
- return;
+ SPICE_CLIENT_ERROR_FAILED, msg);
+ priv->catch_error = NULL;
}
-
- switch (level) {
- case usbredirparser_error:
- g_critical("%s", msg);
- break;
- case usbredirparser_warning:
- g_warning("%s", msg);
- break;
- default:
- CHANNEL_DEBUG(channel, "%s", msg);
- }
-}
-
-static int usbredir_read_callback(void *user_data, uint8_t *data, int count)
-{
- SpiceUsbredirChannel *channel = user_data;
- SpiceUsbredirChannelPrivate *priv = channel->priv;
-
- count = MIN(priv->read_buf_size, count);
-
- if (count != 0) {
- 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)
@@ -659,7 +600,7 @@ 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);
+ spice_usb_backend_return_write_data(priv->host, data);
}
#ifdef USE_LZ4
@@ -731,7 +672,7 @@ static int usbredir_write_callback(void *user_data, uint8_t *data, int count)
#ifdef USE_LZ4
if (try_write_compress_LZ4(channel, data, count)) {
- usbredirhost_free_write_buffer(channel->priv->host, data);
+ spice_usb_backend_return_write_data(channel->priv->host, data);
return count;
}
#endif
@@ -744,15 +685,6 @@ static int usbredir_write_callback(void *user_data, uint8_t *data, int count)
return count;
}
-static void *usbredir_alloc_lock(void) {
- GMutex *mutex;
-
- mutex = g_new0(GMutex, 1);
- g_mutex_init(mutex);
-
- return mutex;
-}
-
G_GNUC_INTERNAL
void spice_usbredir_channel_lock(SpiceUsbredirChannel *channel)
{
@@ -765,25 +697,6 @@ void spice_usbredir_channel_unlock(SpiceUsbredirChannel *channel)
g_mutex_unlock(&channel->priv->device_connect_mutex);
}
-static void usbredir_lock_lock(void *user_data) {
- GMutex *mutex = user_data;
-
- g_mutex_lock(mutex);
-}
-
-static void usbredir_unlock_lock(void *user_data) {
- GMutex *mutex = user_data;
-
- g_mutex_unlock(mutex);
-}
-
-static void usbredir_free_lock(void *user_data) {
- GMutex *mutex = user_data;
-
- g_mutex_clear(mutex);
- g_free(mutex);
-}
-
/* --------------------------------------------------------------------- */
typedef struct device_error_data {
@@ -819,10 +732,14 @@ static void spice_usbredir_channel_up(SpiceChannel *c)
{
SpiceUsbredirChannel *channel = SPICE_USBREDIR_CHANNEL(c);
SpiceUsbredirChannelPrivate *priv = channel->priv;
+ SpiceSession *session = spice_channel_get_session(c);
+ SpiceUsbDeviceManager *manager = spice_usb_device_manager_get(session, NULL);
g_return_if_fail(priv->host != NULL);
/* Flush any pending writes */
- usbredirhost_write_guest_data(priv->host);
+ spice_usb_backend_channel_up(priv->host);
+ /* Check which existing device can be redirected right now */
+ spice_usb_device_manager_check_redir_on_connect(manager, c);
}
static int try_handle_compressed_msg(SpiceMsgCompressedData *compressed_data_msg,
@@ -872,26 +789,20 @@ static void usbredir_handle_msg(SpiceChannel *c, SpiceMsgIn *in)
g_return_if_fail(priv->host != NULL);
- /* No recursion allowed! */
- g_return_if_fail(priv->read_buf == NULL);
-
if (spice_msg_in_type(in) == SPICE_MSG_SPICEVMC_COMPRESSED_DATA) {
SpiceMsgCompressedData *compressed_data_msg = spice_msg_in_parsed(in);
if (try_handle_compressed_msg(compressed_data_msg, &buf, &size)) {
- priv->read_buf_size = size;
- priv->read_buf = buf;
+ /* uncompressed ok*/
} else {
- r = usbredirhost_read_parse_error;
+ r = USB_REDIR_ERROR_READ_PARSE;
}
} else { /* Regular SPICE_MSG_SPICEVMC_DATA msg */
buf = spice_msg_in_raw(in, &size);
- priv->read_buf_size = size;
- priv->read_buf = buf;
}
spice_usbredir_channel_lock(channel);
if (r == 0)
- r = usbredirhost_read_guest_data(priv->host);
+ r = spice_usb_backend_provide_read_data(priv->host, buf, size);
if (r != 0) {
SpiceUsbDevice *spice_device = priv->spice_device;
device_error_data err_data;
@@ -905,16 +816,16 @@ static void usbredir_handle_msg(SpiceChannel *c, SpiceMsgIn *in)
desc = spice_usb_device_get_description(spice_device, NULL);
switch (r) {
- case usbredirhost_read_parse_error:
+ case USB_REDIR_ERROR_READ_PARSE:
err = g_error_new(SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
_("usbredir protocol parse error for %s"), desc);
break;
- case usbredirhost_read_device_rejected:
+ case USB_REDIR_ERROR_DEV_REJECTED:
err = g_error_new(SPICE_CLIENT_ERROR,
SPICE_CLIENT_ERROR_USB_DEVICE_REJECTED,
_("%s rejected by host"), desc);
break;
- case usbredirhost_read_device_lost:
+ case USB_REDIR_ERROR_DEV_LOST:
err = g_error_new(SPICE_CLIENT_ERROR,
SPICE_CLIENT_ERROR_USB_DEVICE_LOST,
_("%s disconnected (fatal IO error)"), desc);
diff --git a/src/map-file b/src/map-file
index cdb81c3..154fd08 100644
--- a/src/map-file
+++ b/src/map-file
@@ -156,6 +156,7 @@ spice_uri_set_scheme;
spice_uri_set_user;
spice_uri_to_string;
spice_usb_device_get_description;
+spice_usb_device_get_info;
spice_usb_device_get_libusb_device;
spice_usb_device_get_type;
spice_usb_device_manager_can_redirect_device;
@@ -178,6 +179,14 @@ spice_util_get_version_string;
spice_util_set_debug;
spice_uuid_to_string;
spice_webdav_channel_get_type;
+spice_usb_device_manager_is_device_cd;
+spice_usb_device_manager_get_device_luns;
+spice_usb_device_manager_add_cd_lun;
+spice_usb_device_manager_device_lun_get_info;
+spice_usb_device_manager_device_lun_lock;
+spice_usb_device_manager_device_lun_load;
+spice_usb_device_manager_device_lun_change_media;
+spice_usb_device_manager_device_lun_remove;
local:
*;
};
diff --git a/src/spice-option.c b/src/spice-option.c
index 6b400bc..dfa7335 100644
--- a/src/spice-option.c
+++ b/src/spice-option.c
@@ -33,6 +33,7 @@ static char *smartcard_db = NULL;
static char *smartcard_certificates = NULL;
static char *usbredir_auto_redirect_filter = NULL;
static char *usbredir_redirect_on_connect = NULL;
+static gchar **cd_share_files = NULL;
static gboolean smartcard = FALSE;
static gboolean disable_audio = FALSE;
static gboolean disable_usbredir = FALSE;
@@ -218,6 +219,8 @@ GOptionGroup* spice_get_option_group(void)
N_("Filter selecting USB devices to be auto-redirected when plugged in"), N_("<filter-string>") },
{ "spice-usbredir-redirect-on-connect", '\0', 0, G_OPTION_ARG_STRING, &usbredir_redirect_on_connect,
N_("Filter selecting USB devices to redirect on connect"), N_("<filter-string>") },
+ { "spice-share-cd", '\0', 0, G_OPTION_ARG_STRING_ARRAY, &cd_share_files,
+ N_("Name of ISO file or CD/DVD device to share"), N_("<filename> (repeat allowed)") },
{ "spice-cache-size", '\0', 0, G_OPTION_ARG_INT, &cache_size,
N_("Image cache size (deprecated)"), N_("<bytes>") },
{ "spice-glz-window-size", '\0', 0, G_OPTION_ARG_INT, &glz_window_size,
@@ -308,6 +311,18 @@ void spice_set_session_option(SpiceSession *session)
g_object_set(m, "redirect-on-connect",
usbredir_redirect_on_connect, NULL);
}
+ if (cd_share_files) {
+ SpiceUsbDeviceManager *m = spice_usb_device_manager_get(session, NULL);
+ if (m) {
+ gchar **name = cd_share_files;
+ while (name && *name) {
+ g_object_set(m, "share-cd", *name, NULL);
+ name++;
+ }
+ }
+ g_strfreev(cd_share_files);
+ cd_share_files = NULL;
+ }
if (disable_usbredir)
g_object_set(session, "enable-usbredir", FALSE, NULL);
if (disable_audio)
diff --git a/src/usb-device-manager-priv.h b/src/usb-device-manager-priv.h
index 83884d7..53149fb 100644
--- a/src/usb-device-manager-priv.h
+++ b/src/usb-device-manager-priv.h
@@ -32,9 +32,12 @@ void spice_usb_device_manager_stop_event_listening(
SpiceUsbDeviceManager *manager);
#ifdef USE_USBREDIR
-#include <libusb.h>
+#include "usb-backend.h"
void spice_usb_device_manager_device_error(
SpiceUsbDeviceManager *manager, SpiceUsbDevice *device, GError *err);
+void spice_usb_device_manager_check_redir_on_connect(
+ SpiceUsbDeviceManager *manager, SpiceChannel *channel);
+
guint8 spice_usb_device_get_busnum(const SpiceUsbDevice *device);
guint8 spice_usb_device_get_devaddr(const SpiceUsbDevice *device);
diff --git a/src/usb-device-manager.c b/src/usb-device-manager.c
index 50fb491..896a4eb 100644
--- a/src/usb-device-manager.c
+++ b/src/usb-device-manager.c
@@ -24,10 +24,11 @@
#include <glib-object.h>
#ifdef USE_USBREDIR
+
#include <errno.h>
-#include <libusb.h>
#ifdef G_OS_WIN32
+#include <windows.h>
#include "usbdk_api.h"
#endif
@@ -41,8 +42,8 @@
#endif
#include "channel-usbredir-priv.h"
-#include "usbredirhost.h"
#include "usbutil.h"
+
#endif
#include "spice-session-priv.h"
@@ -58,6 +59,9 @@
#define DEV_ID_FMT "0x%04x:0x%04x"
#endif
+#define CD_SHARE_VENDOR "Red Hat Inc."
+#define CD_SHARE_PRODUCT "Spice CD drive"
+
/**
* SECTION:usb-device-manager
* @short_description: USB device management
@@ -85,6 +89,7 @@ enum {
PROP_AUTO_CONNECT_FILTER,
PROP_REDIRECT_ON_CONNECT,
PROP_FREE_CHANNELS,
+ PROP_SHARE_CD
};
enum
@@ -93,6 +98,7 @@ enum
DEVICE_REMOVED,
AUTO_CONNECT_FAILED,
DEVICE_ERROR,
+ DEVICE_CHANGED,
LAST_SIGNAL,
};
@@ -102,7 +108,7 @@ struct _SpiceUsbDeviceManagerPrivate {
gchar *auto_connect_filter;
gchar *redirect_on_connect;
#ifdef USE_USBREDIR
- libusb_context *context;
+ SpiceUsbBackend *context;
int event_listeners;
GThread *event_thread;
gint event_thread_run;
@@ -112,10 +118,9 @@ struct _SpiceUsbDeviceManagerPrivate {
int redirect_on_connect_rules_count;
#ifdef USE_GUDEV
GUdevClient *udev;
- libusb_device **coldplug_list; /* Avoid needless reprobing during init */
+ SpiceUsbBackendDevice **coldplug_list; /* Avoid needless reprobing during init */
#else
gboolean redirecting; /* Handled by GUdevClient in the gudev case */
- libusb_hotplug_callback_handle hp_handle;
#endif
#ifdef G_OS_WIN32
usbdk_api_wrapper *usbdk_api;
@@ -139,6 +144,7 @@ enum {
#ifdef USE_USBREDIR
+// this is the structure behind SpiceUsbDevice
typedef struct _SpiceUsbDeviceInfo {
guint8 busnum;
guint8 devaddr;
@@ -148,7 +154,7 @@ typedef struct _SpiceUsbDeviceInfo {
#ifdef G_OS_WIN32
guint8 state;
#else
- libusb_device *libdev;
+ SpiceUsbBackendDevice *bdev;
#endif
gint ref;
} SpiceUsbDeviceInfo;
@@ -166,15 +172,13 @@ static void spice_usb_device_manager_uevent_cb(GUdevClient *client,
static void spice_usb_device_manager_add_udev(SpiceUsbDeviceManager *self,
GUdevDevice *udev);
#else
-static int spice_usb_device_manager_hotplug_cb(libusb_context *ctx,
- libusb_device *device,
- libusb_hotplug_event event,
- void *data);
+static void spice_usb_device_manager_hotplug_cb(
+ void *data,
+ SpiceUsbBackendDevice *bdev,
+ gboolean added);
#endif
-static void spice_usb_device_manager_check_redir_on_connect(
- SpiceUsbDeviceManager *self, SpiceChannel *channel);
-static SpiceUsbDeviceInfo *spice_usb_device_new(libusb_device *libdev);
+static SpiceUsbDeviceInfo *spice_usb_device_new(SpiceUsbBackendDevice *bdev);
static SpiceUsbDevice *spice_usb_device_ref(SpiceUsbDevice *device);
static void spice_usb_device_unref(SpiceUsbDevice *device);
@@ -183,11 +187,11 @@ static void _usbdk_hider_update(SpiceUsbDeviceManager *manager);
static void _usbdk_hider_clear(SpiceUsbDeviceManager *manager);
#endif
-static gboolean spice_usb_manager_device_equal_libdev(SpiceUsbDeviceManager *manager,
+static gboolean spice_usb_manager_device_equal_bdev(SpiceUsbDeviceManager *manager,
SpiceUsbDevice *device,
- libusb_device *libdev);
-static libusb_device *
-spice_usb_device_manager_device_to_libdev(SpiceUsbDeviceManager *self,
+ SpiceUsbBackendDevice *bdev);
+static SpiceUsbBackendDevice*
+spice_usb_device_manager_device_to_bdev(SpiceUsbDeviceManager *self,
SpiceUsbDevice *device);
static void
@@ -205,6 +209,9 @@ static
void disconnect_device_sync(SpiceUsbDeviceManager *self,
SpiceUsbDevice *device);
+static
+void on_device_change(void *self, SpiceUsbBackendDevice *bdev);
+
G_DEFINE_BOXED_TYPE(SpiceUsbDevice, spice_usb_device,
(GBoxedCopyFunc)spice_usb_device_ref,
(GBoxedFreeFunc)spice_usb_device_unref)
@@ -288,26 +295,17 @@ static gboolean spice_usb_device_manager_initable_init(GInitable *initable,
SpiceUsbDeviceManagerPrivate *priv = self->priv;
GList *list;
GList *it;
- int rc;
#ifdef USE_GUDEV
const gchar *const subsystems[] = {"usb", NULL};
#endif
- /* Initialize libusb */
- rc = libusb_init(&priv->context);
- if (rc < 0) {
- 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);
+ /* Initialize spice backend */
+ priv->context = spice_usb_backend_initialize();
+ if (!priv->context) {
return FALSE;
}
-
-#ifdef G_OS_WIN32
-#if LIBUSB_API_VERSION >= 0x01000106
- libusb_set_option(priv->context, LIBUSB_OPTION_USE_USBDK);
-#endif
-#endif
+ spice_usb_backend_set_device_change_callback(priv->context,
+ self, on_device_change);
/* Start listening for usb devices plug / unplug */
#ifdef USE_GUDEV
@@ -319,26 +317,20 @@ static gboolean spice_usb_device_manager_initable_init(GInitable *initable,
g_signal_connect(G_OBJECT(priv->udev), "uevent",
G_CALLBACK(spice_usb_device_manager_uevent_cb), self);
/* Do coldplug (detection of already connected devices) */
- libusb_get_device_list(priv->context, &priv->coldplug_list);
+ priv->coldplug_list = spice_usb_backend_get_device_list(priv->context);
list = g_udev_client_query_by_subsystem(priv->udev, "usb");
for (it = g_list_first(list); it; it = g_list_next(it)) {
spice_usb_device_manager_add_udev(self, it->data);
g_object_unref(it->data);
}
g_list_free(list);
- libusb_free_device_list(priv->coldplug_list, 1);
+ spice_usb_backend_free_device_list(priv->coldplug_list);
priv->coldplug_list = NULL;
#else
- rc = libusb_hotplug_register_callback(priv->context,
- LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT,
- LIBUSB_HOTPLUG_ENUMERATE, LIBUSB_HOTPLUG_MATCH_ANY,
- LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY,
- spice_usb_device_manager_hotplug_cb, self, &priv->hp_handle);
- if (rc < 0) {
- const char *desc = spice_usbutil_libusb_strerror(rc);
- g_warning("Error initializing USB hotplug support: %s [%i]", desc, rc);
+ if (!spice_usb_backend_handle_hotplug(priv->context,
+ self, spice_usb_device_manager_hotplug_cb)) {
g_set_error(err, SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
- "Error initializing USB hotplug support: %s [%i]", desc, rc);
+ "Error initializing USB hotplug support");
return FALSE;
}
spice_usb_device_manager_start_event_listening(self, NULL);
@@ -369,20 +361,20 @@ static void spice_usb_device_manager_dispose(GObject *gobject)
SpiceUsbDeviceManagerPrivate *priv = self->priv;
#ifdef USE_LIBUSB_HOTPLUG
- if (priv->hp_handle) {
- spice_usb_device_manager_stop_event_listening(self);
- if (g_atomic_int_get(&priv->event_thread_run)) {
- /* Force termination of the event thread even if there were some
- * mismatched spice_usb_device_manager_{start,stop}_event_listening
- * calls. Otherwise, the usb event thread will be leaked, and will
- * try to use the libusb context we destroy in finalize(), which would
- * cause a crash */
- g_warn_if_reached();
- g_atomic_int_set(&priv->event_thread_run, FALSE);
- }
- /* This also wakes up the libusb_handle_events() in the event_thread */
- libusb_hotplug_deregister_callback(priv->context, priv->hp_handle);
- priv->hp_handle = 0;
+ // TODO: check in case the initial spice_usb_backend_handle_hotplug fails
+
+ spice_usb_device_manager_stop_event_listening(self);
+ if (g_atomic_int_get(&priv->event_thread_run)) {
+ /* Force termination of the event thread even if there were some
+ * mismatched spice_usb_device_manager_{start,stop}_event_listening
+ * calls. Otherwise, the usb event thread will be leaked, and will
+ * try to use the libusb context we destroy in finalize(), which would
+ * cause a crash */
+ g_warn_if_reached();
+ g_atomic_int_set(&priv->event_thread_run, FALSE);
+
+ /* This also wakes up the libusb_handle_events() in the event_thread */
+ spice_usb_backend_handle_hotplug(priv->context, NULL, NULL);
}
#endif
if (priv->event_thread) {
@@ -411,8 +403,9 @@ static void spice_usb_device_manager_finalize(GObject *gobject)
g_clear_object(&priv->udev);
#endif
g_return_if_fail(priv->event_thread == NULL);
- if (priv->context)
- libusb_exit(priv->context);
+ if (priv->context) {
+ spice_usb_backend_finalize(priv->context);
+ }
free(priv->auto_conn_filter_rules);
free(priv->redirect_on_connect_rules);
#ifdef G_OS_WIN32
@@ -455,6 +448,10 @@ static void spice_usb_device_manager_get_property(GObject *gobject,
case PROP_REDIRECT_ON_CONNECT:
g_value_set_string(value, priv->redirect_on_connect);
break;
+ case PROP_SHARE_CD:
+ /* get_property is not needed */
+ g_value_set_string(value, "");
+ break;
case PROP_FREE_CHANNELS: {
int free_channels = 0;
#ifdef USE_USBREDIR
@@ -545,6 +542,20 @@ static void spice_usb_device_manager_set_property(GObject *gobject,
priv->redirect_on_connect = g_strdup(filter);
break;
}
+ case PROP_SHARE_CD:
+ {
+#ifdef USE_USBREDIR
+ SpiceUsbDeviceLunInfo info = { 0 };
+ const gchar *name = g_value_get_string(value);
+ /* the string is temporary, no need to keep it */
+ SPICE_DEBUG("share_cd set to %s", name);
+ info.started = TRUE;
+ info.loaded = TRUE;
+ info.file_path = name;
+ spice_usb_backend_add_cd_lun(priv->context, &info);
+#endif
+ break;
+ }
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, pspec);
break;
@@ -636,6 +647,18 @@ static void spice_usb_device_manager_class_init(SpiceUsbDeviceManagerClass *klas
pspec);
/**
+ * SpiceUsbDeviceManager:share-cd:
+ *
+ * Set a string specifying a filename (ISO) or physical CD/DVD device
+ * to share via USB after a Spice connection has been established.
+ *
+ */
+ pspec = g_param_spec_string("share-cd", "Share ISO file or device as CD",
+ "File or device name to share", NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+ g_object_class_install_property(gobject_class, PROP_SHARE_CD, pspec);
+
+ /**
* SpiceUsbDeviceManager:free-channels:
*
* Get the number of available channels for redirecting USB devices.
@@ -732,12 +755,32 @@ static void spice_usb_device_manager_class_init(SpiceUsbDeviceManagerClass *klas
2,
SPICE_TYPE_USB_DEVICE,
G_TYPE_ERROR);
+
+ /**
+ * SpiceUsbDeviceManager::device-changed:
+ * @manager: #SpiceUsbDeviceManager that emitted the signal
+ * @device: #SpiceUsbDevice boxed object corresponding to the device which was changed
+ *
+ * The #SpiceUsbDeviceManager::device-changed signal is emitted whenever
+ * the change happens with one or more logical CD units of the device.
+ * Applicable only to emulated CD sharing devices
+ **/
+ signals[DEVICE_CHANGED] =
+ g_signal_new("device-changed",
+ G_OBJECT_CLASS_TYPE(gobject_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET(SpiceUsbDeviceManagerClass, device_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__BOXED,
+ G_TYPE_NONE,
+ 1,
+ SPICE_TYPE_USB_DEVICE);
}
#ifdef USE_USBREDIR
/* ------------------------------------------------------------------ */
-/* gudev / libusb Helper functions */
+/* gudev / backend Helper functions */
#ifdef USE_GUDEV
static gboolean spice_usb_device_manager_get_udev_bus_n_address(
@@ -761,40 +804,16 @@ static gboolean spice_usb_device_manager_get_udev_bus_n_address(
}
#endif
-static gboolean spice_usb_device_manager_get_device_descriptor(
- libusb_device *libdev,
- struct libusb_device_descriptor *desc)
-{
- int errcode;
- const gchar *errstr;
-
- g_return_val_if_fail(libdev != NULL, FALSE);
- g_return_val_if_fail(desc != NULL, FALSE);
-
- errcode = libusb_get_device_descriptor(libdev, desc);
- if (errcode < 0) {
- int bus, addr;
-
- bus = libusb_get_bus_number(libdev);
- addr = libusb_get_device_address(libdev);
- errstr = spice_usbutil_libusb_strerror(errcode);
- g_warning("cannot get device descriptor for (%p) %d.%d -- %s(%d)",
- libdev, bus, addr, errstr, errcode);
- return FALSE;
- }
- return TRUE;
-}
-
#endif // USE_USBREDIR
/**
* spice_usb_device_get_libusb_device:
- * @device: #SpiceUsbDevice to get the descriptor information of
+ * @device: #SpiceUsbDevice to get the libusb device of (if exists)
*
* Finds the %libusb_device associated with the @device.
*
- * Returns: (transfer none): the %libusb_device associated to %SpiceUsbDevice.
- *
+ * Returns: (transfer none): the %libusb_device associated to %SpiceUsbDevice
+ * or NULL (if the device does not have associated libusb device)
* Since: 0.27
**/
gconstpointer
@@ -806,34 +825,13 @@ spice_usb_device_get_libusb_device(const SpiceUsbDevice *device G_GNUC_UNUSED)
g_return_val_if_fail(info != NULL, FALSE);
- return info->libdev;
+ return spice_usb_backend_device_get_libdev(info->bdev);
#endif
#endif
return NULL;
}
#ifdef USE_USBREDIR
-static gboolean spice_usb_device_manager_get_libdev_vid_pid(
- libusb_device *libdev, int *vid, int *pid)
-{
- struct libusb_device_descriptor desc;
-
- g_return_val_if_fail(libdev != NULL, FALSE);
- g_return_val_if_fail(vid != NULL, FALSE);
- g_return_val_if_fail(pid != NULL, FALSE);
-
- *vid = *pid = 0;
-
- if (!spice_usb_device_manager_get_device_descriptor(libdev, &desc)) {
- return FALSE;
- }
- *vid = desc.idVendor;
- *pid = desc.idProduct;
-
- return TRUE;
-}
-
-/* ------------------------------------------------------------------ */
/* callbacks */
static void channel_new(SpiceSession *session, SpiceChannel *channel,
@@ -849,10 +847,8 @@ static void channel_new(SpiceSession *session, SpiceChannel *channel,
spice_channel_connect(channel);
g_ptr_array_add(self->priv->channels, channel);
- spice_usb_device_manager_check_redir_on_connect(self, channel);
-
/*
- * add a reference to ourself, to make sure the libusb context is
+ * add a reference to ourself, to make sure the backend device context is
* alive as long as the channel is.
* TODO: moving to gusb could help here too.
*/
@@ -889,6 +885,9 @@ static void spice_usb_device_manager_auto_connect_cb(GObject *gobject,
g_signal_emit(self, signals[AUTO_CONNECT_FAILED], 0, device, err);
g_error_free(err);
}
+ /* let widget update itself */
+ g_signal_emit(self, signals[DEVICE_CHANGED], 0, device);
+
spice_usb_device_unref(device);
}
@@ -902,12 +901,12 @@ spice_usb_device_manager_device_match(SpiceUsbDeviceManager *self, SpiceUsbDevic
#ifdef USE_GUDEV
static gboolean
-spice_usb_device_manager_libdev_match(SpiceUsbDeviceManager *self, libusb_device *libdev,
+spice_usb_device_manager_bdev_match(SpiceUsbDeviceManager *self, SpiceUsbBackendDevice *dev,
const int bus, const int address)
{
+ const UsbDeviceInformation* info = spice_usb_backend_device_get_info(dev);
/* match functions for Linux/UsbDk -- match by bus.addr */
- return (libusb_get_bus_number(libdev) == bus &&
- libusb_get_device_address(libdev) == address);
+ return (info->bus == bus && info->address == address);
}
#endif
@@ -929,36 +928,36 @@ spice_usb_device_manager_find_device(SpiceUsbDeviceManager *self,
return device;
}
-static void spice_usb_device_manager_add_dev(SpiceUsbDeviceManager *self,
- libusb_device *libdev)
+static void spice_usb_device_manager_add_dev(
+ SpiceUsbDeviceManager *self,
+ SpiceUsbBackendDevice *bdev)
{
SpiceUsbDeviceManagerPrivate *priv = self->priv;
- struct libusb_device_descriptor desc;
SpiceUsbDevice *device;
-
- if (!spice_usb_device_manager_get_device_descriptor(libdev, &desc))
- return;
+ const UsbDeviceInformation* info = spice_usb_backend_device_get_info(bdev);
+ // try redirecting shared CD on creation, if filter allows
+ gboolean always_redirect = info->max_luns != 0;
/* Skip hubs */
- if (desc.bDeviceClass == LIBUSB_CLASS_HUB)
+ if (spice_usb_backend_device_is_hub(bdev))
return;
- device = (SpiceUsbDevice*)spice_usb_device_new(libdev);
+ device = (SpiceUsbDevice*)spice_usb_device_new(bdev);
if (!device)
return;
g_ptr_array_add(priv->devices, device);
- if (priv->auto_connect) {
+ if (priv->auto_connect || always_redirect) {
gboolean can_redirect, auto_ok;
can_redirect = spice_usb_device_manager_can_redirect_device(
self, device, NULL);
- auto_ok = usbredirhost_check_device_filter(
- priv->auto_conn_filter_rules,
- priv->auto_conn_filter_rules_count,
- libdev, 0) == 0;
+ auto_ok = spice_usb_backend_device_check_filter(
+ bdev,
+ priv->auto_conn_filter_rules,
+ priv->auto_conn_filter_rules_count) == 0;
if (can_redirect && auto_ok)
spice_usb_device_manager_connect_device_async(self,
@@ -1005,7 +1004,7 @@ static void spice_usb_device_manager_add_udev(SpiceUsbDeviceManager *self,
GUdevDevice *udev)
{
SpiceUsbDeviceManagerPrivate *priv = self->priv;
- libusb_device *libdev = NULL, **dev_list = NULL;
+ SpiceUsbBackendDevice *devarrived = NULL, **dev_list = NULL;
SpiceUsbDevice *device;
const gchar *devtype;
int i, bus, address;
@@ -1033,23 +1032,23 @@ static void spice_usb_device_manager_add_udev(SpiceUsbDeviceManager *self,
if (priv->coldplug_list)
dev_list = priv->coldplug_list;
else
- libusb_get_device_list(priv->context, &dev_list);
+ dev_list = spice_usb_backend_get_device_list(priv->context);
for (i = 0; dev_list && dev_list[i]; i++) {
- if (spice_usb_device_manager_libdev_match(self, dev_list[i], bus, address)) {
- libdev = dev_list[i];
+ if (spice_usb_device_manager_bdev_match(self, dev_list[i], bus, address)) {
+ devarrived = dev_list[i];
break;
}
}
- if (libdev)
- spice_usb_device_manager_add_dev(self, libdev);
+ if (devarrived)
+ spice_usb_device_manager_add_dev(self, devarrived);
else
g_warning("Could not find USB device to add " DEV_ID_FMT,
(guint) bus, (guint) address);
if (!priv->coldplug_list)
- libusb_free_device_list(dev_list, 1);
+ spice_usb_backend_free_device_list(dev_list);
}
static void spice_usb_device_manager_remove_udev(SpiceUsbDeviceManager *self,
@@ -1078,8 +1077,8 @@ static void spice_usb_device_manager_uevent_cb(GUdevClient *client,
#else
struct hotplug_idle_cb_args {
SpiceUsbDeviceManager *self;
- libusb_device *device;
- libusb_hotplug_event event;
+ SpiceUsbBackendDevice *device;
+ gboolean device_added;
};
static gboolean spice_usb_device_manager_hotplug_idle_cb(gpointer user_data)
@@ -1087,36 +1086,34 @@ static gboolean spice_usb_device_manager_hotplug_idle_cb(gpointer user_data)
struct hotplug_idle_cb_args *args = user_data;
SpiceUsbDeviceManager *self = SPICE_USB_DEVICE_MANAGER(args->self);
- switch (args->event) {
- case LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED:
+ if (args->device_added) {
spice_usb_device_manager_add_dev(self, args->device);
- break;
- case LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT:
+ } else {
+ const UsbDeviceInformation *info = spice_usb_backend_device_get_info(args->device);
spice_usb_device_manager_remove_dev(self,
- libusb_get_bus_number(args->device),
- libusb_get_device_address(args->device));
- break;
+ info->bus,
+ info->address);
}
- libusb_unref_device(args->device);
+ spice_usb_backend_device_release(args->device);
g_object_unref(self);
g_free(args);
return FALSE;
}
/* Can be called from both the main-thread as well as the event_thread */
-static int spice_usb_device_manager_hotplug_cb(libusb_context *ctx,
- libusb_device *device,
- libusb_hotplug_event event,
- void *user_data)
+static void spice_usb_device_manager_hotplug_cb(
+ void *user_data,
+ SpiceUsbBackendDevice *bdev,
+ gboolean added)
{
SpiceUsbDeviceManager *self = SPICE_USB_DEVICE_MANAGER(user_data);
struct hotplug_idle_cb_args *args = g_malloc0(sizeof(*args));
args->self = g_object_ref(self);
- args->device = libusb_ref_device(device);
- args->event = event;
+ spice_usb_backend_device_acquire(bdev);
+ args->device_added = added;
+ args->device = bdev;
g_idle_add(spice_usb_device_manager_hotplug_idle_cb, args);
- return 0;
}
#endif // USE_USBREDIR
@@ -1143,13 +1140,9 @@ static gpointer spice_usb_device_manager_usb_ev_thread(gpointer user_data)
{
SpiceUsbDeviceManager *self = SPICE_USB_DEVICE_MANAGER(user_data);
SpiceUsbDeviceManagerPrivate *priv = self->priv;
- int rc;
while (g_atomic_int_get(&priv->event_thread_run)) {
- rc = libusb_handle_events(priv->context);
- if (rc && rc != LIBUSB_ERROR_INTERRUPTED) {
- const char *desc = spice_usbutil_libusb_strerror(rc);
- g_warning("Error handling USB events: %s [%i]", desc, rc);
+ if (!spice_usb_backend_handle_events(priv->context)) {
break;
}
}
@@ -1194,13 +1187,13 @@ void spice_usb_device_manager_stop_event_listening(
g_atomic_int_set(&priv->event_thread_run, FALSE);
}
-static void spice_usb_device_manager_check_redir_on_connect(
+void spice_usb_device_manager_check_redir_on_connect(
SpiceUsbDeviceManager *self, SpiceChannel *channel)
{
SpiceUsbDeviceManagerPrivate *priv = self->priv;
GTask *task;
SpiceUsbDevice *device;
- libusb_device *libdev;
+ SpiceUsbBackendDevice *dev;
guint i;
if (priv->redirect_on_connect == NULL)
@@ -1212,15 +1205,15 @@ static void spice_usb_device_manager_check_redir_on_connect(
if (spice_usb_device_manager_is_device_connected(self, device))
continue;
- libdev = spice_usb_device_manager_device_to_libdev(self, device);
+ dev = spice_usb_device_manager_device_to_bdev(self, device);
#ifdef G_OS_WIN32
- if (libdev == NULL)
+ if (dev == NULL)
continue;
#endif
- if (usbredirhost_check_device_filter(
- priv->redirect_on_connect_rules,
- priv->redirect_on_connect_rules_count,
- libdev, 0) == 0) {
+ if (spice_usb_backend_device_check_filter(
+ dev,
+ priv->redirect_on_connect_rules,
+ priv->redirect_on_connect_rules_count) == 0) {
/* Note: re-uses spice_usb_device_manager_connect_device_async's
completion handling code! */
task = g_task_new(self,
@@ -1230,14 +1223,14 @@ static void spice_usb_device_manager_check_redir_on_connect(
spice_usbredir_channel_connect_device_async(
SPICE_USBREDIR_CHANNEL(channel),
- libdev, device, NULL,
+ dev, device, NULL,
spice_usb_device_manager_channel_connect_cb,
task);
- libusb_unref_device(libdev);
+ spice_usb_backend_device_release(dev);
return; /* We've taken the channel! */
}
- libusb_unref_device(libdev);
+ spice_usb_backend_device_release(dev);
}
}
@@ -1261,8 +1254,8 @@ static SpiceUsbredirChannel *spice_usb_device_manager_get_channel_for_dev(
for (i = 0; i < priv->channels->len; i++) {
SpiceUsbredirChannel *channel = g_ptr_array_index(priv->channels, i);
spice_usbredir_channel_lock(channel);
- libusb_device *libdev = spice_usbredir_channel_get_device(channel);
- if (spice_usb_manager_device_equal_libdev(manager, device, libdev)) {
+ SpiceUsbBackendDevice *dev = spice_usbredir_channel_get_device(channel);
+ if (spice_usb_manager_device_equal_bdev(manager, device, dev)) {
spice_usbredir_channel_unlock(channel);
return channel;
}
@@ -1319,13 +1312,13 @@ GPtrArray* spice_usb_device_manager_get_devices_with_filter(
SpiceUsbDevice *device = g_ptr_array_index(priv->devices, i);
if (rules) {
- libusb_device *libdev =
- spice_usb_device_manager_device_to_libdev(self, device);
+ SpiceUsbBackendDevice *bdev =
+ spice_usb_device_manager_device_to_bdev(self, device);
#ifdef G_OS_WIN32
- if (libdev == NULL)
+ if (bdev == NULL)
continue;
#endif
- if (usbredirhost_check_device_filter(rules, count, libdev, 0) != 0)
+ if (spice_usb_backend_device_check_filter(bdev, rules, count) != 0)
continue;
}
g_ptr_array_add(devices_copy, spice_usb_device_ref(device));
@@ -1399,7 +1392,7 @@ _spice_usb_device_manager_connect_device_async(SpiceUsbDeviceManager *self,
task = g_task_new(self, cancellable, callback, user_data);
SpiceUsbDeviceManagerPrivate *priv = self->priv;
- libusb_device *libdev;
+ SpiceUsbBackendDevice *bdev;
guint i;
if (spice_usb_device_manager_is_device_connected(self, device)) {
@@ -1415,9 +1408,9 @@ _spice_usb_device_manager_connect_device_async(SpiceUsbDeviceManager *self,
if (spice_usbredir_channel_get_device(channel))
continue; /* Skip already used channels */
- libdev = spice_usb_device_manager_device_to_libdev(self, device);
+ bdev = spice_usb_device_manager_device_to_bdev(self, device);
#ifdef G_OS_WIN32
- if (libdev == NULL) {
+ if (bdev == NULL) {
/* Most likely, the device was plugged out at driver installation
* time, and its remove-device event was ignored.
* So remove the device now
@@ -1435,12 +1428,12 @@ _spice_usb_device_manager_connect_device_async(SpiceUsbDeviceManager *self,
}
#endif
spice_usbredir_channel_connect_device_async(channel,
- libdev,
+ bdev,
device,
cancellable,
spice_usb_device_manager_channel_connect_cb,
task);
- libusb_unref_device(libdev);
+ spice_usb_backend_device_release(bdev);
return;
}
@@ -1702,20 +1695,20 @@ spice_usb_device_manager_can_redirect_device(SpiceUsbDeviceManager *self,
if (guest_filter_rules) {
gboolean filter_ok;
- libusb_device *libdev;
+ SpiceUsbBackendDevice *bdev;
- libdev = spice_usb_device_manager_device_to_libdev(self, device);
+ bdev = spice_usb_device_manager_device_to_bdev(self, device);
#ifdef G_OS_WIN32
- if (libdev == NULL) {
+ if (bdev == NULL) {
g_set_error_literal(err, SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
_("Some USB devices were not found"));
return FALSE;
}
#endif
- filter_ok = (usbredirhost_check_device_filter(
- guest_filter_rules, guest_filter_rules_count,
- libdev, 0) == 0);
- libusb_unref_device(libdev);
+ filter_ok = (spice_usb_backend_device_check_filter(
+ bdev,
+ guest_filter_rules, guest_filter_rules_count) == 0);
+ spice_usb_backend_device_release(bdev);
if (!filter_ok) {
g_set_error_literal(err, SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
_("Some USB devices are blocked by host policy"));
@@ -1774,6 +1767,7 @@ gchar *spice_usb_device_get_description(SpiceUsbDevice *device, const gchar *for
#ifdef USE_USBREDIR
guint16 bus, address, vid, pid;
gchar *description, *descriptor, *manufacturer = NULL, *product = NULL;
+ UsbDeviceInformation info;
g_return_val_if_fail(device != NULL, NULL);
@@ -1788,8 +1782,13 @@ gchar *spice_usb_device_get_description(SpiceUsbDevice *device, const gchar *for
descriptor = g_strdup("");
}
- spice_usb_util_get_device_strings(bus, address, vid, pid,
- &manufacturer, &product);
+ if (spice_usb_backend_device_get_info_by_address(bus, address, &info) && info.max_luns) {
+ manufacturer = g_strdup(CD_SHARE_VENDOR);
+ product = g_strdup(CD_SHARE_PRODUCT);
+ } else {
+ spice_usb_util_get_device_strings(bus, address, vid, pid,
+ &manufacturer, &product, TRUE);
+ }
if (!format)
format = _("%s %s %s at %d-%d");
@@ -1806,64 +1805,58 @@ gchar *spice_usb_device_get_description(SpiceUsbDevice *device, const gchar *for
#endif
}
-
-
-#ifdef USE_USBREDIR
-static gboolean probe_isochronous_endpoint(libusb_device *libdev)
+void spice_usb_device_get_info(SpiceUsbDeviceManager *self,
+ SpiceUsbDevice *device,
+ SpiceUsbDeviceDescription *info)
{
- struct libusb_config_descriptor *conf_desc;
- gboolean isoc_found = FALSE;
- gint i, j, k;
-
- g_return_val_if_fail(libdev != NULL, FALSE);
-
- if (libusb_get_active_config_descriptor(libdev, &conf_desc) != 0) {
- g_return_val_if_reached(FALSE);
+#ifdef USE_USBREDIR
+ g_return_if_fail(device != NULL);
+ info->vendor = info->product;
+ info->bus = spice_usb_device_get_busnum(device);
+ info->address = spice_usb_device_get_devaddr(device);
+ info->vendor_id = spice_usb_device_get_vid(device);
+ info->product_id = spice_usb_device_get_pid(device);
+
+ if (spice_usb_device_manager_is_device_cd(self, device)) {
+ info->vendor = g_strdup(CD_SHARE_VENDOR);
+ info->product = g_strdup(CD_SHARE_PRODUCT);
+ return;
}
-
- for (i = 0; !isoc_found && i < conf_desc->bNumInterfaces; i++) {
- for (j = 0; !isoc_found && j < conf_desc->interface[i].num_altsetting; j++) {
- for (k = 0; !isoc_found && k < conf_desc->interface[i].altsetting[j].bNumEndpoints;k++) {
- gint attributes = conf_desc->interface[i].altsetting[j].endpoint[k].bmAttributes;
- gint type = attributes & LIBUSB_TRANSFER_TYPE_MASK;
- if (type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS)
- isoc_found = TRUE;
- }
- }
+ spice_usb_util_get_device_strings(info->bus, info->address,
+ info->vendor_id, info->product_id, &info->vendor, &info->product, FALSE);
+ if (!info->vendor) {
+ info->vendor = g_strdup_printf("[%04X]", info->vendor_id);
}
-
- libusb_free_config_descriptor(conf_desc);
- return isoc_found;
+ if (!info->product) {
+ info->product = g_strdup_printf("[%04X]", info->product_id);
+ }
+#endif
}
+#ifdef USE_USBREDIR
+
/*
* SpiceUsbDeviceInfo
*/
-static SpiceUsbDeviceInfo *spice_usb_device_new(libusb_device *libdev)
+static SpiceUsbDeviceInfo *spice_usb_device_new(SpiceUsbBackendDevice *bdev)
{
SpiceUsbDeviceInfo *info;
- int vid, pid;
- guint8 bus, addr;
+ const UsbDeviceInformation *devinfo;
- g_return_val_if_fail(libdev != NULL, NULL);
-
- bus = libusb_get_bus_number(libdev);
- addr = libusb_get_device_address(libdev);
-
- if (!spice_usb_device_manager_get_libdev_vid_pid(libdev, &vid, &pid)) {
- return NULL;
- }
+ g_return_val_if_fail(bdev != NULL, NULL);
+ devinfo = spice_usb_backend_device_get_info(bdev);
info = g_new0(SpiceUsbDeviceInfo, 1);
- info->busnum = bus;
- info->devaddr = addr;
- info->vid = vid;
- info->pid = pid;
+ info->busnum = devinfo->bus;
+ info->devaddr = devinfo->address;
+ info->vid = devinfo->vid;
+ info->pid = devinfo->pid;
info->ref = 1;
- info->isochronous = probe_isochronous_endpoint(libdev);
+ info->isochronous = devinfo->isochronous;
#ifndef G_OS_WIN32
- info->libdev = libusb_ref_device(libdev);
+ info->bdev = bdev;
+ spice_usb_backend_device_acquire(bdev);
#endif
return info;
@@ -2001,49 +1994,51 @@ static void spice_usb_device_unref(SpiceUsbDevice *device)
ref_count_is_0 = g_atomic_int_dec_and_test(&info->ref);
if (ref_count_is_0) {
#ifndef G_OS_WIN32
- libusb_unref_device(info->libdev);
+ spice_usb_backend_device_release(info->bdev);
#endif
+ info->vid = info->pid = 0;
+ SPICE_DEBUG("%s: deleting %p", __FUNCTION__, info);
g_free(info);
}
}
#ifndef G_OS_WIN32 /* Linux -- directly compare libdev */
static gboolean
-spice_usb_manager_device_equal_libdev(SpiceUsbDeviceManager *manager,
+spice_usb_manager_device_equal_bdev(SpiceUsbDeviceManager *manager,
SpiceUsbDevice *device,
- libusb_device *libdev)
+ SpiceUsbBackendDevice *bdev)
{
SpiceUsbDeviceInfo *info = (SpiceUsbDeviceInfo *)device;
- if ((device == NULL) || (libdev == NULL))
+ if ((device == NULL) || (bdev == NULL))
return FALSE;
- return info->libdev == libdev;
+ return spice_usb_backend_devices_same(info->bdev, bdev);
}
#else /* Windows -- compare vid:pid of device and libdev */
static gboolean
-spice_usb_manager_device_equal_libdev(SpiceUsbDeviceManager *manager,
- SpiceUsbDevice *device,
- libusb_device *libdev)
+spice_usb_manager_device_equal_bdev(SpiceUsbDeviceManager *manager,
+ SpiceUsbDevice *device,
+ SpiceUsbBackendDevice *bdev)
{
int busnum, devaddr;
- if ((device == NULL) || (libdev == NULL))
+ if ((device == NULL) || (bdev == NULL))
return FALSE;
busnum = spice_usb_device_get_busnum(device);
devaddr = spice_usb_device_get_devaddr(device);
- return spice_usb_device_manager_libdev_match(manager, libdev,
+ return spice_usb_device_manager_bdev_match(manager, bdev,
busnum, devaddr);
}
#endif
/*
- * Caller must libusb_unref_device the libusb_device returned by this function.
- * Returns a libusb_device, or NULL upon failure
+ * Caller must spice_usb_backend_device_release the SpiceUsbBackendDevice returned by this function.
+ * Returns a SpiceUsbBackendDevice, or NULL upon failure
*/
-static libusb_device *
-spice_usb_device_manager_device_to_libdev(SpiceUsbDeviceManager *self,
+static SpiceUsbBackendDevice *
+spice_usb_device_manager_device_to_bdev(SpiceUsbDeviceManager *self,
SpiceUsbDevice *device)
{
#ifdef G_OS_WIN32
@@ -2054,7 +2049,7 @@ spice_usb_device_manager_device_to_libdev(SpiceUsbDeviceManager *self,
* driver swap we do under windows invalidates the cached libdev.
*/
- libusb_device *d, **devlist;
+ SpiceUsbBackendDevice *d, **devlist;
int i;
g_return_val_if_fail(SPICE_IS_USB_DEVICE_MANAGER(self), NULL);
@@ -2062,18 +2057,18 @@ spice_usb_device_manager_device_to_libdev(SpiceUsbDeviceManager *self,
g_return_val_if_fail(self->priv != NULL, NULL);
g_return_val_if_fail(self->priv->context != NULL, NULL);
- libusb_get_device_list(self->priv->context, &devlist);
+ devlist = spice_usb_backend_get_device_list(self->priv->context);
if (!devlist)
return NULL;
for (i = 0; (d = devlist[i]) != NULL; i++) {
- if (spice_usb_manager_device_equal_libdev(self, device, d)) {
- libusb_ref_device(d);
+ if (spice_usb_manager_device_equal_bdev(self, device, d)) {
+ spice_usb_backend_device_acquire(d);
break;
}
}
- libusb_free_device_list(devlist, 1);
+ spice_usb_backend_free_device_list(devlist);
return d;
@@ -2081,7 +2076,257 @@ spice_usb_device_manager_device_to_libdev(SpiceUsbDeviceManager *self,
/* Simply return a ref to the cached libdev */
SpiceUsbDeviceInfo *info = (SpiceUsbDeviceInfo *)device;
- return libusb_ref_device(info->libdev);
+ spice_usb_backend_device_acquire(info->bdev);
+ return info->bdev;
#endif
}
+
+static void on_device_change(void *user_data, SpiceUsbBackendDevice *bdev)
+{
+ SpiceUsbDeviceManager *self = user_data;
+ const UsbDeviceInformation *info = spice_usb_backend_device_get_info(bdev);
+ SpiceUsbDevice *device = spice_usb_device_manager_find_device(self, info->bus, info->address);
+ if (device) {
+ SPICE_DEBUG("%s dev:%u:%u", __FUNCTION__, info->bus, info->address);
+ g_signal_emit(self, signals[DEVICE_CHANGED], 0, device);
+ } else {
+ SPICE_DEBUG("%s %u:%u not found in usb device manager", __FUNCTION__, info->bus, info->address);
+ }
+}
+
+/**
+ * spice_usb_device_manager_is_device_cd:
+ * @self: the #SpiceUsbDeviceManager manager
+ * @device: #SpiceUsbDevice to query whether it is shared CD
+ *
+ * Checks whether specified #SpiceUsbDevice is shared CD device.
+ *
+ * Returns: (transfer none): maximal possible number of logical units
+ * on the device or 0 if the device is not shared CD
+ *
+ * Since: 0.35
+ **/
+guint spice_usb_device_manager_is_device_cd(SpiceUsbDeviceManager *self,
+ SpiceUsbDevice *device)
+{
+ guint val = 0;
+ SpiceUsbBackendDevice *bdev = spice_usb_device_manager_device_to_bdev(self, device);
+ if (bdev) {
+ const UsbDeviceInformation *info = spice_usb_backend_device_get_info(bdev);
+ val = info->max_luns;
+ spice_usb_backend_device_release(bdev);
+ }
+ return val;
+}
+
+/**
+ * spice_usb_device_manager_add_cd_lun:
+ * @self: the #SpiceUsbDeviceManager manager
+ * @lun_info: structure containing data for logical
+ * unit to create
+ *
+ * Creates new shared CD device with logical CD unit on it
+ * (emits %device-added signal) or
+ * adds logical unit to existing device
+ * (emits %device-changed signal)
+ *
+ * Returns: (transfer none): TRUE if new logical unit was created,
+ * otherwise FALSE
+ *
+ * Since: 0.35
+ **/
+gboolean spice_usb_device_manager_add_cd_lun(SpiceUsbDeviceManager *self,
+ SpiceUsbDeviceLunInfo *lun_info)
+{
+ return spice_usb_backend_add_cd_lun(self->priv->context, lun_info);
+}
+
+/**
+ * spice_usb_device_manager_device_lun_remove:
+ * @self: the #SpiceUsbDeviceManager manager
+ * @device: #SpiceUsbDevice to remove the logical CD unit from
+ * @lun: index of logical unit to remove
+ *
+ * Removes specified logical CD unit from specified shared CD device
+ * (emits %device-changed signal)
+ * If after LUN removal the device does not have any LUNs,
+ * removes the device too (emits %device-removed signal)
+ *
+ * Returns: (transfer none): TRUE if the device is shared CD and
+ * specified LUN exists, otherwise FALSE
+ *
+ * Since: 0.35
+ **/
+gboolean
+spice_usb_device_manager_device_lun_remove(SpiceUsbDeviceManager *self,
+ SpiceUsbDevice *device,
+ guint lun)
+{
+ gboolean b = FALSE;
+ SpiceUsbBackendDevice *bdev = spice_usb_device_manager_device_to_bdev(self, device);
+ if (bdev) {
+ b = spice_usb_backend_remove_cd_lun(self->priv->context, bdev, lun);
+ spice_usb_backend_device_release(bdev);
+ }
+ return b;
+}
+
+/**
+ * spice_usb_device_manager_device_lun_get_info:
+ * @self: the #SpiceUsbDeviceManager manager
+ * @device: #SpiceUsbDevice to get the logical CD unit info of
+ * @lun: index of logical unit to get the info of
+ * @lun_info: contains actual LUN information on output
+ *
+ * Retrieves information about specified logical CD unit on
+ * specified shared CD device.
+ *
+ * Returns: (transfer none): TRUE if the device is shared CD and
+ * specified LUN exists, otherwise FALSE
+ *
+ * Since: 0.35
+ **/
+gboolean
+spice_usb_device_manager_device_lun_get_info(SpiceUsbDeviceManager *self,
+ SpiceUsbDevice *device,
+ guint lun,
+ SpiceUsbDeviceLunInfo *lun_info)
+{
+ gboolean b = FALSE;
+ SpiceUsbBackendDevice *bdev = spice_usb_device_manager_device_to_bdev(self, device);
+ if (bdev) {
+ b = spice_usb_backend_get_cd_lun_info(bdev, lun, lun_info);
+ spice_usb_backend_device_release(bdev);
+ }
+ return b;
+}
+
+/**
+ * spice_usb_device_manager_device_lun_load:
+ * @self: the #SpiceUsbDeviceManager manager
+ * @device: #SpiceUsbDevice to load/unload logical CD unit on
+ * @lun: index of logical unit to load/unload media on
+ * @load: TRUE if the media shall be loaded
+ *
+ * Loads or unloads specified logical CD unit on
+ * specified shared CD device.
+ *
+ * Returns: (transfer none): TRUE if the device is shared CD,
+ * specified LUN exists and load/unload operation done,
+ * otherwise FALSE
+ *
+ * Since: 0.35
+ **/
+gboolean
+spice_usb_device_manager_device_lun_load(SpiceUsbDeviceManager *self,
+ SpiceUsbDevice *device,
+ guint lun,
+ gboolean load)
+{
+ gboolean b = FALSE;
+ SpiceUsbBackendDevice *bdev = spice_usb_device_manager_device_to_bdev(self, device);
+ if (bdev) {
+ b = spice_usb_backend_load_cd_lun(self->priv->context, bdev, lun, load);
+ spice_usb_backend_device_release(bdev);
+ }
+ return b;
+}
+
+/**
+ * spice_usb_device_manager_device_lun_change_media:
+ * @self: the #SpiceUsbDeviceManager manager
+ * @device: #SpiceUsbDevice to change the media in
+ * @lun: index of logical unit to change the media
+ * @lun_info: structure describing new media file path
+ *
+ * Changes path to the media file (usually ISO) in specified
+ * logical CD unit on specified shared CD device.
+ *
+ * Returns: (transfer none): TRUE if the device is shared CD,
+ * specified LUN exists and the media is ejected and
+ * new media files accessible,
+ * otherwise FALSE
+ *
+ * Since: 0.35
+ **/
+gboolean
+spice_usb_device_manager_device_lun_change_media(SpiceUsbDeviceManager *self,
+ SpiceUsbDevice *device,
+ guint lun,
+ const SpiceUsbDeviceLunInfo *lun_info)
+{
+ gboolean b = FALSE;
+ SpiceUsbBackendDevice *bdev = spice_usb_device_manager_device_to_bdev(self, device);
+ if (bdev) {
+ b = spice_usb_backend_change_cd_lun(self->priv->context, bdev, lun, lun_info);
+ spice_usb_backend_device_release(bdev);
+ }
+ return b;
+}
+
+/**
+ * spice_usb_device_manager_device_lun_lock:
+ * @self: the #SpiceUsbDeviceManager manager
+ * @device: #SpiceUsbDevice to lock or unlock
+ * @lun: index of logical unit to lock or unlock
+ * @lock: TRUE to lock, FALSE to unlock
+ *
+ * Locks or unlocks the media in specified logical CD unit.
+ * When the media is locked, the media can't be ejected.
+ *
+ * Returns: (transfer none): TRUE if the device is shared CD,
+ * specified LUN exists and the media is locked or unlocked successfully,
+ * otherwise FALSE
+ *
+ * Since: 0.35
+ **/
+gboolean
+spice_usb_device_manager_device_lun_lock(SpiceUsbDeviceManager *self,
+ SpiceUsbDevice *device,
+ guint lun,
+ gboolean lock)
+{
+ gboolean b = FALSE;
+ SpiceUsbBackendDevice *bdev = spice_usb_device_manager_device_to_bdev(self, device);
+ if (bdev) {
+ b = spice_usb_backend_lock_cd_lun(self->priv->context, bdev, lun, lock);
+ spice_usb_backend_device_release(bdev);
+ }
+ return b;
+}
+
+/**
+ * spice_usb_device_manager_get_device_luns:
+ * @self: the #SpiceUsbDeviceManager manager
+ * @device: #SpiceUsbDevice to get LUN indices for
+ *
+ * Returns array of integers, where each value contains
+ * index of logical CD unit on the device
+ *
+ * Returns: (element-type guint) (transfer full): a
+ * %GArray of %indices. If the device is not shared CD device,
+ * the array is empty.
+ *
+ * Since: 0.35
+ **/
+GArray *spice_usb_device_manager_get_device_luns(SpiceUsbDeviceManager *self,
+ SpiceUsbDevice *device)
+{
+ GArray *indices = g_array_new(FALSE, TRUE, sizeof(guint));
+ SpiceUsbBackendDevice *bdev = spice_usb_device_manager_device_to_bdev(self, device);
+ if (bdev) {
+ uint32_t value = spice_usb_backend_get_cd_luns_bitmask(bdev);
+ guint i = 0;
+ while (value) {
+ if (value & 1) {
+ g_array_append_val(indices, i);
+ }
+ value >>= 1;
+ i++;
+ }
+ spice_usb_backend_device_release(bdev);
+ }
+ return indices;
+}
+
#endif /* USE_USBREDIR */
diff --git a/src/usb-device-manager.h b/src/usb-device-manager.h
index 773208f..a6f8064 100644
--- a/src/usb-device-manager.h
+++ b/src/usb-device-manager.h
@@ -71,6 +71,7 @@ struct _SpiceUsbDeviceManager
* @device_removed: Signal class handler for the #SpiceUsbDeviceManager::device-removed signal.
* @auto_connect_failed: Signal class handler for the #SpiceUsbDeviceManager::auto-connect-failed signal.
* @device_error: Signal class handler for the #SpiceUsbDeviceManager::device_error signal.
+ * @device_changed: Signal class handler for the #SpiceUsbDeviceManager::device_changed signal.
*
* Class structure for #SpiceUsbDeviceManager.
*/
@@ -87,18 +88,52 @@ struct _SpiceUsbDeviceManagerClass
SpiceUsbDevice *device, GError *error);
void (*device_error) (SpiceUsbDeviceManager *manager,
SpiceUsbDevice *device, GError *error);
+ void (*device_changed) (SpiceUsbDeviceManager *manager,
+ SpiceUsbDevice *device);
+
/*< private >*/
/*
* If adding fields to this struct, remove corresponding
* amount of padding to avoid changing overall struct size
*/
- gchar _spice_reserved[SPICE_RESERVED_PADDING];
+ gchar _spice_reserved[SPICE_RESERVED_PADDING - 1 * sizeof(void *)];
};
GType spice_usb_device_get_type(void);
GType spice_usb_device_manager_get_type(void);
gchar *spice_usb_device_get_description(SpiceUsbDevice *device, const gchar *format);
+
+/**
+* SpiceUsbDeviceDescription:
+* @bus: USB bus number.
+* @address: address on the bus.
+* @vendor_id: vendor ID value from device descriptor.
+* @product_id: product ID value from device descriptor.
+* @vendor: vendor name (new string allocated on return)
+* @product: vendor name (new string allocated on return)
+*
+* Structure containing description of Spice USB device.
+*/
+typedef struct _SpiceUsbDeviceDescription
+{
+ guint16 bus;
+ guint16 address;
+ guint16 vendor_id;
+ guint16 product_id;
+ // (OUT) allocated strings for vendor and product
+ gchar *vendor;
+ gchar *product;
+} SpiceUsbDeviceDescription;
+
+/*
+spice_usb_device_get_info is similar to spice_usb_device_get_description,
+i.e. 'vendor' and 'product' strings are allocated by callee and shall be
+freed by caller
+*/
+void spice_usb_device_get_info(SpiceUsbDeviceManager *manager,
+ SpiceUsbDevice *device,
+ SpiceUsbDeviceDescription *info);
gconstpointer spice_usb_device_get_libusb_device(const SpiceUsbDevice *device);
SpiceUsbDeviceManager *spice_usb_device_manager_get(SpiceSession *session,
@@ -143,6 +178,76 @@ spice_usb_device_manager_can_redirect_device(SpiceUsbDeviceManager *self,
gboolean spice_usb_device_manager_is_redirecting(SpiceUsbDeviceManager *self);
+guint spice_usb_device_manager_is_device_cd(SpiceUsbDeviceManager *self,
+ SpiceUsbDevice *device);
+
+/* returns new array of guint LUN indices */
+GArray *spice_usb_device_manager_get_device_luns(SpiceUsbDeviceManager *self,
+ SpiceUsbDevice *device);
+
+/**
+* SpiceUsbDeviceLunInfo:
+* @file_path: Path to ISO file or local CD device that the unit represents.
+* @vendor: string containing vendor name according to SCSI standard (first 8 characters used)
+* @product: string containing product name according to SCSI standard (first 16 characters used)
+* @revision: string containing the revision according to SCSI standard (first 4 characters used)
+* @loaded: %TRUE if the media is currently loaded
+* @locked: %TRUE if the media is currently locked from ejection
+* @started: %TRUE if the device is started by guest OS
+*
+* Structure containing information about CD logical unit of Spice USB device.
+*/
+typedef struct _SpiceUsbDeviceLunInfo
+{
+ const gchar *file_path;
+ const gchar *vendor;
+ const gchar *product;
+ const gchar *revision;
+ gboolean loaded;
+ gboolean locked;
+ gboolean started;
+} SpiceUsbDeviceLunInfo;
+
+/* CD LUN will be attached to a (possibly new) USB device automatically */
+gboolean spice_usb_device_manager_add_cd_lun(SpiceUsbDeviceManager *self,
+ SpiceUsbDeviceLunInfo *lun_info);
+
+/* Get CD LUN info, intended primarily for enumerating LUNs.
+ The caller shall not free the strings returned in lun_info structure
+ on successfull call. It can use them only in context of current call or
+ duplicate for further usage.
+*/
+gboolean
+spice_usb_device_manager_device_lun_get_info(SpiceUsbDeviceManager *self,
+ SpiceUsbDevice *device,
+ guint lun,
+ SpiceUsbDeviceLunInfo *lun_info);
+/* lock or unlock device */
+gboolean
+spice_usb_device_manager_device_lun_lock(SpiceUsbDeviceManager *self,
+ SpiceUsbDevice *device,
+ guint lun,
+ gboolean lock);
+
+/* load or eject device */
+gboolean
+spice_usb_device_manager_device_lun_load(SpiceUsbDeviceManager *self,
+ SpiceUsbDevice *device,
+ guint lun,
+ gboolean load);
+
+/* change the media - device must be not currently loaded */
+gboolean
+spice_usb_device_manager_device_lun_change_media(SpiceUsbDeviceManager *self,
+ SpiceUsbDevice *device,
+ guint lun,
+ const SpiceUsbDeviceLunInfo *lun_info);
+/* remove lun from the usb device */
+gboolean
+spice_usb_device_manager_device_lun_remove(SpiceUsbDeviceManager *self,
+ SpiceUsbDevice *device,
+ guint lun);
+
G_END_DECLS
#endif /* __SPICE_USB_DEVICE_MANAGER_H__ */
diff --git a/src/usb-device-widget.c b/src/usb-device-widget.c
index d6e440c..f68a1a9 100644
--- a/src/usb-device-widget.c
+++ b/src/usb-device-widget.c
@@ -25,6 +25,8 @@
#include "spice-marshal.h"
#include "usb-device-widget.h"
+#ifndef USE_NEW_USB_WIDGET
+
/**
* SECTION:usb-device-widget
* @short_description: USB device selection widget
@@ -62,6 +64,7 @@ static void device_removed_cb(SpiceUsbDeviceManager *manager,
SpiceUsbDevice *device, gpointer user_data);
static void device_error_cb(SpiceUsbDeviceManager *manager,
SpiceUsbDevice *device, GError *err, gpointer user_data);
+
static gboolean spice_usb_device_widget_update_status(gpointer user_data);
enum {
@@ -591,3 +594,5 @@ static void device_error_cb(SpiceUsbDeviceManager *manager,
spice_usb_device_widget_update_status(self);
}
+
+#endif // !USE_NEW_USB_WIDGET
diff --git a/src/usbutil.c b/src/usbutil.c
index e96ab11..025f610 100644
--- a/src/usbutil.c
+++ b/src/usbutil.c
@@ -58,42 +58,6 @@ static GMutex usbids_load_mutex;
static int usbids_vendor_count = 0; /* < 0: failed, 0: empty, > 0: loaded */
static usb_vendor_info *usbids_vendor_info = NULL;
-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 */
@@ -252,7 +216,8 @@ leave:
G_GNUC_INTERNAL
void spice_usb_util_get_device_strings(int bus, int address,
int vendor_id, int product_id,
- gchar **manufacturer, gchar **product)
+ gchar **manufacturer, gchar **product,
+ gboolean fill_always)
{
usb_product_info *product_info;
int i, j;
@@ -292,17 +257,20 @@ void spice_usb_util_get_device_strings(int bus, int address,
}
}
- if (!*manufacturer)
+ if (!*manufacturer && fill_always)
*manufacturer = g_strdup(_("USB"));
- if (!*product)
+ if (!*product && fill_always)
*product = g_strdup(_("Device"));
/* Some devices have unwanted whitespace in their strings */
- g_strstrip(*manufacturer);
- g_strstrip(*product);
+ if (*manufacturer)
+ g_strstrip(*manufacturer);
+ if (*product)
+ g_strstrip(*product);
/* Some devices repeat the manufacturer at the beginning of product */
- if (g_str_has_prefix(*product, *manufacturer) &&
+ if (*manufacturer && *product &&
+ g_str_has_prefix(*product, *manufacturer) &&
strlen(*product) > strlen(*manufacturer)) {
gchar *tmp = g_strdup(*product + strlen(*manufacturer));
g_free(*product);
diff --git a/src/usbutil.h b/src/usbutil.h
index de5e92a..c2ff7be 100644
--- a/src/usbutil.h
+++ b/src/usbutil.h
@@ -24,14 +24,13 @@
#include <glib.h>
#ifdef USE_USBREDIR
-#include <libusb.h>
G_BEGIN_DECLS
-const char *spice_usbutil_libusb_strerror(enum libusb_error error_code);
void spice_usb_util_get_device_strings(int bus, int address,
int vendor_id, int product_id,
- gchar **manufacturer, gchar **product);
+ gchar **manufacturer, gchar **product,
+ gboolean fill_always);
G_END_DECLS
diff --git a/src/win-usb-dev.c b/src/win-usb-dev.c
index 9a130a3..d5dd2d2 100644
--- a/src/win-usb-dev.c
+++ b/src/win-usb-dev.c
@@ -23,11 +23,13 @@
#include "config.h"
#include <windows.h>
-#include <libusb.h>
#include "win-usb-dev.h"
#include "spice-marshal.h"
#include "spice-util.h"
#include "usbutil.h"
+#include "usb-backend.h"
+
+#define USB_CLASS_HUB 9
enum {
PROP_0,
@@ -35,7 +37,7 @@ enum {
};
struct _GUdevClientPrivate {
- libusb_context *ctx;
+ SpiceUsbBackend *ctx;
GList *udev_list;
HWND hwnd;
gboolean redirecting;
@@ -85,7 +87,7 @@ static GUdevClient *singleton = NULL;
static GUdevDevice *g_udev_device_new(GUdevDeviceInfo *udevinfo);
static LRESULT CALLBACK wnd_proc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam);
-static gboolean get_usb_dev_info(libusb_device *dev, GUdevDeviceInfo *udevinfo);
+static gboolean get_usb_dev_info(SpiceUsbBackendDevice *dev, GUdevDeviceInfo *udevinfo);
//uncomment to debug gudev device lists.
//#define DEBUG_GUDEV_DEVICE_LISTS
@@ -122,8 +124,7 @@ static ssize_t
g_udev_client_list_devices(GUdevClient *self, GList **devs,
GError **err, const gchar *name)
{
- gssize rc;
- libusb_device **lusb_list, **dev;
+ SpiceUsbBackendDevice **lusb_list, **dev;
GUdevClientPrivate *priv;
GUdevDeviceInfo *udevinfo;
GUdevDevice *udevice;
@@ -136,13 +137,8 @@ g_udev_client_list_devices(GUdevClient *self, GList **devs,
g_return_val_if_fail(self->priv->ctx != NULL, -3);
- rc = libusb_get_device_list(priv->ctx, &lusb_list);
- if (rc < 0) {
- const char *errstr = spice_usbutil_libusb_strerror(rc);
- g_warning("%s: libusb_get_device_list failed - %s", name, errstr);
- g_set_error(err, G_UDEV_CLIENT_ERROR, G_UDEV_CLIENT_LIBUSB_FAILED,
- "%s: Error getting device list from libusb: %s [%"G_GSSIZE_FORMAT"]",
- name, errstr, rc);
+ lusb_list = spice_usb_backend_get_device_list(priv->ctx);
+ if (!lusb_list) {
return -4;
}
@@ -158,7 +154,7 @@ g_udev_client_list_devices(GUdevClient *self, GList **devs,
*devs = g_list_prepend(*devs, udevice);
n++;
}
- libusb_free_device_list(lusb_list, 1);
+ spice_usb_backend_free_device_list(lusb_list);
return n;
}
@@ -180,7 +176,6 @@ g_udev_client_initable_init(GInitable *initable, GCancellable *cancellable,
GUdevClient *self;
GUdevClientPrivate *priv;
WNDCLASS wcls;
- int rc;
g_return_val_if_fail(G_UDEV_IS_CLIENT(initable), FALSE);
g_return_val_if_fail(cancellable == NULL, FALSE);
@@ -188,19 +183,10 @@ g_udev_client_initable_init(GInitable *initable, GCancellable *cancellable,
self = G_UDEV_CLIENT(initable);
priv = self->priv;
- rc = libusb_init(&priv->ctx);
- if (rc < 0) {
- const char *errstr = spice_usbutil_libusb_strerror(rc);
- g_warning("Error initializing USB support: %s [%i]", errstr, rc);
- g_set_error(err, G_UDEV_CLIENT_ERROR, G_UDEV_CLIENT_LIBUSB_FAILED,
- "Error initializing USB support: %s [%i]", errstr, rc);
+ priv->ctx = spice_usb_backend_initialize();
+ if (!priv->ctx) {
return FALSE;
}
-#ifdef G_OS_WIN32
-#if LIBUSB_API_VERSION >= 0x01000106
- libusb_set_option(priv->ctx, LIBUSB_OPTION_USE_USBDK);
-#endif
-#endif
/* get initial device list */
if (g_udev_client_list_devices(self, &priv->udev_list, err, __FUNCTION__) < 0) {
@@ -267,7 +253,7 @@ static void g_udev_client_finalize(GObject *gobject)
/* free libusb context initializing by libusb_init() */
g_warn_if_fail(priv->ctx != NULL);
- libusb_exit(priv->ctx);
+ spice_usb_backend_finalize(priv->ctx);
/* Chain up to the parent class */
if (G_OBJECT_CLASS(g_udev_client_parent_class)->finalize)
@@ -356,23 +342,18 @@ static void g_udev_client_class_init(GUdevClientClass *klass)
g_object_class_install_property(gobject_class, PROP_REDIRECTING, pspec);
}
-static gboolean get_usb_dev_info(libusb_device *dev, GUdevDeviceInfo *udevinfo)
+static gboolean get_usb_dev_info(SpiceUsbBackendDevice *dev, GUdevDeviceInfo *udevinfo)
{
- struct libusb_device_descriptor desc;
+ const UsbDeviceInformation* info = spice_usb_backend_device_get_info(dev);
g_return_val_if_fail(dev, FALSE);
g_return_val_if_fail(udevinfo, FALSE);
- if (libusb_get_device_descriptor(dev, &desc) < 0) {
- g_warning("cannot get device descriptor %p", dev);
- return FALSE;
- }
-
- udevinfo->bus = libusb_get_bus_number(dev);
- udevinfo->addr = libusb_get_device_address(dev);
- udevinfo->class = desc.bDeviceClass;
- udevinfo->vid = desc.idVendor;
- udevinfo->pid = desc.idProduct;
+ udevinfo->bus = info->bus;
+ udevinfo->addr = info->address;
+ udevinfo->class = info->class;
+ udevinfo->vid = info->vid;
+ udevinfo->pid = info->pid;
snprintf(udevinfo->sclass, sizeof(udevinfo->sclass), "%d", udevinfo->class);
snprintf(udevinfo->sbus, sizeof(udevinfo->sbus), "%d", udevinfo->bus);
snprintf(udevinfo->saddr, sizeof(udevinfo->saddr), "%d", udevinfo->addr);
@@ -573,7 +554,14 @@ static gboolean g_udev_skip_search(GUdevDevice *udev)
#if defined(LIBUSBX_API_VERSION) && (LIBUSBX_API_VERSION >= 0x010000FF)
(udevinfo->addr == 1) || /* root hub addr for libusbx >= 1.0.13 */
#endif
- (udevinfo->class == LIBUSB_CLASS_HUB) || /* hub*/
+ (udevinfo->class == USB_CLASS_HUB) || /* hub*/
(udevinfo->addr == 0)); /* bad address */
return skip;
}
+
+void spice_usb_backend_indicate_dev_change(void)
+{
+ GUdevClient *client = g_udev_client_new(NULL);
+ GUdevClientPrivate *priv = client->priv;
+ PostMessage(priv->hwnd, WM_DEVICECHANGE, 0, 0);
+}
diff --git a/src/win-usb-dev.h b/src/win-usb-dev.h
index b5c4fce..ff12aac 100644
--- a/src/win-usb-dev.h
+++ b/src/win-usb-dev.h
@@ -104,6 +104,8 @@ typedef enum
G_UDEV_CLIENT_WINAPI_FAILED
} GUdevClientError;
+/* callback to indicate creation of new simulated device */
+void spice_usb_backend_indicate_dev_change(void);
G_END_DECLS
--
2.9.4
More information about the Spice-devel
mailing list