<div dir="ltr">Regarding the new widget:<div>New widget introduced in order to keep the backward compatibility.</div><div>There is an option (described in the commit comment) to keep the old look of the widget (this will suppress the cd-sharing, of course).</div><div>I do not take the responsibility to decide that the old widget is not needed anymore.</div><div>When such decision is taken, it is possible to remove the old file in simple future commit.</div><div><br></div><div>Regarding additional split of the 2/2 to 'backend' and 'cd-sharing'</div><div>I've checked this option before preparing the combined commit and found that the content</div><div>of 'cd-sharing' changes in 2/2 is not more than 10% and splitting will add significant time</div><div>to test the intermediate patch (as everything shall work without regression after each patch)</div><div>which still stays intermediate as the main motivation of 'backend' part is the 'cd-sharing' feature.</div><div><br></div><div>If there are no very strong reasons for spending time for preparing/testing such intermediate patch</div><div>I'd suggest to continue reviewing the combined patch.</div><div><br></div><div>Thanks,</div><div>Yuri Benditovich</div><div><br></div></div><div class="gmail_extra"><br><div class="gmail_quote">On Thu, Sep 13, 2018 at 10:43 AM, Christophe Fergeau <span dir="ltr"><<a href="mailto:cfergeau@redhat.com" target="_blank">cfergeau@redhat.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><span class="">On Mon, Sep 10, 2018 at 02:33:45PM +0300, Yuri Benditovich wrote:<br>
> This commit contains modified files related to CD sharing feature.<br>
> The feature adds ability to share one or more CD images<br>
> (or local CD-DVD devices) with the guest by emulating<br>
> USB CD devices connected to guest system.<br>
<br>
</span>My feeling is that these commits are very big. For example, a big part<br>
of the changes in this file seem to be to replace direct libusb_device<br>
use with SpiceUsbBackendDevice. Have you considered starting by<br>
reworking the existing code to switch so SpiceUsbBackendDevice<br>
(introducing the needed new files at the same time), and then adding the<br>
cd redirection stuff on top of that?<br>
I'm also not quite clear why a brand new widget is introduced rather<br>
than changing the existing widget to the new look, and then adding the<br>
CD redirection bits on top of it?<br>
<br>
And btw, s/RedHat/Red Hat/ in src/usb-device-manager.c :)<br>
<br>
Christophe<br>
<div><div class="h5"><br>
> <br>
> Prerequisites:<br>
> Guest system shall have USB2 or USB3 controller and one or more<br>
> USB redirection channels. Each emulated CD device currently<br>
> uses one USB redirection channel.<br>
> Usage from GUI:<br>
> New USB device redirection widget includes 'Add CD' button that<br>
> allows selection of ISO file which will be used for emulation<br>
> of USB CD drive.<br>
> Usage from command line:<br>
> Added command line option '--spice-share-cd=filename' which<br>
> allows to create one or more emulated CD drives.<br>
> Sharing local CD drive:<br>
> For sharing local CD drive use file name of respective device,<br>
> it depends on the operating system of client machine.<br>
> Example for Windows clients - d:<br>
> Example for Linux clients - /dev/cdrom<br>
> Build backward compatibility:<br>
> The feature can be disabled by build configuration:<br>
> --enable-cdsharing=no disables CD sharing (USB redirection<br>
> GUI uses new USB redirection widget)<br>
> --enable-newusbwidget=no also disables CD sharing (USB<br>
> redirection GUI uses old USB widget)<br>
> Notes for Windows build of 'VirtViewer':<br>
> In order to show proper icons in new USB redirection widget<br>
> the installation package of VirtViewer shall include<br>
> following icons:<br>
> "media-optical", "network-transmit-receive", "network-offline",<br>
> "dialog-warning", "dialog-information",<br>
> "preferences-system","system-<wbr>lock-screen","media-eject",<br>
> "edit-delete", "list-add"<br>
> Automatic redirection of emulated CD devices:<br>
> Same as one for local USB drives:<br>
> - shared CD devices created by command line redirected<br>
> according to 'redirect-on-connect' filter<br>
> - shared CD devices created during session redirected<br>
> according to 'auto-redirect' filter<br>
> Disable redirection of emulated CD devices on server side:<br>
> Same as for redirection of local USB drives:<br>
> -device usb-redir,filter=<filter> option of qemu<br>
> <br>
> Signed-off-by: Yuri Benditovich <<a href="mailto:yuri.benditovich@daynix.com">yuri.benditovich@daynix.com</a>><br>
> ---<br>
> <a href="http://configure.ac" rel="noreferrer" target="_blank">configure.ac</a> | 27 ++<br>
> src/Makefile.am | 15 +-<br>
> src/channel-usbredir-priv.h | 9 +-<br>
> src/channel-usbredir.c | 267 ++++++------------<br>
> src/map-file | 9 +<br>
> src/spice-option.c | 15 ++<br>
> src/usb-device-manager-priv.h | 5 +-<br>
> src/usb-device-manager.c | 615 +++++++++++++++++++++++++-----<wbr>------------<br>
> src/usb-device-manager.h | 107 +++++++-<br>
> src/usb-device-widget.c | 5 +<br>
> src/usbutil.c | 52 +---<br>
> src/usbutil.h | 5 +-<br>
> src/win-usb-dev.c | 66 ++---<br>
> src/win-usb-dev.h | 2 +<br>
> 14 files changed, 682 insertions(+), 517 deletions(-)<br>
> <br>
> diff --git a/<a href="http://configure.ac" rel="noreferrer" target="_blank">configure.ac</a> b/<a href="http://configure.ac" rel="noreferrer" target="_blank">configure.ac</a><br>
> index 7b32e29..1e977b3 100644<br>
> --- a/<a href="http://configure.ac" rel="noreferrer" target="_blank">configure.ac</a><br>
> +++ b/<a href="http://configure.ac" rel="noreferrer" target="_blank">configure.ac</a><br>
> @@ -349,6 +349,32 @@ else<br>
> fi<br>
> AM_CONDITIONAL([WITH_USBREDIR]<wbr>, [test "x$have_usbredir" = "xyes"])<br>
> <br>
> +AC_ARG_ENABLE([newusbwidget],<br>
> + AS_HELP_STRING([--enable-<wbr>newusbwidget=@<:@yes/no@:>@],<br>
> + [Use new USB devices widget @<:@default=yes@:>@]),<br>
> + [],<br>
> + [enable_newusbwidget="yes"])<br>
> +<br>
> +AC_ARG_ENABLE([cdsharing],<br>
> + AS_HELP_STRING([--enable-<wbr>cdsharing=@<:@auto/yes/no@:>@]<wbr>,<br>
> + [Enable CD charing feature @<:@default=auto@:>@]),<br>
> + [],<br>
> + [enable_cdsharing="auto"])<br>
> +<br>
> +if test "x$enable_newusbwidget" = "xno" || test "x$have_usbredir" != "xyes" ; then<br>
> + have_newusbwidget="no"<br>
> +else<br>
> + have_newusbwidget="yes"<br>
> + AC_DEFINE([USE_NEW_USB_WIDGET]<wbr>, [1], [Define if new USB widget should be used])<br>
> +fi<br>
> +<br>
> +if test "x$enable_cdsharing" = "xno" || test "x$have_newusbwidget" = "xno"; then<br>
> + have_cdsharing="no"<br>
> +else<br>
> + have_cdsharing="yes"<br>
> + AC_DEFINE([USE_CD_SHARING], [1], [Define if supporting cd sharing via usbredir])<br>
> +fi<br>
> +<br>
> AC_ARG_ENABLE([polkit],<br>
> AS_HELP_STRING([--enable-<wbr>polkit=@<:@auto/yes/no@:>@],<br>
> [Enable PolicyKit support (for the usb acl helper)@<:@default=auto@:>@]),<br>
> @@ -605,6 +631,7 @@ AC_MSG_NOTICE([<br>
> SASL support: ${have_sasl}<br>
> Smartcard support: ${have_smartcard}<br>
> USB redirection support: ${have_usbredir} ${with_usbredir_hotplug}<br>
> + CD sharing support: ${have_cdsharing} new widget: ${have_newusbwidget}<br>
> DBus: ${have_dbus}<br>
> WebDAV support: ${have_phodav}<br>
> LZ4 support: ${have_lz4}<br>
> diff --git a/src/Makefile.am b/src/Makefile.am<br>
> index 4dd657d..c23e7da 100644<br>
> --- a/src/Makefile.am<br>
> +++ b/src/Makefile.am<br>
> @@ -127,7 +127,8 @@ SPICE_GTK_SOURCES_COMMON = \<br>
> spice-grabsequence-priv.h \<br>
> desktop-integration.c \<br>
> desktop-integration.h \<br>
> - usb-device-widget.c \<br>
> + usb-device-redir-widget.c \<br>
> + usb-device-widget.c \<br>
> $(NULL)<br>
> <br>
> nodist_SPICE_GTK_SOURCES_<wbr>COMMON = \<br>
> @@ -249,6 +250,15 @@ libspice_client_glib_2_0_la_<wbr>SOURCES = \<br>
> spice-uri-priv.h \<br>
> usb-device-manager.c \<br>
> usb-device-manager-priv.h \<br>
> + usb-backend-common.c \<br>
> + usb-backend.h \<br>
> + cd-usb-bulk-msd.c \<br>
> + cd-usb-bulk-msd.h \<br>
> + cd-scsi.c \<br>
> + cd-scsi.h \<br>
> + cd-device.h \<br>
> + cd-device-win.c \<br>
> + cd-device-linux.c \<br>
> usbutil.c \<br>
> usbutil.h \<br>
> $(USB_ACL_HELPER_SRCS) \<br>
> @@ -536,7 +546,8 @@ gtk_introspection_files = \<br>
> spice-gtk-session.c \<br>
> spice-widget.c \<br>
> spice-grabsequence.c \<br>
> - usb-device-widget.c \<br>
> + usb-device-redir-widget.c \<br>
> + usb-device-widget.c \<br>
> $(NULL)<br>
> <br>
> SpiceClientGLib-2.0.gir: <a href="http://libspice-client-glib-2.0.la" rel="noreferrer" target="_blank">libspice-client-glib-2.0.la</a><br>
> diff --git a/src/channel-usbredir-priv.h b/src/channel-usbredir-priv.h<br>
> index 17e9716..2c3e705 100644<br>
> --- a/src/channel-usbredir-priv.h<br>
> +++ b/src/channel-usbredir-priv.h<br>
> @@ -21,9 +21,8 @@<br>
> #ifndef __SPICE_CLIENT_USBREDIR_<wbr>CHANNEL_PRIV_H__<br>
> #define __SPICE_CLIENT_USBREDIR_<wbr>CHANNEL_PRIV_H__<br>
> <br>
> -#include <libusb.h><br>
> -#include <usbredirfilter.h><br>
> #include "spice-client.h"<br>
> +#include "usb-backend.h"<br>
> <br>
> G_BEGIN_DECLS<br>
> <br>
> @@ -31,7 +30,7 @@ G_BEGIN_DECLS<br>
> context should not be destroyed before the last device has been<br>
> disconnected */<br>
> void spice_usbredir_channel_set_<wbr>context(SpiceUsbredirChannel *channel,<br>
> - libusb_context *context);<br>
> + SpiceUsbBackend *context);<br>
> <br>
> void spice_usbredir_channel_<wbr>disconnect_device_async(<wbr>SpiceUsbredirChannel *channel,<br>
> GCancellable *cancellable,<br>
> @@ -46,7 +45,7 @@ gboolean spice_usbredir_channel_<wbr>disconnect_device_finish(<wbr>SpiceUsbredirChannel *c<br>
> (through spice_channel_connect()), before calling this. */<br>
> void spice_usbredir_channel_<wbr>connect_device_async(<br>
> SpiceUsbredirChannel *channel,<br>
> - libusb_device *device,<br>
> + SpiceUsbBackendDevice *device,<br>
> SpiceUsbDevice *spice_device,<br>
> GCancellable *cancellable,<br>
> GAsyncReadyCallback callback,<br>
> @@ -58,7 +57,7 @@ gboolean spice_usbredir_channel_<wbr>connect_device_finish(<br>
> <br>
> void spice_usbredir_channel_<wbr>disconnect_device(<wbr>SpiceUsbredirChannel *channel);<br>
> <br>
> -libusb_device *spice_usbredir_channel_get_<wbr>device(SpiceUsbredirChannel *channel);<br>
> +SpiceUsbBackendDevice *spice_usbredir_channel_get_<wbr>device(SpiceUsbredirChannel *channel);<br>
> <br>
> void spice_usbredir_channel_lock(<wbr>SpiceUsbredirChannel *channel);<br>
> <br>
> diff --git a/src/channel-usbredir.c b/src/channel-usbredir.c<br>
> index 1d9c380..ffdf9b1 100644<br>
> --- a/src/channel-usbredir.c<br>
> +++ b/src/channel-usbredir.c<br>
> @@ -23,7 +23,6 @@<br>
> <br>
> #ifdef USE_USBREDIR<br>
> #include <glib/gi18n-lib.h><br>
> -#include <usbredirhost.h><br>
> #ifdef USE_LZ4<br>
> #include <lz4.h><br>
> #endif<br>
> @@ -66,15 +65,12 @@ enum SpiceUsbredirChannelState {<br>
> };<br>
> <br>
> struct _SpiceUsbredirChannelPrivate {<br>
> - libusb_device *device;<br>
> + SpiceUsbBackendDevice *device;<br>
> SpiceUsbDevice *spice_device;<br>
> - libusb_context *context;<br>
> - struct usbredirhost *host;<br>
> + SpiceUsbBackend *context;<br>
> + SpiceUsbBackendChannel *host;<br>
> /* To catch usbredirhost error messages and report them as a GError */<br>
> GError **catch_error;<br>
> - /* Data passed from channel handle msg to the usbredirhost read cb */<br>
> - const uint8_t *read_buf;<br>
> - int read_buf_size;<br>
> enum SpiceUsbredirChannelState state;<br>
> #ifdef USE_POLKIT<br>
> GTask *task;<br>
> @@ -90,18 +86,10 @@ static void spice_usbredir_channel_<wbr>dispose(GObject *obj);<br>
> static void spice_usbredir_channel_<wbr>finalize(GObject *obj);<br>
> static void usbredir_handle_msg(<wbr>SpiceChannel *channel, SpiceMsgIn *in);<br>
> <br>
> -static void usbredir_log(void *user_data, int level, const char *msg);<br>
> -static int usbredir_read_callback(void *user_data, uint8_t *data, int count);<br>
> +static void usbredir_log(void *user_data, const char *msg, gboolean error);<br>
> static int usbredir_write_callback(void *user_data, uint8_t *data, int count);<br>
> -static void usbredir_write_flush_callback(<wbr>void *user_data);<br>
> -#if USBREDIR_VERSION >= 0x000701<br>
> -static uint64_t usbredir_buffered_output_size_<wbr>callback(void *user_data);<br>
> -#endif<br>
> -<br>
> -static void *usbredir_alloc_lock(void);<br>
> -static void usbredir_lock_lock(void *user_data);<br>
> -static void usbredir_unlock_lock(void *user_data);<br>
> -static void usbredir_free_lock(void *user_data);<br>
> +static gboolean usbredir_is_channel_ready(void *user_data);<br>
> +static uint64_t usbredir_get_queue_size(void *user_data);<br>
> <br>
> #else<br>
> struct _SpiceUsbredirChannelPrivate {<br>
> @@ -128,7 +116,7 @@ static void _channel_reset_finish(<wbr>SpiceUsbredirChannel *channel)<br>
> <br>
> spice_usbredir_channel_lock(<wbr>channel);<br>
> <br>
> - usbredirhost_close(priv->host)<wbr>;<br>
> + spice_usb_backend_channel_<wbr>finalize(priv->host);<br>
> priv->host = NULL;<br>
> <br>
> /* Call set_context to re-create the host */<br>
> @@ -228,7 +216,7 @@ static void spice_usbredir_channel_<wbr>finalize(GObject *obj)<br>
> SpiceUsbredirChannel *channel = SPICE_USBREDIR_CHANNEL(obj);<br>
> <br>
> if (channel->priv->host)<br>
> - usbredirhost_close(channel-><wbr>priv->host);<br>
> + spice_usb_backend_channel_<wbr>finalize(channel->priv->host);<br>
> #ifdef USE_USBREDIR<br>
> g_mutex_clear(&channel->priv-><wbr>device_connect_mutex);<br>
> #endif<br>
> @@ -252,33 +240,24 @@ static void channel_set_handlers(<wbr>SpiceChannelClass *klass)<br>
> /* private api */<br>
> <br>
> G_GNUC_INTERNAL<br>
> -void spice_usbredir_channel_set_<wbr>context(SpiceUsbredirChannel *channel,<br>
> - libusb_context *context)<br>
> +void spice_usbredir_channel_set_<wbr>context(<br>
> + SpiceUsbredirChannel *channel,<br>
> + SpiceUsbBackend *context)<br>
> {<br>
> SpiceUsbredirChannelPrivate *priv = channel->priv;<br>
> + SpiceUsbBackendChannelInitData init_data;<br>
> + init_data.user_data = channel;<br>
> + init_data.get_queue_size = usbredir_get_queue_size;<br>
> + init_data.is_channel_ready = usbredir_is_channel_ready;<br>
> + init_data.log = usbredir_log;<br>
> + init_data.write_callback = usbredir_write_callback;<br>
> + init_data.debug = spice_util_get_debug();<br>
> <br>
> g_return_if_fail(priv->host == NULL);<br>
> <br>
> priv->context = context;<br>
> - priv->host = usbredirhost_open_full(<br>
> - context, NULL,<br>
> - usbredir_log,<br>
> - usbredir_read_callback,<br>
> - usbredir_write_callback,<br>
> - usbredir_write_flush_callback,<br>
> - usbredir_alloc_lock,<br>
> - usbredir_lock_lock,<br>
> - usbredir_unlock_lock,<br>
> - usbredir_free_lock,<br>
> - channel, PACKAGE_STRING,<br>
> - spice_util_get_debug() ? usbredirparser_debug : usbredirparser_warning,<br>
> - usbredirhost_fl_write_cb_owns_<wbr>buffer);<br>
> - if (!priv->host)<br>
> - g_error("Out of memory allocating usbredirhost");<br>
> + priv->host = spice_usb_backend_channel_<wbr>initialize(context, &init_data);<br>
> <br>
> -#if USBREDIR_VERSION >= 0x000701<br>
> - usbredirhost_set_buffered_<wbr>output_size_cb(priv->host, usbredir_buffered_output_size_<wbr>callback);<br>
> -#endif<br>
> #ifdef USE_LZ4<br>
> spice_channel_set_capability(<wbr>channel, SPICE_SPICEVMC_CAP_DATA_<wbr>COMPRESS_LZ4);<br>
> #endif<br>
> @@ -289,9 +268,8 @@ static gboolean spice_usbredir_channel_open_<wbr>device(<br>
> {<br>
> SpiceUsbredirChannelPrivate *priv = channel->priv;<br>
> SpiceSession *session;<br>
> - libusb_device_handle *handle = NULL;<br>
> - int rc, status;<br>
> SpiceUsbDeviceManager *manager;<br>
> + const char *msg = NULL;<br>
> <br>
> g_return_val_if_fail(priv-><wbr>state == STATE_DISCONNECTED<br>
> #ifdef USE_POLKIT<br>
> @@ -299,29 +277,28 @@ static gboolean spice_usbredir_channel_open_<wbr>device(<br>
> #endif<br>
> , FALSE);<br>
> <br>
> - rc = libusb_open(priv->device, &handle);<br>
> - if (rc != 0) {<br>
> - g_set_error(err, SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,<br>
> - "Could not open usb device: %s [%i]",<br>
> - spice_usbutil_libusb_strerror(<wbr>rc), rc);<br>
> - return FALSE;<br>
> - }<br>
> -<br>
> priv->catch_error = err;<br>
> - status = usbredirhost_set_device(priv-><wbr>host, handle);<br>
> - priv->catch_error = NULL;<br>
> - if (status != usb_redir_success) {<br>
> - g_return_val_if_fail(err == NULL || *err != NULL, FALSE);<br>
> + if (!spice_usb_backend_channel_<wbr>attach(priv->host, priv->device, &msg)) {<br>
> + priv->catch_error = NULL;<br>
> + if (*err == NULL) {<br>
> + if (!msg) {<br>
> + msg = "Exact error not reported";<br>
> + }<br>
> + g_set_error(err, SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,<br>
> + "Error attaching device: %s", msg);<br>
> + }<br>
> return FALSE;<br>
> }<br>
> + priv->catch_error = NULL;<br>
> <br>
> session = spice_channel_get_session(<wbr>SPICE_CHANNEL(channel));<br>
> manager = spice_usb_device_manager_get(<wbr>session, NULL);<br>
> g_return_val_if_fail(manager != NULL, FALSE);<br>
> <br>
> priv->usb_device_manager = g_object_ref(manager);<br>
> - if (!spice_usb_device_manager_<wbr>start_event_listening(priv-><wbr>usb_device_manager, err)) {<br>
> - usbredirhost_set_device(priv-><wbr>host, NULL);<br>
> + if (spice_usb_backend_device_<wbr>need_thread(priv->device) &&<br>
> + !spice_usb_device_manager_<wbr>start_event_listening(priv-><wbr>usb_device_manager, err)) {<br>
> + spice_usb_backend_channel_<wbr>attach(priv->host, NULL, NULL);<br>
> return FALSE;<br>
> }<br>
> <br>
> @@ -352,8 +329,7 @@ static void spice_usbredir_channel_open_<wbr>acl_cb(<br>
> spice_usbredir_channel_open_<wbr>device(channel, &err);<br>
> }<br>
> if (err) {<br>
> - libusb_unref_device(priv-><wbr>device);<br>
> - priv->device = NULL;<br>
> + g_clear_pointer(&priv->device, spice_usb_backend_device_<wbr>release);<br>
> g_boxed_free(spice_usb_device_<wbr>get_type(), priv->spice_device);<br>
> priv->spice_device = NULL;<br>
> priv->state = STATE_DISCONNECTED;<br>
> @@ -370,7 +346,6 @@ static void spice_usbredir_channel_open_<wbr>acl_cb(<br>
> }<br>
> #endif<br>
> <br>
> -#ifndef USE_POLKIT<br>
> static void<br>
> _open_device_async_cb(GTask *task,<br>
> gpointer object,<br>
> @@ -384,8 +359,7 @@ _open_device_async_cb(GTask *task,<br>
> spice_usbredir_channel_lock(<wbr>channel);<br>
> <br>
> if (!spice_usbredir_channel_open_<wbr>device(channel, &err)) {<br>
> - libusb_unref_device(priv-><wbr>device);<br>
> - priv->device = NULL;<br>
> + g_clear_pointer(&priv->device, spice_usb_backend_device_<wbr>release);<br>
> g_boxed_free(spice_usb_device_<wbr>get_type(), priv->spice_device);<br>
> priv->spice_device = NULL;<br>
> }<br>
> @@ -398,18 +372,20 @@ _open_device_async_cb(GTask *task,<br>
> g_task_return_boolean(task, TRUE);<br>
> }<br>
> }<br>
> -#endif<br>
> <br>
> G_GNUC_INTERNAL<br>
> void spice_usbredir_channel_<wbr>connect_device_async(<br>
> SpiceUsbredirChannel *channel,<br>
> - libusb_device *device,<br>
> + SpiceUsbBackendDevice *device,<br>
> SpiceUsbDevice *spice_device,<br>
> GCancellable *cancellable,<br>
> GAsyncReadyCallback callback,<br>
> gpointer user_data)<br>
> {<br>
> SpiceUsbredirChannelPrivate *priv = channel->priv;<br>
> +#ifdef USE_POLKIT<br>
> + const UsbDeviceInformation *info = spice_usb_backend_device_get_<wbr>info(device);<br>
> +#endif<br>
> GTask *task;<br>
> <br>
> g_return_if_fail(SPICE_IS_<wbr>USBREDIR_CHANNEL(channel));<br>
> @@ -436,25 +412,28 @@ void spice_usbredir_channel_<wbr>connect_device_async(<br>
> goto done;<br>
> }<br>
> <br>
> - priv->device = libusb_ref_device(device);<br>
> + spice_usb_backend_device_<wbr>acquire(device);<br>
> + priv->device = device;<br>
> priv->spice_device = g_boxed_copy(spice_usb_device_<wbr>get_type(),<br>
> spice_device);<br>
> #ifdef USE_POLKIT<br>
> - priv->task = task;<br>
> - priv->state = STATE_WAITING_FOR_ACL_HELPER;<br>
> - priv->acl_helper = spice_usb_acl_helper_new();<br>
> - g_object_set(spice_channel_<wbr>get_session(SPICE_CHANNEL(<wbr>channel)),<br>
> - "inhibit-keyboard-grab", TRUE, NULL);<br>
> - spice_usb_acl_helper_open_acl_<wbr>async(priv->acl_helper,<br>
> - libusb_get_bus_number(device),<br>
> - libusb_get_device_address(<wbr>device),<br>
> - cancellable,<br>
> - spice_usbredir_channel_open_<wbr>acl_cb,<br>
> - channel);<br>
> - return;<br>
> -#else<br>
> - g_task_run_in_thread(task, _open_device_async_cb);<br>
> + // avoid calling ACL helper for emulated CD devices<br>
> + if (info->max_luns == 0) {<br>
> + priv->task = task;<br>
> + priv->state = STATE_WAITING_FOR_ACL_HELPER;<br>
> + priv->acl_helper = spice_usb_acl_helper_new();<br>
> + g_object_set(spice_channel_<wbr>get_session(SPICE_CHANNEL(<wbr>channel)),<br>
> + "inhibit-keyboard-grab", TRUE, NULL);<br>
> + spice_usb_acl_helper_open_acl_<wbr>async(priv->acl_helper,<br>
> + info->bus,<br>
> + info->address,<br>
> + cancellable,<br>
> + spice_usbredir_channel_open_<wbr>acl_cb,<br>
> + channel);<br>
> + return;<br>
> + }<br>
> #endif<br>
> + g_task_run_in_thread(task, _open_device_async_cb);<br>
> <br>
> done:<br>
> g_object_unref(task);<br>
> @@ -501,13 +480,14 @@ void spice_usbredir_channel_<wbr>disconnect_device(<wbr>SpiceUsbredirChannel *channel)<br>
> * libusb_handle_events call in the thread.<br>
> */<br>
> g_warn_if_fail(priv->usb_<wbr>device_manager != NULL);<br>
> - spice_usb_device_manager_stop_<wbr>event_listening(priv->usb_<wbr>device_manager);<br>
> + if (spice_usb_backend_device_<wbr>need_thread(priv->device)) {<br>
> + spice_usb_device_manager_stop_<wbr>event_listening(priv->usb_<wbr>device_manager);<br>
> + }<br>
> g_clear_object(&priv->usb_<wbr>device_manager);<br>
> <br>
> /* This also closes the libusb handle we passed from open_device */<br>
> - usbredirhost_set_device(priv-><wbr>host, NULL);<br>
> - libusb_unref_device(priv-><wbr>device);<br>
> - priv->device = NULL;<br>
> + spice_usb_backend_channel_<wbr>attach(priv->host, NULL, NULL);<br>
> + g_clear_pointer(&priv->device, spice_usb_backend_device_<wbr>release);<br>
> g_boxed_free(spice_usb_device_<wbr>get_type(), priv->spice_device);<br>
> priv->spice_device = NULL;<br>
> priv->state = STATE_DISCONNECTED;<br>
> @@ -558,7 +538,7 @@ spice_usbredir_channel_get_<wbr>spice_usb_device(<wbr>SpiceUsbredirChannel *channel)<br>
> #endif<br>
> <br>
> G_GNUC_INTERNAL<br>
> -libusb_device *spice_usbredir_channel_get_<wbr>device(SpiceUsbredirChannel *channel)<br>
> +SpiceUsbBackendDevice *spice_usbredir_channel_get_<wbr>device(SpiceUsbredirChannel *channel)<br>
> {<br>
> return channel->priv->device;<br>
> }<br>
> @@ -573,85 +553,46 @@ void spice_usbredir_channel_get_<wbr>guest_filter(<br>
> <br>
> g_return_if_fail(priv->host != NULL);<br>
> <br>
> - usbredirhost_get_guest_filter(<wbr>priv->host, rules_ret, rules_count_ret);<br>
> + spice_usb_backend_channel_get_<wbr>guest_filter(priv->host, rules_ret, rules_count_ret);<br>
> }<br>
> <br>
> /* ------------------------------<wbr>------------------------------<wbr>------ */<br>
> /* callbacks (any context) */<br>
> <br>
> -#if USBREDIR_VERSION >= 0x000701<br>
> -static uint64_t usbredir_buffered_output_size_<wbr>callback(void *user_data)<br>
> +static uint64_t usbredir_get_queue_size(void *user_data)<br>
> {<br>
> g_return_val_if_fail(SPICE_IS_<wbr>USBREDIR_CHANNEL(user_data), 0);<br>
> return spice_channel_get_queue_size(<wbr>SPICE_CHANNEL(user_data));<br>
> }<br>
> -#endif<br>
> <br>
> -/* Note that this function must be re-entrant safe, as it can get called<br>
> - from both the main thread as well as from the usb event handling thread */<br>
> -static void usbredir_write_flush_callback(<wbr>void *user_data)<br>
> +static gboolean usbredir_is_channel_ready(void *user_data)<br>
> {<br>
> SpiceUsbredirChannel *channel = SPICE_USBREDIR_CHANNEL(user_<wbr>data);<br>
> SpiceUsbredirChannelPrivate *priv = channel->priv;<br>
> -<br>
> - if (spice_channel_get_state(<wbr>SPICE_CHANNEL(channel)) !=<br>
> - SPICE_CHANNEL_STATE_READY)<br>
> - return;<br>
> -<br>
> + if (spice_channel_get_state(<wbr>SPICE_CHANNEL(channel)) != SPICE_CHANNEL_STATE_READY)<br>
> + return FALSE;<br>
> if (!priv->host)<br>
> - return;<br>
> + return FALSE;<br>
> <br>
> - usbredirhost_write_guest_data(<wbr>priv->host);<br>
> + return TRUE;<br>
> }<br>
> <br>
> -static void usbredir_log(void *user_data, int level, const char *msg)<br>
> +static void usbredir_log(void *user_data, const char *msg, gboolean error)<br>
> {<br>
> SpiceUsbredirChannel *channel = user_data;<br>
> SpiceUsbredirChannelPrivate *priv = channel->priv;<br>
> <br>
> - if (priv->catch_error && level == usbredirparser_error) {<br>
> - CHANNEL_DEBUG(channel, "%s", msg);<br>
> + CHANNEL_DEBUG(channel, "%s", msg);<br>
> + if (priv->catch_error && error) {<br>
> /* Remove "usbredirhost: " prefix from usbredirhost messages */<br>
> if (strncmp(msg, "usbredirhost: ", 14) == 0)<br>
> g_set_error_literal(priv-><wbr>catch_error, SPICE_CLIENT_ERROR,<br>
> - SPICE_CLIENT_ERROR_FAILED, msg + 14);<br>
> + SPICE_CLIENT_ERROR_FAILED, msg + 14);<br>
> else<br>
> g_set_error_literal(priv-><wbr>catch_error, SPICE_CLIENT_ERROR,<br>
> - SPICE_CLIENT_ERROR_FAILED, msg);<br>
> - return;<br>
> + SPICE_CLIENT_ERROR_FAILED, msg);<br>
> + priv->catch_error = NULL;<br>
> }<br>
> -<br>
> - switch (level) {<br>
> - case usbredirparser_error:<br>
> - g_critical("%s", msg);<br>
> - break;<br>
> - case usbredirparser_warning:<br>
> - g_warning("%s", msg);<br>
> - break;<br>
> - default:<br>
> - CHANNEL_DEBUG(channel, "%s", msg);<br>
> - }<br>
> -}<br>
> -<br>
> -static int usbredir_read_callback(void *user_data, uint8_t *data, int count)<br>
> -{<br>
> - SpiceUsbredirChannel *channel = user_data;<br>
> - SpiceUsbredirChannelPrivate *priv = channel->priv;<br>
> -<br>
> - count = MIN(priv->read_buf_size, count);<br>
> -<br>
> - if (count != 0) {<br>
> - memcpy(data, priv->read_buf, count);<br>
> - }<br>
> -<br>
> - priv->read_buf_size -= count;<br>
> - if (priv->read_buf_size) {<br>
> - priv->read_buf += count;<br>
> - } else {<br>
> - priv->read_buf = NULL;<br>
> - }<br>
> -<br>
> - return count;<br>
> }<br>
> <br>
> static void usbredir_free_write_cb_data(<wbr>uint8_t *data, void *user_data)<br>
> @@ -659,7 +600,7 @@ static void usbredir_free_write_cb_data(<wbr>uint8_t *data, void *user_data)<br>
> SpiceUsbredirChannel *channel = user_data;<br>
> SpiceUsbredirChannelPrivate *priv = channel->priv;<br>
> <br>
> - usbredirhost_free_write_<wbr>buffer(priv->host, data);<br>
> + spice_usb_backend_return_<wbr>write_data(priv->host, data);<br>
> }<br>
> <br>
> #ifdef USE_LZ4<br>
> @@ -731,7 +672,7 @@ static int usbredir_write_callback(void *user_data, uint8_t *data, int count)<br>
> <br>
> #ifdef USE_LZ4<br>
> if (try_write_compress_LZ4(<wbr>channel, data, count)) {<br>
> - usbredirhost_free_write_<wbr>buffer(channel->priv->host, data);<br>
> + spice_usb_backend_return_<wbr>write_data(channel->priv-><wbr>host, data);<br>
> return count;<br>
> }<br>
> #endif<br>
> @@ -744,15 +685,6 @@ static int usbredir_write_callback(void *user_data, uint8_t *data, int count)<br>
> return count;<br>
> }<br>
> <br>
> -static void *usbredir_alloc_lock(void) {<br>
> - GMutex *mutex;<br>
> -<br>
> - mutex = g_new0(GMutex, 1);<br>
> - g_mutex_init(mutex);<br>
> -<br>
> - return mutex;<br>
> -}<br>
> -<br>
> G_GNUC_INTERNAL<br>
> void spice_usbredir_channel_lock(<wbr>SpiceUsbredirChannel *channel)<br>
> {<br>
> @@ -765,25 +697,6 @@ void spice_usbredir_channel_unlock(<wbr>SpiceUsbredirChannel *channel)<br>
> g_mutex_unlock(&channel->priv-<wbr>>device_connect_mutex);<br>
> }<br>
> <br>
> -static void usbredir_lock_lock(void *user_data) {<br>
> - GMutex *mutex = user_data;<br>
> -<br>
> - g_mutex_lock(mutex);<br>
> -}<br>
> -<br>
> -static void usbredir_unlock_lock(void *user_data) {<br>
> - GMutex *mutex = user_data;<br>
> -<br>
> - g_mutex_unlock(mutex);<br>
> -}<br>
> -<br>
> -static void usbredir_free_lock(void *user_data) {<br>
> - GMutex *mutex = user_data;<br>
> -<br>
> - g_mutex_clear(mutex);<br>
> - g_free(mutex);<br>
> -}<br>
> -<br>
> /* ------------------------------<wbr>------------------------------<wbr>--------- */<br>
> <br>
> typedef struct device_error_data {<br>
> @@ -819,10 +732,14 @@ static void spice_usbredir_channel_up(<wbr>SpiceChannel *c)<br>
> {<br>
> SpiceUsbredirChannel *channel = SPICE_USBREDIR_CHANNEL(c);<br>
> SpiceUsbredirChannelPrivate *priv = channel->priv;<br>
> + SpiceSession *session = spice_channel_get_session(c);<br>
> + SpiceUsbDeviceManager *manager = spice_usb_device_manager_get(<wbr>session, NULL);<br>
> <br>
> g_return_if_fail(priv->host != NULL);<br>
> /* Flush any pending writes */<br>
> - usbredirhost_write_guest_data(<wbr>priv->host);<br>
> + spice_usb_backend_channel_up(<wbr>priv->host);<br>
> + /* Check which existing device can be redirected right now */<br>
> + spice_usb_device_manager_<wbr>check_redir_on_connect(<wbr>manager, c);<br>
> }<br>
> <br>
> static int try_handle_compressed_msg(<wbr>SpiceMsgCompressedData *compressed_data_msg,<br>
> @@ -872,26 +789,20 @@ static void usbredir_handle_msg(<wbr>SpiceChannel *c, SpiceMsgIn *in)<br>
> <br>
> g_return_if_fail(priv->host != NULL);<br>
> <br>
> - /* No recursion allowed! */<br>
> - g_return_if_fail(priv->read_<wbr>buf == NULL);<br>
> -<br>
> if (spice_msg_in_type(in) == SPICE_MSG_SPICEVMC_COMPRESSED_<wbr>DATA) {<br>
> SpiceMsgCompressedData *compressed_data_msg = spice_msg_in_parsed(in);<br>
> if (try_handle_compressed_msg(<wbr>compressed_data_msg, &buf, &size)) {<br>
> - priv->read_buf_size = size;<br>
> - priv->read_buf = buf;<br>
> + /* uncompressed ok*/<br>
> } else {<br>
> - r = usbredirhost_read_parse_error;<br>
> + r = USB_REDIR_ERROR_READ_PARSE;<br>
> }<br>
> } else { /* Regular SPICE_MSG_SPICEVMC_DATA msg */<br>
> buf = spice_msg_in_raw(in, &size);<br>
> - priv->read_buf_size = size;<br>
> - priv->read_buf = buf;<br>
> }<br>
> <br>
> spice_usbredir_channel_lock(<wbr>channel);<br>
> if (r == 0)<br>
> - r = usbredirhost_read_guest_data(<wbr>priv->host);<br>
> + r = spice_usb_backend_provide_<wbr>read_data(priv->host, buf, size);<br>
> if (r != 0) {<br>
> SpiceUsbDevice *spice_device = priv->spice_device;<br>
> device_error_data err_data;<br>
> @@ -905,16 +816,16 @@ static void usbredir_handle_msg(<wbr>SpiceChannel *c, SpiceMsgIn *in)<br>
> <br>
> desc = spice_usb_device_get_<wbr>description(spice_device, NULL);<br>
> switch (r) {<br>
> - case usbredirhost_read_parse_error:<br>
> + case USB_REDIR_ERROR_READ_PARSE:<br>
> err = g_error_new(SPICE_CLIENT_<wbr>ERROR, SPICE_CLIENT_ERROR_FAILED,<br>
> _("usbredir protocol parse error for %s"), desc);<br>
> break;<br>
> - case usbredirhost_read_device_<wbr>rejected:<br>
> + case USB_REDIR_ERROR_DEV_REJECTED:<br>
> err = g_error_new(SPICE_CLIENT_<wbr>ERROR,<br>
> SPICE_CLIENT_ERROR_USB_DEVICE_<wbr>REJECTED,<br>
> _("%s rejected by host"), desc);<br>
> break;<br>
> - case usbredirhost_read_device_lost:<br>
> + case USB_REDIR_ERROR_DEV_LOST:<br>
> err = g_error_new(SPICE_CLIENT_<wbr>ERROR,<br>
> SPICE_CLIENT_ERROR_USB_DEVICE_<wbr>LOST,<br>
> _("%s disconnected (fatal IO error)"), desc);<br>
> diff --git a/src/map-file b/src/map-file<br>
> index cdb81c3..154fd08 100644<br>
> --- a/src/map-file<br>
> +++ b/src/map-file<br>
> @@ -156,6 +156,7 @@ spice_uri_set_scheme;<br>
> spice_uri_set_user;<br>
> spice_uri_to_string;<br>
> spice_usb_device_get_<wbr>description;<br>
> +spice_usb_device_get_info;<br>
> spice_usb_device_get_libusb_<wbr>device;<br>
> spice_usb_device_get_type;<br>
> spice_usb_device_manager_can_<wbr>redirect_device;<br>
> @@ -178,6 +179,14 @@ spice_util_get_version_string;<br>
> spice_util_set_debug;<br>
> spice_uuid_to_string;<br>
> spice_webdav_channel_get_type;<br>
> +spice_usb_device_manager_is_<wbr>device_cd;<br>
> +spice_usb_device_manager_get_<wbr>device_luns;<br>
> +spice_usb_device_manager_add_<wbr>cd_lun;<br>
> +spice_usb_device_manager_<wbr>device_lun_get_info;<br>
> +spice_usb_device_manager_<wbr>device_lun_lock;<br>
> +spice_usb_device_manager_<wbr>device_lun_load;<br>
> +spice_usb_device_manager_<wbr>device_lun_change_media;<br>
> +spice_usb_device_manager_<wbr>device_lun_remove;<br>
> local:<br>
> *;<br>
> };<br>
> diff --git a/src/spice-option.c b/src/spice-option.c<br>
> index 6b400bc..dfa7335 100644<br>
> --- a/src/spice-option.c<br>
> +++ b/src/spice-option.c<br>
> @@ -33,6 +33,7 @@ static char *smartcard_db = NULL;<br>
> static char *smartcard_certificates = NULL;<br>
> static char *usbredir_auto_redirect_filter = NULL;<br>
> static char *usbredir_redirect_on_connect = NULL;<br>
> +static gchar **cd_share_files = NULL;<br>
> static gboolean smartcard = FALSE;<br>
> static gboolean disable_audio = FALSE;<br>
> static gboolean disable_usbredir = FALSE;<br>
> @@ -218,6 +219,8 @@ GOptionGroup* spice_get_option_group(void)<br>
> N_("Filter selecting USB devices to be auto-redirected when plugged in"), N_("<filter-string>") },<br>
> { "spice-usbredir-redirect-on-<wbr>connect", '\0', 0, G_OPTION_ARG_STRING, &usbredir_redirect_on_connect,<br>
> N_("Filter selecting USB devices to redirect on connect"), N_("<filter-string>") },<br>
> + { "spice-share-cd", '\0', 0, G_OPTION_ARG_STRING_ARRAY, &cd_share_files,<br>
> + N_("Name of ISO file or CD/DVD device to share"), N_("<filename> (repeat allowed)") },<br>
> { "spice-cache-size", '\0', 0, G_OPTION_ARG_INT, &cache_size,<br>
> N_("Image cache size (deprecated)"), N_("<bytes>") },<br>
> { "spice-glz-window-size", '\0', 0, G_OPTION_ARG_INT, &glz_window_size,<br>
> @@ -308,6 +311,18 @@ void spice_set_session_option(<wbr>SpiceSession *session)<br>
> g_object_set(m, "redirect-on-connect",<br>
> usbredir_redirect_on_connect, NULL);<br>
> }<br>
> + if (cd_share_files) {<br>
> + SpiceUsbDeviceManager *m = spice_usb_device_manager_get(<wbr>session, NULL);<br>
> + if (m) {<br>
> + gchar **name = cd_share_files;<br>
> + while (name && *name) {<br>
> + g_object_set(m, "share-cd", *name, NULL);<br>
> + name++;<br>
> + }<br>
> + }<br>
> + g_strfreev(cd_share_files);<br>
> + cd_share_files = NULL;<br>
> + }<br>
> if (disable_usbredir)<br>
> g_object_set(session, "enable-usbredir", FALSE, NULL);<br>
> if (disable_audio)<br>
> diff --git a/src/usb-device-manager-priv.<wbr>h b/src/usb-device-manager-priv.<wbr>h<br>
> index 83884d7..53149fb 100644<br>
> --- a/src/usb-device-manager-priv.<wbr>h<br>
> +++ b/src/usb-device-manager-priv.<wbr>h<br>
> @@ -32,9 +32,12 @@ void spice_usb_device_manager_stop_<wbr>event_listening(<br>
> SpiceUsbDeviceManager *manager);<br>
> <br>
> #ifdef USE_USBREDIR<br>
> -#include <libusb.h><br>
> +#include "usb-backend.h"<br>
> void spice_usb_device_manager_<wbr>device_error(<br>
> SpiceUsbDeviceManager *manager, SpiceUsbDevice *device, GError *err);<br>
> +void spice_usb_device_manager_<wbr>check_redir_on_connect(<br>
> + SpiceUsbDeviceManager *manager, SpiceChannel *channel);<br>
> +<br>
> <br>
> guint8 spice_usb_device_get_busnum(<wbr>const SpiceUsbDevice *device);<br>
> guint8 spice_usb_device_get_devaddr(<wbr>const SpiceUsbDevice *device);<br>
> diff --git a/src/usb-device-manager.c b/src/usb-device-manager.c<br>
> index 50fb491..05b38ae 100644<br>
> --- a/src/usb-device-manager.c<br>
> +++ b/src/usb-device-manager.c<br>
> @@ -24,10 +24,11 @@<br>
> #include <glib-object.h><br>
> <br>
> #ifdef USE_USBREDIR<br>
> +<br>
> #include <errno.h><br>
> -#include <libusb.h><br>
> <br>
> #ifdef G_OS_WIN32<br>
> +#include <windows.h><br>
> #include "usbdk_api.h"<br>
> #endif<br>
> <br>
> @@ -41,8 +42,8 @@<br>
> #endif<br>
> <br>
> #include "channel-usbredir-priv.h"<br>
> -#include "usbredirhost.h"<br>
> #include "usbutil.h"<br>
> +<br>
> #endif<br>
> <br>
> #include "spice-session-priv.h"<br>
> @@ -58,6 +59,9 @@<br>
> #define DEV_ID_FMT "0x%04x:0x%04x"<br>
> #endif<br>
> <br>
> +#define CD_SHARE_VENDOR "RedHat Inc."<br>
> +#define CD_SHARE_PRODUCT "Spice CD drive"<br>
> +<br>
> /**<br>
> * SECTION:usb-device-manager<br>
> * @short_description: USB device management<br>
> @@ -85,6 +89,7 @@ enum {<br>
> PROP_AUTO_CONNECT_FILTER,<br>
> PROP_REDIRECT_ON_CONNECT,<br>
> PROP_FREE_CHANNELS,<br>
> + PROP_SHARE_CD<br>
> };<br>
> <br>
> enum<br>
> @@ -93,6 +98,7 @@ enum<br>
> DEVICE_REMOVED,<br>
> AUTO_CONNECT_FAILED,<br>
> DEVICE_ERROR,<br>
> + DEVICE_CHANGED,<br>
> LAST_SIGNAL,<br>
> };<br>
> <br>
> @@ -102,7 +108,7 @@ struct _SpiceUsbDeviceManagerPrivate {<br>
> gchar *auto_connect_filter;<br>
> gchar *redirect_on_connect;<br>
> #ifdef USE_USBREDIR<br>
> - libusb_context *context;<br>
> + SpiceUsbBackend *context;<br>
> int event_listeners;<br>
> GThread *event_thread;<br>
> gint event_thread_run;<br>
> @@ -112,10 +118,9 @@ struct _SpiceUsbDeviceManagerPrivate {<br>
> int redirect_on_connect_rules_<wbr>count;<br>
> #ifdef USE_GUDEV<br>
> GUdevClient *udev;<br>
> - libusb_device **coldplug_list; /* Avoid needless reprobing during init */<br>
> + SpiceUsbBackendDevice **coldplug_list; /* Avoid needless reprobing during init */<br>
> #else<br>
> gboolean redirecting; /* Handled by GUdevClient in the gudev case */<br>
> - libusb_hotplug_callback_handle hp_handle;<br>
> #endif<br>
> #ifdef G_OS_WIN32<br>
> usbdk_api_wrapper *usbdk_api;<br>
> @@ -139,6 +144,7 @@ enum {<br>
> <br>
> #ifdef USE_USBREDIR<br>
> <br>
> +// this is the structure behind SpiceUsbDevice<br>
> typedef struct _SpiceUsbDeviceInfo {<br>
> guint8 busnum;<br>
> guint8 devaddr;<br>
> @@ -148,7 +154,7 @@ typedef struct _SpiceUsbDeviceInfo {<br>
> #ifdef G_OS_WIN32<br>
> guint8 state;<br>
> #else<br>
> - libusb_device *libdev;<br>
> + SpiceUsbBackendDevice *bdev;<br>
> #endif<br>
> gint ref;<br>
> } SpiceUsbDeviceInfo;<br>
> @@ -166,15 +172,13 @@ static void spice_usb_device_manager_<wbr>uevent_cb(GUdevClient *client,<br>
> static void spice_usb_device_manager_add_<wbr>udev(SpiceUsbDeviceManager *self,<br>
> GUdevDevice *udev);<br>
> #else<br>
> -static int spice_usb_device_manager_<wbr>hotplug_cb(libusb_context *ctx,<br>
> - libusb_device *device,<br>
> - libusb_hotplug_event event,<br>
> - void *data);<br>
> +static void spice_usb_device_manager_<wbr>hotplug_cb(<br>
> + void *data,<br>
> + SpiceUsbBackendDevice *bdev,<br>
> + gboolean added);<br>
> #endif<br>
> -static void spice_usb_device_manager_<wbr>check_redir_on_connect(<br>
> - SpiceUsbDeviceManager *self, SpiceChannel *channel);<br>
> <br>
> -static SpiceUsbDeviceInfo *spice_usb_device_new(libusb_<wbr>device *libdev);<br>
> +static SpiceUsbDeviceInfo *spice_usb_device_new(<wbr>SpiceUsbBackendDevice *bdev);<br>
> static SpiceUsbDevice *spice_usb_device_ref(<wbr>SpiceUsbDevice *device);<br>
> static void spice_usb_device_unref(<wbr>SpiceUsbDevice *device);<br>
> <br>
> @@ -183,11 +187,11 @@ static void _usbdk_hider_update(<wbr>SpiceUsbDeviceManager *manager);<br>
> static void _usbdk_hider_clear(<wbr>SpiceUsbDeviceManager *manager);<br>
> #endif<br>
> <br>
> -static gboolean spice_usb_manager_device_<wbr>equal_libdev(<wbr>SpiceUsbDeviceManager *manager,<br>
> +static gboolean spice_usb_manager_device_<wbr>equal_bdev(<wbr>SpiceUsbDeviceManager *manager,<br>
> SpiceUsbDevice *device,<br>
> - libusb_device *libdev);<br>
> -static libusb_device *<br>
> -spice_usb_device_manager_<wbr>device_to_libdev(<wbr>SpiceUsbDeviceManager *self,<br>
> + SpiceUsbBackendDevice *bdev);<br>
> +static SpiceUsbBackendDevice*<br>
> +spice_usb_device_manager_<wbr>device_to_bdev(<wbr>SpiceUsbDeviceManager *self,<br>
> SpiceUsbDevice *device);<br>
> <br>
> static void<br>
> @@ -205,6 +209,9 @@ static<br>
> void disconnect_device_sync(<wbr>SpiceUsbDeviceManager *self,<br>
> SpiceUsbDevice *device);<br>
> <br>
> +static<br>
> +void on_device_change(void *self, SpiceUsbBackendDevice *bdev);<br>
> +<br>
> G_DEFINE_BOXED_TYPE(<wbr>SpiceUsbDevice, spice_usb_device,<br>
> (GBoxedCopyFunc)spice_usb_<wbr>device_ref,<br>
> (GBoxedFreeFunc)spice_usb_<wbr>device_unref)<br>
> @@ -288,26 +295,17 @@ static gboolean spice_usb_device_manager_<wbr>initable_init(GInitable *initable,<br>
> SpiceUsbDeviceManagerPrivate *priv = self->priv;<br>
> GList *list;<br>
> GList *it;<br>
> - int rc;<br>
> #ifdef USE_GUDEV<br>
> const gchar *const subsystems[] = {"usb", NULL};<br>
> #endif<br>
> <br>
> - /* Initialize libusb */<br>
> - rc = libusb_init(&priv->context);<br>
> - if (rc < 0) {<br>
> - const char *desc = spice_usbutil_libusb_strerror(<wbr>rc);<br>
> - g_warning("Error initializing USB support: %s [%i]", desc, rc);<br>
> - g_set_error(err, SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,<br>
> - "Error initializing USB support: %s [%i]", desc, rc);<br>
> + /* Initialize spice backend */<br>
> + priv->context = spice_usb_backend_initialize()<wbr>;<br>
> + if (!priv->context) {<br>
> return FALSE;<br>
> }<br>
> -<br>
> -#ifdef G_OS_WIN32<br>
> -#if LIBUSB_API_VERSION >= 0x01000106<br>
> - libusb_set_option(priv-><wbr>context, LIBUSB_OPTION_USE_USBDK);<br>
> -#endif<br>
> -#endif<br>
> + spice_usb_backend_set_device_<wbr>change_callback(priv->context,<br>
> + self, on_device_change);<br>
> <br>
> /* Start listening for usb devices plug / unplug */<br>
> #ifdef USE_GUDEV<br>
> @@ -319,26 +317,20 @@ static gboolean spice_usb_device_manager_<wbr>initable_init(GInitable *initable,<br>
> g_signal_connect(G_OBJECT(<wbr>priv->udev), "uevent",<br>
> G_CALLBACK(spice_usb_device_<wbr>manager_uevent_cb), self);<br>
> /* Do coldplug (detection of already connected devices) */<br>
> - libusb_get_device_list(priv-><wbr>context, &priv->coldplug_list);<br>
> + priv->coldplug_list = spice_usb_backend_get_device_<wbr>list(priv->context);<br>
> list = g_udev_client_query_by_<wbr>subsystem(priv->udev, "usb");<br>
> for (it = g_list_first(list); it; it = g_list_next(it)) {<br>
> spice_usb_device_manager_add_<wbr>udev(self, it->data);<br>
> g_object_unref(it->data);<br>
> }<br>
> g_list_free(list);<br>
> - libusb_free_device_list(priv-><wbr>coldplug_list, 1);<br>
> + spice_usb_backend_free_device_<wbr>list(priv->coldplug_list);<br>
> priv->coldplug_list = NULL;<br>
> #else<br>
> - rc = libusb_hotplug_register_<wbr>callback(priv->context,<br>
> - LIBUSB_HOTPLUG_EVENT_DEVICE_<wbr>ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_<wbr>LEFT,<br>
> - LIBUSB_HOTPLUG_ENUMERATE, LIBUSB_HOTPLUG_MATCH_ANY,<br>
> - LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY,<br>
> - spice_usb_device_manager_<wbr>hotplug_cb, self, &priv->hp_handle);<br>
> - if (rc < 0) {<br>
> - const char *desc = spice_usbutil_libusb_strerror(<wbr>rc);<br>
> - g_warning("Error initializing USB hotplug support: %s [%i]", desc, rc);<br>
> + if (!spice_usb_backend_handle_<wbr>hotplug(priv->context,<br>
> + self, spice_usb_device_manager_<wbr>hotplug_cb)) {<br>
> g_set_error(err, SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,<br>
> - "Error initializing USB hotplug support: %s [%i]", desc, rc);<br>
> + "Error initializing USB hotplug support");<br>
> return FALSE;<br>
> }<br>
> spice_usb_device_manager_<wbr>start_event_listening(self, NULL);<br>
> @@ -369,20 +361,20 @@ static void spice_usb_device_manager_<wbr>dispose(GObject *gobject)<br>
> SpiceUsbDeviceManagerPrivate *priv = self->priv;<br>
> <br>
> #ifdef USE_LIBUSB_HOTPLUG<br>
> - if (priv->hp_handle) {<br>
> - spice_usb_device_manager_stop_<wbr>event_listening(self);<br>
> - if (g_atomic_int_get(&priv-><wbr>event_thread_run)) {<br>
> - /* Force termination of the event thread even if there were some<br>
> - * mismatched spice_usb_device_manager_{<wbr>start,stop}_event_listening<br>
> - * calls. Otherwise, the usb event thread will be leaked, and will<br>
> - * try to use the libusb context we destroy in finalize(), which would<br>
> - * cause a crash */<br>
> - g_warn_if_reached();<br>
> - g_atomic_int_set(&priv->event_<wbr>thread_run, FALSE);<br>
> - }<br>
> - /* This also wakes up the libusb_handle_events() in the event_thread */<br>
> - libusb_hotplug_deregister_<wbr>callback(priv->context, priv->hp_handle);<br>
> - priv->hp_handle = 0;<br>
> + // TODO: check in case the initial spice_usb_backend_handle_<wbr>hotplug fails<br>
> +<br>
> + spice_usb_device_manager_stop_<wbr>event_listening(self);<br>
> + if (g_atomic_int_get(&priv-><wbr>event_thread_run)) {<br>
> + /* Force termination of the event thread even if there were some<br>
> + * mismatched spice_usb_device_manager_{<wbr>start,stop}_event_listening<br>
> + * calls. Otherwise, the usb event thread will be leaked, and will<br>
> + * try to use the libusb context we destroy in finalize(), which would<br>
> + * cause a crash */<br>
> + g_warn_if_reached();<br>
> + g_atomic_int_set(&priv->event_<wbr>thread_run, FALSE);<br>
> +<br>
> + /* This also wakes up the libusb_handle_events() in the event_thread */<br>
> + spice_usb_backend_handle_<wbr>hotplug(priv->context, NULL, NULL);<br>
> }<br>
> #endif<br>
> if (priv->event_thread) {<br>
> @@ -411,8 +403,9 @@ static void spice_usb_device_manager_<wbr>finalize(GObject *gobject)<br>
> g_clear_object(&priv->udev);<br>
> #endif<br>
> g_return_if_fail(priv->event_<wbr>thread == NULL);<br>
> - if (priv->context)<br>
> - libusb_exit(priv->context);<br>
> + if (priv->context) {<br>
> + spice_usb_backend_finalize(<wbr>priv->context);<br>
> + }<br>
> free(priv->auto_conn_filter_<wbr>rules);<br>
> free(priv->redirect_on_<wbr>connect_rules);<br>
> #ifdef G_OS_WIN32<br>
> @@ -455,6 +448,10 @@ static void spice_usb_device_manager_get_<wbr>property(GObject *gobject,<br>
> case PROP_REDIRECT_ON_CONNECT:<br>
> g_value_set_string(value, priv->redirect_on_connect);<br>
> break;<br>
> + case PROP_SHARE_CD:<br>
> + /* get_property is not needed */<br>
> + g_value_set_string(value, "");<br>
> + break;<br>
> case PROP_FREE_CHANNELS: {<br>
> int free_channels = 0;<br>
> #ifdef USE_USBREDIR<br>
> @@ -545,6 +542,20 @@ static void spice_usb_device_manager_set_<wbr>property(GObject *gobject,<br>
> priv->redirect_on_connect = g_strdup(filter);<br>
> break;<br>
> }<br>
> + case PROP_SHARE_CD:<br>
> + {<br>
> +#ifdef USE_USBREDIR<br>
> + SpiceUsbDeviceLunInfo info = { 0 };<br>
> + const gchar *name = g_value_get_string(value);<br>
> + /* the string is temporary, no need to keep it */<br>
> + SPICE_DEBUG("share_cd set to %s", name);<br>
> + info.started = TRUE;<br>
> + info.loaded = TRUE;<br>
> + info.file_path = name;<br>
> + spice_usb_backend_add_cd_lun(<wbr>priv->context, &info);<br>
> +#endif<br>
> + break;<br>
> + }<br>
> default:<br>
> G_OBJECT_WARN_INVALID_<wbr>PROPERTY_ID(gobject, prop_id, pspec);<br>
> break;<br>
> @@ -636,6 +647,18 @@ static void spice_usb_device_manager_<wbr>class_init(<wbr>SpiceUsbDeviceManagerClass *klas<br>
> pspec);<br>
> <br>
> /**<br>
> + * SpiceUsbDeviceManager:share-<wbr>cd:<br>
> + *<br>
> + * Set a string specifying a filename (ISO) or physical CD/DVD device<br>
> + * to share via USB after a Spice connection has been established.<br>
> + *<br>
> + */<br>
> + pspec = g_param_spec_string("share-cd"<wbr>, "Share ISO file or device as CD",<br>
> + "File or device name to share", NULL,<br>
> + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);<br>
> + g_object_class_install_<wbr>property(gobject_class, PROP_SHARE_CD, pspec);<br>
> +<br>
> + /**<br>
> * SpiceUsbDeviceManager:free-<wbr>channels:<br>
> *<br>
> * Get the number of available channels for redirecting USB devices.<br>
> @@ -732,12 +755,32 @@ static void spice_usb_device_manager_<wbr>class_init(<wbr>SpiceUsbDeviceManagerClass *klas<br>
> 2,<br>
> SPICE_TYPE_USB_DEVICE,<br>
> G_TYPE_ERROR);<br>
> +<br>
> + /**<br>
> + * SpiceUsbDeviceManager::device-<wbr>changed:<br>
> + * @manager: #SpiceUsbDeviceManager that emitted the signal<br>
> + * @device: #SpiceUsbDevice boxed object corresponding to the device which was changed<br>
> + *<br>
> + * The #SpiceUsbDeviceManager::<wbr>device-changed signal is emitted whenever<br>
> + * the change happens with one or more logical CD units of the device.<br>
> + * Applicable only to emulated CD sharing devices<br>
> + **/<br>
> + signals[DEVICE_CHANGED] =<br>
> + g_signal_new("device-changed",<br>
> + G_OBJECT_CLASS_TYPE(gobject_<wbr>class),<br>
> + G_SIGNAL_RUN_FIRST,<br>
> + G_STRUCT_OFFSET(<wbr>SpiceUsbDeviceManagerClass, device_changed),<br>
> + NULL, NULL,<br>
> + g_cclosure_marshal_VOID__<wbr>BOXED,<br>
> + G_TYPE_NONE,<br>
> + 1,<br>
> + SPICE_TYPE_USB_DEVICE);<br>
> }<br>
> <br>
> #ifdef USE_USBREDIR<br>
> <br>
> /* ------------------------------<wbr>------------------------------<wbr>------ */<br>
> -/* gudev / libusb Helper functions */<br>
> +/* gudev / backend Helper functions */<br>
> <br>
> #ifdef USE_GUDEV<br>
> static gboolean spice_usb_device_manager_get_<wbr>udev_bus_n_address(<br>
> @@ -761,30 +804,6 @@ static gboolean spice_usb_device_manager_get_<wbr>udev_bus_n_address(<br>
> }<br>
> #endif<br>
> <br>
> -static gboolean spice_usb_device_manager_get_<wbr>device_descriptor(<br>
> - libusb_device *libdev,<br>
> - struct libusb_device_descriptor *desc)<br>
> -{<br>
> - int errcode;<br>
> - const gchar *errstr;<br>
> -<br>
> - g_return_val_if_fail(libdev != NULL, FALSE);<br>
> - g_return_val_if_fail(desc != NULL, FALSE);<br>
> -<br>
> - errcode = libusb_get_device_descriptor(<wbr>libdev, desc);<br>
> - if (errcode < 0) {<br>
> - int bus, addr;<br>
> -<br>
> - bus = libusb_get_bus_number(libdev);<br>
> - addr = libusb_get_device_address(<wbr>libdev);<br>
> - errstr = spice_usbutil_libusb_strerror(<wbr>errcode);<br>
> - g_warning("cannot get device descriptor for (%p) %d.%d -- %s(%d)",<br>
> - libdev, bus, addr, errstr, errcode);<br>
> - return FALSE;<br>
> - }<br>
> - return TRUE;<br>
> -}<br>
> -<br>
> #endif // USE_USBREDIR<br>
> <br>
> /**<br>
> @@ -806,34 +825,13 @@ spice_usb_device_get_libusb_<wbr>device(const SpiceUsbDevice *device G_GNUC_UNUSED)<br>
> <br>
> g_return_val_if_fail(info != NULL, FALSE);<br>
> <br>
> - return info->libdev;<br>
> + return spice_usb_backend_device_get_<wbr>libdev(info->bdev);<br>
> #endif<br>
> #endif<br>
> return NULL;<br>
> }<br>
> <br>
> #ifdef USE_USBREDIR<br>
> -static gboolean spice_usb_device_manager_get_<wbr>libdev_vid_pid(<br>
> - libusb_device *libdev, int *vid, int *pid)<br>
> -{<br>
> - struct libusb_device_descriptor desc;<br>
> -<br>
> - g_return_val_if_fail(libdev != NULL, FALSE);<br>
> - g_return_val_if_fail(vid != NULL, FALSE);<br>
> - g_return_val_if_fail(pid != NULL, FALSE);<br>
> -<br>
> - *vid = *pid = 0;<br>
> -<br>
> - if (!spice_usb_device_manager_<wbr>get_device_descriptor(libdev, &desc)) {<br>
> - return FALSE;<br>
> - }<br>
> - *vid = desc.idVendor;<br>
> - *pid = desc.idProduct;<br>
> -<br>
> - return TRUE;<br>
> -}<br>
> -<br>
> -/* ------------------------------<wbr>------------------------------<wbr>------ */<br>
> /* callbacks */<br>
> <br>
> static void channel_new(SpiceSession *session, SpiceChannel *channel,<br>
> @@ -849,10 +847,8 @@ static void channel_new(SpiceSession *session, SpiceChannel *channel,<br>
> spice_channel_connect(channel)<wbr>;<br>
> g_ptr_array_add(self->priv-><wbr>channels, channel);<br>
> <br>
> - spice_usb_device_manager_<wbr>check_redir_on_connect(self, channel);<br>
> -<br>
> /*<br>
> - * add a reference to ourself, to make sure the libusb context is<br>
> + * add a reference to ourself, to make sure the backend device context is<br>
> * alive as long as the channel is.<br>
> * TODO: moving to gusb could help here too.<br>
> */<br>
> @@ -889,6 +885,9 @@ static void spice_usb_device_manager_auto_<wbr>connect_cb(GObject *gobject,<br>
> g_signal_emit(self, signals[AUTO_CONNECT_FAILED], 0, device, err);<br>
> g_error_free(err);<br>
> }<br>
> + /* let widget update itself */<br>
> + g_signal_emit(self, signals[DEVICE_CHANGED], 0, device);<br>
> +<br>
> spice_usb_device_unref(device)<wbr>;<br>
> }<br>
> <br>
> @@ -902,12 +901,12 @@ spice_usb_device_manager_<wbr>device_match(<wbr>SpiceUsbDeviceManager *self, SpiceUsbDevic<br>
> <br>
> #ifdef USE_GUDEV<br>
> static gboolean<br>
> -spice_usb_device_manager_<wbr>libdev_match(<wbr>SpiceUsbDeviceManager *self, libusb_device *libdev,<br>
> +spice_usb_device_manager_<wbr>bdev_match(<wbr>SpiceUsbDeviceManager *self, SpiceUsbBackendDevice *dev,<br>
> const int bus, const int address)<br>
> {<br>
> + const UsbDeviceInformation* info = spice_usb_backend_device_get_<wbr>info(dev);<br>
> /* match functions for Linux/UsbDk -- match by bus.addr */<br>
> - return (libusb_get_bus_number(libdev) == bus &&<br>
> - libusb_get_device_address(<wbr>libdev) == address);<br>
> + return (info->bus == bus && info->address == address);<br>
> }<br>
> #endif<br>
> <br>
> @@ -929,36 +928,36 @@ spice_usb_device_manager_find_<wbr>device(SpiceUsbDeviceManager *self,<br>
> return device;<br>
> }<br>
> <br>
> -static void spice_usb_device_manager_add_<wbr>dev(SpiceUsbDeviceManager *self,<br>
> - libusb_device *libdev)<br>
> +static void spice_usb_device_manager_add_<wbr>dev(<br>
> + SpiceUsbDeviceManager *self,<br>
> + SpiceUsbBackendDevice *bdev)<br>
> {<br>
> SpiceUsbDeviceManagerPrivate *priv = self->priv;<br>
> - struct libusb_device_descriptor desc;<br>
> SpiceUsbDevice *device;<br>
> -<br>
> - if (!spice_usb_device_manager_<wbr>get_device_descriptor(libdev, &desc))<br>
> - return;<br>
> + const UsbDeviceInformation* info = spice_usb_backend_device_get_<wbr>info(bdev);<br>
> + // try redirecting shared CD on creation, if filter allows<br>
> + gboolean always_redirect = info->max_luns != 0;<br>
> <br>
> /* Skip hubs */<br>
> - if (desc.bDeviceClass == LIBUSB_CLASS_HUB)<br>
> + if (spice_usb_backend_device_is_<wbr>hub(bdev))<br>
> return;<br>
> <br>
> - device = (SpiceUsbDevice*)spice_usb_<wbr>device_new(libdev);<br>
> + device = (SpiceUsbDevice*)spice_usb_<wbr>device_new(bdev);<br>
> if (!device)<br>
> return;<br>
> <br>
> g_ptr_array_add(priv->devices, device);<br>
> <br>
> - if (priv->auto_connect) {<br>
> + if (priv->auto_connect || always_redirect) {<br>
> gboolean can_redirect, auto_ok;<br>
> <br>
> can_redirect = spice_usb_device_manager_can_<wbr>redirect_device(<br>
> self, device, NULL);<br>
> <br>
> - auto_ok = usbredirhost_check_device_<wbr>filter(<br>
> - priv->auto_conn_filter_rules,<br>
> - priv->auto_conn_filter_rules_<wbr>count,<br>
> - libdev, 0) == 0;<br>
> + auto_ok = spice_usb_backend_device_<wbr>check_filter(<br>
> + bdev,<br>
> + priv->auto_conn_filter_rules,<br>
> + priv->auto_conn_filter_rules_<wbr>count) == 0;<br>
> <br>
> if (can_redirect && auto_ok)<br>
> spice_usb_device_manager_<wbr>connect_device_async(self,<br>
> @@ -1005,7 +1004,7 @@ static void spice_usb_device_manager_add_<wbr>udev(SpiceUsbDeviceManager *self,<br>
> GUdevDevice *udev)<br>
> {<br>
> SpiceUsbDeviceManagerPrivate *priv = self->priv;<br>
> - libusb_device *libdev = NULL, **dev_list = NULL;<br>
> + SpiceUsbBackendDevice *devarrived = NULL, **dev_list = NULL;<br>
> SpiceUsbDevice *device;<br>
> const gchar *devtype;<br>
> int i, bus, address;<br>
> @@ -1033,23 +1032,23 @@ static void spice_usb_device_manager_add_<wbr>udev(SpiceUsbDeviceManager *self,<br>
> if (priv->coldplug_list)<br>
> dev_list = priv->coldplug_list;<br>
> else<br>
> - libusb_get_device_list(priv-><wbr>context, &dev_list);<br>
> + dev_list = spice_usb_backend_get_device_<wbr>list(priv->context);<br>
> <br>
> for (i = 0; dev_list && dev_list[i]; i++) {<br>
> - if (spice_usb_device_manager_<wbr>libdev_match(self, dev_list[i], bus, address)) {<br>
> - libdev = dev_list[i];<br>
> + if (spice_usb_device_manager_<wbr>bdev_match(self, dev_list[i], bus, address)) {<br>
> + devarrived = dev_list[i];<br>
> break;<br>
> }<br>
> }<br>
> <br>
> - if (libdev)<br>
> - spice_usb_device_manager_add_<wbr>dev(self, libdev);<br>
> + if (devarrived)<br>
> + spice_usb_device_manager_add_<wbr>dev(self, devarrived);<br>
> else<br>
> g_warning("Could not find USB device to add " DEV_ID_FMT,<br>
> (guint) bus, (guint) address);<br>
> <br>
> if (!priv->coldplug_list)<br>
> - libusb_free_device_list(dev_<wbr>list, 1);<br>
> + spice_usb_backend_free_device_<wbr>list(dev_list);<br>
> }<br>
> <br>
> static void spice_usb_device_manager_<wbr>remove_udev(<wbr>SpiceUsbDeviceManager *self,<br>
> @@ -1078,8 +1077,8 @@ static void spice_usb_device_manager_<wbr>uevent_cb(GUdevClient *client,<br>
> #else<br>
> struct hotplug_idle_cb_args {<br>
> SpiceUsbDeviceManager *self;<br>
> - libusb_device *device;<br>
> - libusb_hotplug_event event;<br>
> + SpiceUsbBackendDevice *device;<br>
> + gboolean device_added;<br>
> };<br>
> <br>
> static gboolean spice_usb_device_manager_<wbr>hotplug_idle_cb(gpointer user_data)<br>
> @@ -1087,36 +1086,34 @@ static gboolean spice_usb_device_manager_<wbr>hotplug_idle_cb(gpointer user_data)<br>
> struct hotplug_idle_cb_args *args = user_data;<br>
> SpiceUsbDeviceManager *self = SPICE_USB_DEVICE_MANAGER(args-<wbr>>self);<br>
> <br>
> - switch (args->event) {<br>
> - case LIBUSB_HOTPLUG_EVENT_DEVICE_<wbr>ARRIVED:<br>
> + if (args->device_added) {<br>
> spice_usb_device_manager_add_<wbr>dev(self, args->device);<br>
> - break;<br>
> - case LIBUSB_HOTPLUG_EVENT_DEVICE_<wbr>LEFT:<br>
> + } else {<br>
> + const UsbDeviceInformation *info = spice_usb_backend_device_get_<wbr>info(args->device);<br>
> spice_usb_device_manager_<wbr>remove_dev(self,<br>
> - libusb_get_bus_number(args-><wbr>device),<br>
> - libusb_get_device_address(<wbr>args->device));<br>
> - break;<br>
> + info->bus,<br>
> + info->address);<br>
> }<br>
> - libusb_unref_device(args-><wbr>device);<br>
> + spice_usb_backend_device_<wbr>release(args->device);<br>
> g_object_unref(self);<br>
> g_free(args);<br>
> return FALSE;<br>
> }<br>
> <br>
> /* Can be called from both the main-thread as well as the event_thread */<br>
> -static int spice_usb_device_manager_<wbr>hotplug_cb(libusb_context *ctx,<br>
> - libusb_device *device,<br>
> - libusb_hotplug_event event,<br>
> - void *user_data)<br>
> +static void spice_usb_device_manager_<wbr>hotplug_cb(<br>
> + void *user_data,<br>
> + SpiceUsbBackendDevice *bdev,<br>
> + gboolean added)<br>
> {<br>
> SpiceUsbDeviceManager *self = SPICE_USB_DEVICE_MANAGER(user_<wbr>data);<br>
> struct hotplug_idle_cb_args *args = g_malloc0(sizeof(*args));<br>
> <br>
> args->self = g_object_ref(self);<br>
> - args->device = libusb_ref_device(device);<br>
> - args->event = event;<br>
> + spice_usb_backend_device_<wbr>acquire(bdev);<br>
> + args->device_added = added;<br>
> + args->device = bdev;<br>
> g_idle_add(spice_usb_device_<wbr>manager_hotplug_idle_cb, args);<br>
> - return 0;<br>
> }<br>
> #endif // USE_USBREDIR<br>
> <br>
> @@ -1143,13 +1140,9 @@ static gpointer spice_usb_device_manager_usb_<wbr>ev_thread(gpointer user_data)<br>
> {<br>
> SpiceUsbDeviceManager *self = SPICE_USB_DEVICE_MANAGER(user_<wbr>data);<br>
> SpiceUsbDeviceManagerPrivate *priv = self->priv;<br>
> - int rc;<br>
> <br>
> while (g_atomic_int_get(&priv-><wbr>event_thread_run)) {<br>
> - rc = libusb_handle_events(priv-><wbr>context);<br>
> - if (rc && rc != LIBUSB_ERROR_INTERRUPTED) {<br>
> - const char *desc = spice_usbutil_libusb_strerror(<wbr>rc);<br>
> - g_warning("Error handling USB events: %s [%i]", desc, rc);<br>
> + if (!spice_usb_backend_handle_<wbr>events(priv->context)) {<br>
> break;<br>
> }<br>
> }<br>
> @@ -1194,13 +1187,13 @@ void spice_usb_device_manager_stop_<wbr>event_listening(<br>
> g_atomic_int_set(&priv->event_<wbr>thread_run, FALSE);<br>
> }<br>
> <br>
> -static void spice_usb_device_manager_<wbr>check_redir_on_connect(<br>
> +void spice_usb_device_manager_<wbr>check_redir_on_connect(<br>
> SpiceUsbDeviceManager *self, SpiceChannel *channel)<br>
> {<br>
> SpiceUsbDeviceManagerPrivate *priv = self->priv;<br>
> GTask *task;<br>
> SpiceUsbDevice *device;<br>
> - libusb_device *libdev;<br>
> + SpiceUsbBackendDevice *dev;<br>
> guint i;<br>
> <br>
> if (priv->redirect_on_connect == NULL)<br>
> @@ -1212,15 +1205,15 @@ static void spice_usb_device_manager_<wbr>check_redir_on_connect(<br>
> if (spice_usb_device_manager_is_<wbr>device_connected(self, device))<br>
> continue;<br>
> <br>
> - libdev = spice_usb_device_manager_<wbr>device_to_libdev(self, device);<br>
> + dev = spice_usb_device_manager_<wbr>device_to_bdev(self, device);<br>
> #ifdef G_OS_WIN32<br>
> - if (libdev == NULL)<br>
> + if (dev == NULL)<br>
> continue;<br>
> #endif<br>
> - if (usbredirhost_check_device_<wbr>filter(<br>
> - priv->redirect_on_connect_<wbr>rules,<br>
> - priv->redirect_on_connect_<wbr>rules_count,<br>
> - libdev, 0) == 0) {<br>
> + if (spice_usb_backend_device_<wbr>check_filter(<br>
> + dev,<br>
> + priv->redirect_on_connect_<wbr>rules,<br>
> + priv->redirect_on_connect_<wbr>rules_count) == 0) {<br>
> /* Note: re-uses spice_usb_device_manager_<wbr>connect_device_async's<br>
> completion handling code! */<br>
> task = g_task_new(self,<br>
> @@ -1230,14 +1223,14 @@ static void spice_usb_device_manager_<wbr>check_redir_on_connect(<br>
> <br>
> spice_usbredir_channel_<wbr>connect_device_async(<br>
> SPICE_USBREDIR_CHANNEL(<wbr>channel),<br>
> - libdev, device, NULL,<br>
> + dev, device, NULL,<br>
> spice_usb_device_manager_<wbr>channel_connect_cb,<br>
> task);<br>
> - libusb_unref_device(libdev);<br>
> + spice_usb_backend_device_<wbr>release(dev);<br>
> return; /* We've taken the channel! */<br>
> }<br>
> <br>
> - libusb_unref_device(libdev);<br>
> + spice_usb_backend_device_<wbr>release(dev);<br>
> }<br>
> }<br>
> <br>
> @@ -1261,8 +1254,8 @@ static SpiceUsbredirChannel *spice_usb_device_manager_get_<wbr>channel_for_dev(<br>
> for (i = 0; i < priv->channels->len; i++) {<br>
> SpiceUsbredirChannel *channel = g_ptr_array_index(priv-><wbr>channels, i);<br>
> spice_usbredir_channel_lock(<wbr>channel);<br>
> - libusb_device *libdev = spice_usbredir_channel_get_<wbr>device(channel);<br>
> - if (spice_usb_manager_device_<wbr>equal_libdev(manager, device, libdev)) {<br>
> + SpiceUsbBackendDevice *dev = spice_usbredir_channel_get_<wbr>device(channel);<br>
> + if (spice_usb_manager_device_<wbr>equal_bdev(manager, device, dev)) {<br>
> spice_usbredir_channel_unlock(<wbr>channel);<br>
> return channel;<br>
> }<br>
> @@ -1319,13 +1312,13 @@ GPtrArray* spice_usb_device_manager_get_<wbr>devices_with_filter(<br>
> SpiceUsbDevice *device = g_ptr_array_index(priv-><wbr>devices, i);<br>
> <br>
> if (rules) {<br>
> - libusb_device *libdev =<br>
> - spice_usb_device_manager_<wbr>device_to_libdev(self, device);<br>
> + SpiceUsbBackendDevice *bdev =<br>
> + spice_usb_device_manager_<wbr>device_to_bdev(self, device);<br>
> #ifdef G_OS_WIN32<br>
> - if (libdev == NULL)<br>
> + if (bdev == NULL)<br>
> continue;<br>
> #endif<br>
> - if (usbredirhost_check_device_<wbr>filter(rules, count, libdev, 0) != 0)<br>
> + if (spice_usb_backend_device_<wbr>check_filter(bdev, rules, count) != 0)<br>
> continue;<br>
> }<br>
> g_ptr_array_add(devices_copy, spice_usb_device_ref(device));<br>
> @@ -1399,7 +1392,7 @@ _spice_usb_device_manager_<wbr>connect_device_async(<wbr>SpiceUsbDeviceManager *self,<br>
> task = g_task_new(self, cancellable, callback, user_data);<br>
> <br>
> SpiceUsbDeviceManagerPrivate *priv = self->priv;<br>
> - libusb_device *libdev;<br>
> + SpiceUsbBackendDevice *bdev;<br>
> guint i;<br>
> <br>
> if (spice_usb_device_manager_is_<wbr>device_connected(self, device)) {<br>
> @@ -1415,9 +1408,9 @@ _spice_usb_device_manager_<wbr>connect_device_async(<wbr>SpiceUsbDeviceManager *self,<br>
> if (spice_usbredir_channel_get_<wbr>device(channel))<br>
> continue; /* Skip already used channels */<br>
> <br>
> - libdev = spice_usb_device_manager_<wbr>device_to_libdev(self, device);<br>
> + bdev = spice_usb_device_manager_<wbr>device_to_bdev(self, device);<br>
> #ifdef G_OS_WIN32<br>
> - if (libdev == NULL) {<br>
> + if (bdev == NULL) {<br>
> /* Most likely, the device was plugged out at driver installation<br>
> * time, and its remove-device event was ignored.<br>
> * So remove the device now<br>
> @@ -1435,12 +1428,12 @@ _spice_usb_device_manager_<wbr>connect_device_async(<wbr>SpiceUsbDeviceManager *self,<br>
> }<br>
> #endif<br>
> spice_usbredir_channel_<wbr>connect_device_async(channel,<br>
> - libdev,<br>
> + bdev,<br>
> device,<br>
> cancellable,<br>
> spice_usb_device_manager_<wbr>channel_connect_cb,<br>
> task);<br>
> - libusb_unref_device(libdev);<br>
> + spice_usb_backend_device_<wbr>release(bdev);<br>
> return;<br>
> }<br>
> <br>
> @@ -1702,20 +1695,20 @@ spice_usb_device_manager_can_<wbr>redirect_device(<wbr>SpiceUsbDeviceManager *self,<br>
> <br>
> if (guest_filter_rules) {<br>
> gboolean filter_ok;<br>
> - libusb_device *libdev;<br>
> + SpiceUsbBackendDevice *bdev;<br>
> <br>
> - libdev = spice_usb_device_manager_<wbr>device_to_libdev(self, device);<br>
> + bdev = spice_usb_device_manager_<wbr>device_to_bdev(self, device);<br>
> #ifdef G_OS_WIN32<br>
> - if (libdev == NULL) {<br>
> + if (bdev == NULL) {<br>
> g_set_error_literal(err, SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,<br>
> _("Some USB devices were not found"));<br>
> return FALSE;<br>
> }<br>
> #endif<br>
> - filter_ok = (usbredirhost_check_device_<wbr>filter(<br>
> - guest_filter_rules, guest_filter_rules_count,<br>
> - libdev, 0) == 0);<br>
> - libusb_unref_device(libdev);<br>
> + filter_ok = (spice_usb_backend_device_<wbr>check_filter(<br>
> + bdev,<br>
> + guest_filter_rules, guest_filter_rules_count) == 0);<br>
> + spice_usb_backend_device_<wbr>release(bdev);<br>
> if (!filter_ok) {<br>
> g_set_error_literal(err, SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,<br>
> _("Some USB devices are blocked by host policy"));<br>
> @@ -1774,6 +1767,7 @@ gchar *spice_usb_device_get_<wbr>description(SpiceUsbDevice *device, const gchar *for<br>
> #ifdef USE_USBREDIR<br>
> guint16 bus, address, vid, pid;<br>
> gchar *description, *descriptor, *manufacturer = NULL, *product = NULL;<br>
> + UsbDeviceInformation info;<br>
> <br>
> g_return_val_if_fail(device != NULL, NULL);<br>
> <br>
> @@ -1788,8 +1782,13 @@ gchar *spice_usb_device_get_<wbr>description(SpiceUsbDevice *device, const gchar *for<br>
> descriptor = g_strdup("");<br>
> }<br>
> <br>
> - spice_usb_util_get_device_<wbr>strings(bus, address, vid, pid,<br>
> - &manufacturer, &product);<br>
> + if (spice_usb_backend_device_get_<wbr>info_by_address(bus, address, &info) && info.max_luns) {<br>
> + manufacturer = g_strdup(CD_SHARE_VENDOR);<br>
> + product = g_strdup(CD_SHARE_PRODUCT);<br>
> + } else {<br>
> + spice_usb_util_get_device_<wbr>strings(bus, address, vid, pid,<br>
> + &manufacturer, &product, TRUE);<br>
> + }<br>
> <br>
> if (!format)<br>
> format = _("%s %s %s at %d-%d");<br>
> @@ -1806,64 +1805,58 @@ gchar *spice_usb_device_get_<wbr>description(SpiceUsbDevice *device, const gchar *for<br>
> #endif<br>
> }<br>
> <br>
> -<br>
> -<br>
> -#ifdef USE_USBREDIR<br>
> -static gboolean probe_isochronous_endpoint(<wbr>libusb_device *libdev)<br>
> +void spice_usb_device_get_info(<wbr>SpiceUsbDeviceManager *self,<br>
> + SpiceUsbDevice *device,<br>
> + SpiceUsbDeviceDescription *info)<br>
> {<br>
> - struct libusb_config_descriptor *conf_desc;<br>
> - gboolean isoc_found = FALSE;<br>
> - gint i, j, k;<br>
> -<br>
> - g_return_val_if_fail(libdev != NULL, FALSE);<br>
> -<br>
> - if (libusb_get_active_config_<wbr>descriptor(libdev, &conf_desc) != 0) {<br>
> - g_return_val_if_reached(FALSE)<wbr>;<br>
> +#ifdef USE_USBREDIR<br>
> + g_return_if_fail(device != NULL);<br>
> + info->vendor = info->product;<br>
> + info->bus = spice_usb_device_get_busnum(<wbr>device);<br>
> + info->address = spice_usb_device_get_devaddr(<wbr>device);<br>
> + info->vendor_id = spice_usb_device_get_vid(<wbr>device);<br>
> + info->product_id = spice_usb_device_get_pid(<wbr>device);<br>
> +<br>
> + if (spice_usb_device_manager_is_<wbr>device_cd(self, device)) {<br>
> + info->vendor = g_strdup(CD_SHARE_VENDOR);<br>
> + info->product = g_strdup(CD_SHARE_PRODUCT);<br>
> + return;<br>
> }<br>
> -<br>
> - for (i = 0; !isoc_found && i < conf_desc->bNumInterfaces; i++) {<br>
> - for (j = 0; !isoc_found && j < conf_desc->interface[i].num_<wbr>altsetting; j++) {<br>
> - for (k = 0; !isoc_found && k < conf_desc->interface[i].<wbr>altsetting[j].bNumEndpoints;k+<wbr>+) {<br>
> - gint attributes = conf_desc->interface[i].<wbr>altsetting[j].endpoint[k].<wbr>bmAttributes;<br>
> - gint type = attributes & LIBUSB_TRANSFER_TYPE_MASK;<br>
> - if (type == LIBUSB_TRANSFER_TYPE_<wbr>ISOCHRONOUS)<br>
> - isoc_found = TRUE;<br>
> - }<br>
> - }<br>
> + spice_usb_util_get_device_<wbr>strings(info->bus, info->address,<br>
> + info->vendor_id, info->product_id, &info->vendor, &info->product, FALSE);<br>
> + if (!info->vendor) {<br>
> + info->vendor = g_strdup_printf("[%04X]", info->vendor_id);<br>
> }<br>
> -<br>
> - libusb_free_config_descriptor(<wbr>conf_desc);<br>
> - return isoc_found;<br>
> + if (!info->product) {<br>
> + info->product = g_strdup_printf("[%04X]", info->product_id);<br>
> + }<br>
> +#endif<br>
> }<br>
> <br>
> +#ifdef USE_USBREDIR<br>
> +<br>
> /*<br>
> * SpiceUsbDeviceInfo<br>
> */<br>
> -static SpiceUsbDeviceInfo *spice_usb_device_new(libusb_<wbr>device *libdev)<br>
> +static SpiceUsbDeviceInfo *spice_usb_device_new(<wbr>SpiceUsbBackendDevice *bdev)<br>
> {<br>
> SpiceUsbDeviceInfo *info;<br>
> - int vid, pid;<br>
> - guint8 bus, addr;<br>
> -<br>
> - g_return_val_if_fail(libdev != NULL, NULL);<br>
> + const UsbDeviceInformation *devinfo;<br>
> <br>
> - bus = libusb_get_bus_number(libdev);<br>
> - addr = libusb_get_device_address(<wbr>libdev);<br>
> -<br>
> - if (!spice_usb_device_manager_<wbr>get_libdev_vid_pid(libdev, &vid, &pid)) {<br>
> - return NULL;<br>
> - }<br>
> + g_return_val_if_fail(bdev != NULL, NULL);<br>
> + devinfo = spice_usb_backend_device_get_<wbr>info(bdev);<br>
> <br>
> info = g_new0(SpiceUsbDeviceInfo, 1);<br>
> <br>
> - info->busnum = bus;<br>
> - info->devaddr = addr;<br>
> - info->vid = vid;<br>
> - info->pid = pid;<br>
> + info->busnum = devinfo->bus;<br>
> + info->devaddr = devinfo->address;<br>
> + info->vid = devinfo->vid;<br>
> + info->pid = devinfo->pid;<br>
> info->ref = 1;<br>
> - info->isochronous = probe_isochronous_endpoint(<wbr>libdev);<br>
> + info->isochronous = devinfo->isochronous;<br>
> #ifndef G_OS_WIN32<br>
> - info->libdev = libusb_ref_device(libdev);<br>
> + info->bdev = bdev;<br>
> + spice_usb_backend_device_<wbr>acquire(bdev);<br>
> #endif<br>
> <br>
> return info;<br>
> @@ -2001,49 +1994,51 @@ static void spice_usb_device_unref(<wbr>SpiceUsbDevice *device)<br>
> ref_count_is_0 = g_atomic_int_dec_and_test(&<wbr>info->ref);<br>
> if (ref_count_is_0) {<br>
> #ifndef G_OS_WIN32<br>
> - libusb_unref_device(info-><wbr>libdev);<br>
> + spice_usb_backend_device_<wbr>release(info->bdev);<br>
> #endif<br>
> + info->vid = info->pid = 0;<br>
> + SPICE_DEBUG("%s: deleting %p", __FUNCTION__, info);<br>
> g_free(info);<br>
> }<br>
> }<br>
> <br>
> #ifndef G_OS_WIN32 /* Linux -- directly compare libdev */<br>
> static gboolean<br>
> -spice_usb_manager_device_<wbr>equal_libdev(<wbr>SpiceUsbDeviceManager *manager,<br>
> +spice_usb_manager_device_<wbr>equal_bdev(<wbr>SpiceUsbDeviceManager *manager,<br>
> SpiceUsbDevice *device,<br>
> - libusb_device *libdev)<br>
> + SpiceUsbBackendDevice *bdev)<br>
> {<br>
> SpiceUsbDeviceInfo *info = (SpiceUsbDeviceInfo *)device;<br>
> <br>
> - if ((device == NULL) || (libdev == NULL))<br>
> + if ((device == NULL) || (bdev == NULL))<br>
> return FALSE;<br>
> <br>
> - return info->libdev == libdev;<br>
> + return spice_usb_backend_devices_<wbr>same(info->bdev, bdev);<br>
> }<br>
> #else /* Windows -- compare vid:pid of device and libdev */<br>
> static gboolean<br>
> -spice_usb_manager_device_<wbr>equal_libdev(<wbr>SpiceUsbDeviceManager *manager,<br>
> - SpiceUsbDevice *device,<br>
> - libusb_device *libdev)<br>
> +spice_usb_manager_device_<wbr>equal_bdev(<wbr>SpiceUsbDeviceManager *manager,<br>
> + SpiceUsbDevice *device,<br>
> + SpiceUsbBackendDevice *bdev)<br>
> {<br>
> int busnum, devaddr;<br>
> <br>
> - if ((device == NULL) || (libdev == NULL))<br>
> + if ((device == NULL) || (bdev == NULL))<br>
> return FALSE;<br>
> <br>
> busnum = spice_usb_device_get_busnum(<wbr>device);<br>
> devaddr = spice_usb_device_get_devaddr(<wbr>device);<br>
> - return spice_usb_device_manager_<wbr>libdev_match(manager, libdev,<br>
> + return spice_usb_device_manager_bdev_<wbr>match(manager, bdev,<br>
> busnum, devaddr);<br>
> }<br>
> #endif<br>
> <br>
> /*<br>
> - * Caller must libusb_unref_device the libusb_device returned by this function.<br>
> - * Returns a libusb_device, or NULL upon failure<br>
> + * Caller must spice_usb_backend_device_<wbr>release the SpiceUsbBackendDevice returned by this function.<br>
> + * Returns a SpiceUsbBackendDevice, or NULL upon failure<br>
> */<br>
> -static libusb_device *<br>
> -spice_usb_device_manager_<wbr>device_to_libdev(<wbr>SpiceUsbDeviceManager *self,<br>
> +static SpiceUsbBackendDevice *<br>
> +spice_usb_device_manager_<wbr>device_to_bdev(<wbr>SpiceUsbDeviceManager *self,<br>
> SpiceUsbDevice *device)<br>
> {<br>
> #ifdef G_OS_WIN32<br>
> @@ -2054,7 +2049,7 @@ spice_usb_device_manager_<wbr>device_to_libdev(<wbr>SpiceUsbDeviceManager *self,<br>
> * driver swap we do under windows invalidates the cached libdev.<br>
> */<br>
> <br>
> - libusb_device *d, **devlist;<br>
> + SpiceUsbBackendDevice *d, **devlist;<br>
> int i;<br>
> <br>
> g_return_val_if_fail(SPICE_IS_<wbr>USB_DEVICE_MANAGER(self), NULL);<br>
> @@ -2062,18 +2057,18 @@ spice_usb_device_manager_<wbr>device_to_libdev(<wbr>SpiceUsbDeviceManager *self,<br>
> g_return_val_if_fail(self-><wbr>priv != NULL, NULL);<br>
> g_return_val_if_fail(self-><wbr>priv->context != NULL, NULL);<br>
> <br>
> - libusb_get_device_list(self-><wbr>priv->context, &devlist);<br>
> + devlist = spice_usb_backend_get_device_<wbr>list(self->priv->context);<br>
> if (!devlist)<br>
> return NULL;<br>
> <br>
> for (i = 0; (d = devlist[i]) != NULL; i++) {<br>
> - if (spice_usb_manager_device_<wbr>equal_libdev(self, device, d)) {<br>
> - libusb_ref_device(d);<br>
> + if (spice_usb_manager_device_<wbr>equal_bdev(self, device, d)) {<br>
> + spice_usb_backend_device_<wbr>acquire(d);<br>
> break;<br>
> }<br>
> }<br>
> <br>
> - libusb_free_device_list(<wbr>devlist, 1);<br>
> + spice_usb_backend_free_device_<wbr>list(devlist);<br>
> <br>
> return d;<br>
> <br>
> @@ -2081,7 +2076,135 @@ spice_usb_device_manager_<wbr>device_to_libdev(<wbr>SpiceUsbDeviceManager *self,<br>
> /* Simply return a ref to the cached libdev */<br>
> SpiceUsbDeviceInfo *info = (SpiceUsbDeviceInfo *)device;<br>
> <br>
> - return libusb_ref_device(info-><wbr>libdev);<br>
> + spice_usb_backend_device_<wbr>acquire(info->bdev);<br>
> + return info->bdev;<br>
> #endif<br>
> }<br>
> +<br>
> +static void on_device_change(void *user_data, SpiceUsbBackendDevice *bdev)<br>
> +{<br>
> + SpiceUsbDeviceManager *self = user_data;<br>
> + const UsbDeviceInformation *info = spice_usb_backend_device_get_<wbr>info(bdev);<br>
> + SpiceUsbDevice *device = spice_usb_device_manager_find_<wbr>device(self, info->bus, info->address);<br>
> + if (device) {<br>
> + SPICE_DEBUG("%s dev:%u:%u", __FUNCTION__, info->bus, info->address);<br>
> + g_signal_emit(self, signals[DEVICE_CHANGED], 0, device);<br>
> + } else {<br>
> + SPICE_DEBUG("%s %u:%u not found in usb device manager", __FUNCTION__, info->bus, info->address);<br>
> + }<br>
> +}<br>
> +<br>
> +guint spice_usb_device_manager_is_<wbr>device_cd(<wbr>SpiceUsbDeviceManager *self,<br>
> + SpiceUsbDevice *device)<br>
> +{<br>
> + guint val = 0;<br>
> + SpiceUsbBackendDevice *bdev = spice_usb_device_manager_<wbr>device_to_bdev(self, device);<br>
> + if (bdev) {<br>
> + const UsbDeviceInformation *info = spice_usb_backend_device_get_<wbr>info(bdev);<br>
> + val = info->max_luns;<br>
> + spice_usb_backend_device_<wbr>release(bdev);<br>
> + }<br>
> + return val;<br>
> +}<br>
> +<br>
> +gboolean spice_usb_device_manager_add_<wbr>cd_lun(SpiceUsbDeviceManager *self,<br>
> + SpiceUsbDeviceLunInfo *lun_info)<br>
> +{<br>
> + return spice_usb_backend_add_cd_lun(<wbr>self->priv->context, lun_info);<br>
> +}<br>
> +<br>
> +gboolean<br>
> +spice_usb_device_manager_<wbr>device_lun_remove(<wbr>SpiceUsbDeviceManager *self,<br>
> + SpiceUsbDevice *device,<br>
> + guint lun)<br>
> +{<br>
> + gboolean b = FALSE;<br>
> + SpiceUsbBackendDevice *bdev = spice_usb_device_manager_<wbr>device_to_bdev(self, device);<br>
> + if (bdev) {<br>
> + b = spice_usb_backend_remove_cd_<wbr>lun(self->priv->context, bdev, lun);<br>
> + spice_usb_backend_device_<wbr>release(bdev);<br>
> + }<br>
> + return b;<br>
> +}<br>
> +<br>
> +gboolean<br>
> +spice_usb_device_manager_<wbr>device_lun_get_info(<wbr>SpiceUsbDeviceManager *self,<br>
> + SpiceUsbDevice *device,<br>
> + guint lun,<br>
> + SpiceUsbDeviceLunInfo *lun_info)<br>
> +{<br>
> + gboolean b = FALSE;<br>
> + SpiceUsbBackendDevice *bdev = spice_usb_device_manager_<wbr>device_to_bdev(self, device);<br>
> + if (bdev) {<br>
> + b = spice_usb_backend_get_cd_lun_<wbr>info(bdev, lun, lun_info);<br>
> + spice_usb_backend_device_<wbr>release(bdev);<br>
> + }<br>
> + return b;<br>
> +}<br>
> +<br>
> +gboolean<br>
> +spice_usb_device_manager_<wbr>device_lun_load(<wbr>SpiceUsbDeviceManager *self,<br>
> + SpiceUsbDevice *device,<br>
> + guint lun,<br>
> + gboolean load)<br>
> +{<br>
> + gboolean b = FALSE;<br>
> + SpiceUsbBackendDevice *bdev = spice_usb_device_manager_<wbr>device_to_bdev(self, device);<br>
> + if (bdev) {<br>
> + b = spice_usb_backend_load_cd_lun(<wbr>self->priv->context, bdev, lun, load);<br>
> + spice_usb_backend_device_<wbr>release(bdev);<br>
> + }<br>
> + return b;<br>
> +}<br>
> +<br>
> +gboolean<br>
> +spice_usb_device_manager_<wbr>device_lun_change_media(<wbr>SpiceUsbDeviceManager *self,<br>
> + SpiceUsbDevice *device,<br>
> + guint lun,<br>
> + const SpiceUsbDeviceLunInfo *lun_info)<br>
> +{<br>
> + gboolean b = FALSE;<br>
> + SpiceUsbBackendDevice *bdev = spice_usb_device_manager_<wbr>device_to_bdev(self, device);<br>
> + if (bdev) {<br>
> + b = spice_usb_backend_change_cd_<wbr>lun(self->priv->context, bdev, lun, lun_info);<br>
> + spice_usb_backend_device_<wbr>release(bdev);<br>
> + }<br>
> + return b;<br>
> +}<br>
> +<br>
> +gboolean<br>
> +spice_usb_device_manager_<wbr>device_lun_lock(<wbr>SpiceUsbDeviceManager *self,<br>
> + SpiceUsbDevice *device,<br>
> + guint lun,<br>
> + gboolean lock)<br>
> +{<br>
> + gboolean b = FALSE;<br>
> + SpiceUsbBackendDevice *bdev = spice_usb_device_manager_<wbr>device_to_bdev(self, device);<br>
> + if (bdev) {<br>
> + b = spice_usb_backend_lock_cd_lun(<wbr>self->priv->context, bdev, lun, lock);<br>
> + spice_usb_backend_device_<wbr>release(bdev);<br>
> + }<br>
> + return b;<br>
> +}<br>
> +<br>
> +GArray *spice_usb_device_manager_get_<wbr>device_luns(<wbr>SpiceUsbDeviceManager *self,<br>
> + SpiceUsbDevice *device)<br>
> +{<br>
> + GArray *indices = g_array_new(FALSE, TRUE, sizeof(guint));<br>
> + SpiceUsbBackendDevice *bdev = spice_usb_device_manager_<wbr>device_to_bdev(self, device);<br>
> + if (bdev) {<br>
> + uint32_t value = spice_usb_backend_get_cd_luns_<wbr>bitmask(bdev);<br>
> + guint i = 0;<br>
> + while (value) {<br>
> + if (value & 1) {<br>
> + g_array_append_val(indices, i);<br>
> + }<br>
> + value >>= 1;<br>
> + i++;<br>
> + }<br>
> + spice_usb_backend_device_<wbr>release(bdev);<br>
> + }<br>
> + return indices;<br>
> +}<br>
> +<br>
> #endif /* USE_USBREDIR */<br>
> diff --git a/src/usb-device-manager.h b/src/usb-device-manager.h<br>
> index 773208f..a6f8064 100644<br>
> --- a/src/usb-device-manager.h<br>
> +++ b/src/usb-device-manager.h<br>
> @@ -71,6 +71,7 @@ struct _SpiceUsbDeviceManager<br>
> * @device_removed: Signal class handler for the #SpiceUsbDeviceManager::<wbr>device-removed signal.<br>
> * @auto_connect_failed: Signal class handler for the #SpiceUsbDeviceManager::auto-<wbr>connect-failed signal.<br>
> * @device_error: Signal class handler for the #SpiceUsbDeviceManager::<wbr>device_error signal.<br>
> + * @device_changed: Signal class handler for the #SpiceUsbDeviceManager::<wbr>device_changed signal.<br>
> *<br>
> * Class structure for #SpiceUsbDeviceManager.<br>
> */<br>
> @@ -87,18 +88,52 @@ struct _SpiceUsbDeviceManagerClass<br>
> SpiceUsbDevice *device, GError *error);<br>
> void (*device_error) (SpiceUsbDeviceManager *manager,<br>
> SpiceUsbDevice *device, GError *error);<br>
> + void (*device_changed) (SpiceUsbDeviceManager *manager,<br>
> + SpiceUsbDevice *device);<br>
> +<br>
> /*< private >*/<br>
> /*<br>
> * If adding fields to this struct, remove corresponding<br>
> * amount of padding to avoid changing overall struct size<br>
> */<br>
> - gchar _spice_reserved[SPICE_<wbr>RESERVED_PADDING];<br>
> + gchar _spice_reserved[SPICE_<wbr>RESERVED_PADDING - 1 * sizeof(void *)];<br>
> };<br>
> <br>
> GType spice_usb_device_get_type(<wbr>void);<br>
> GType spice_usb_device_manager_get_<wbr>type(void);<br>
> <br>
> gchar *spice_usb_device_get_<wbr>description(SpiceUsbDevice *device, const gchar *format);<br>
> +<br>
> +/**<br>
> +* SpiceUsbDeviceDescription:<br>
> +* @bus: USB bus number.<br>
> +* @address: address on the bus.<br>
> +* @vendor_id: vendor ID value from device descriptor.<br>
> +* @product_id: product ID value from device descriptor.<br>
> +* @vendor: vendor name (new string allocated on return)<br>
> +* @product: vendor name (new string allocated on return)<br>
> +*<br>
> +* Structure containing description of Spice USB device.<br>
> +*/<br>
> +typedef struct _SpiceUsbDeviceDescription<br>
> +{<br>
> + guint16 bus;<br>
> + guint16 address;<br>
> + guint16 vendor_id;<br>
> + guint16 product_id;<br>
> + // (OUT) allocated strings for vendor and product<br>
> + gchar *vendor;<br>
> + gchar *product;<br>
> +} SpiceUsbDeviceDescription;<br>
> +<br>
> +/*<br>
> +spice_usb_device_get_info is similar to spice_usb_device_get_<wbr>description,<br>
> +i.e. 'vendor' and 'product' strings are allocated by callee and shall be<br>
> +freed by caller<br>
> +*/<br>
> +void spice_usb_device_get_info(<wbr>SpiceUsbDeviceManager *manager,<br>
> + SpiceUsbDevice *device,<br>
> + SpiceUsbDeviceDescription *info);<br>
> gconstpointer spice_usb_device_get_libusb_<wbr>device(const SpiceUsbDevice *device);<br>
> <br>
> SpiceUsbDeviceManager *spice_usb_device_manager_get(<wbr>SpiceSession *session,<br>
> @@ -143,6 +178,76 @@ spice_usb_device_manager_can_<wbr>redirect_device(<wbr>SpiceUsbDeviceManager *self,<br>
> <br>
> gboolean spice_usb_device_manager_is_<wbr>redirecting(<wbr>SpiceUsbDeviceManager *self);<br>
> <br>
> +guint spice_usb_device_manager_is_<wbr>device_cd(<wbr>SpiceUsbDeviceManager *self,<br>
> + SpiceUsbDevice *device);<br>
> +<br>
> +/* returns new array of guint LUN indices */<br>
> +GArray *spice_usb_device_manager_get_<wbr>device_luns(<wbr>SpiceUsbDeviceManager *self,<br>
> + SpiceUsbDevice *device);<br>
> +<br>
> +/**<br>
> +* SpiceUsbDeviceLunInfo:<br>
> +* @file_path: Path to ISO file or local CD device that the unit represents.<br>
> +* @vendor: string containing vendor name according to SCSI standard (first 8 characters used)<br>
> +* @product: string containing product name according to SCSI standard (first 16 characters used)<br>
> +* @revision: string containing the revision according to SCSI standard (first 4 characters used)<br>
> +* @loaded: %TRUE if the media is currently loaded<br>
> +* @locked: %TRUE if the media is currently locked from ejection<br>
> +* @started: %TRUE if the device is started by guest OS<br>
> +*<br>
> +* Structure containing information about CD logical unit of Spice USB device.<br>
> +*/<br>
> +typedef struct _SpiceUsbDeviceLunInfo<br>
> +{<br>
> + const gchar *file_path;<br>
> + const gchar *vendor;<br>
> + const gchar *product;<br>
> + const gchar *revision;<br>
> + gboolean loaded;<br>
> + gboolean locked;<br>
> + gboolean started;<br>
> +} SpiceUsbDeviceLunInfo;<br>
> +<br>
> +/* CD LUN will be attached to a (possibly new) USB device automatically */<br>
> +gboolean spice_usb_device_manager_add_<wbr>cd_lun(SpiceUsbDeviceManager *self,<br>
> + SpiceUsbDeviceLunInfo *lun_info);<br>
> +<br>
> +/* Get CD LUN info, intended primarily for enumerating LUNs.<br>
> + The caller shall not free the strings returned in lun_info structure<br>
> + on successfull call. It can use them only in context of current call or<br>
> + duplicate for further usage.<br>
> +*/<br>
> +gboolean<br>
> +spice_usb_device_manager_<wbr>device_lun_get_info(<wbr>SpiceUsbDeviceManager *self,<br>
> + SpiceUsbDevice *device,<br>
> + guint lun,<br>
> + SpiceUsbDeviceLunInfo *lun_info);<br>
> +/* lock or unlock device */<br>
> +gboolean<br>
> +spice_usb_device_manager_<wbr>device_lun_lock(<wbr>SpiceUsbDeviceManager *self,<br>
> + SpiceUsbDevice *device,<br>
> + guint lun,<br>
> + gboolean lock);<br>
> +<br>
> +/* load or eject device */<br>
> +gboolean<br>
> +spice_usb_device_manager_<wbr>device_lun_load(<wbr>SpiceUsbDeviceManager *self,<br>
> + SpiceUsbDevice *device,<br>
> + guint lun,<br>
> + gboolean load);<br>
> +<br>
> +/* change the media - device must be not currently loaded */<br>
> +gboolean<br>
> +spice_usb_device_manager_<wbr>device_lun_change_media(<wbr>SpiceUsbDeviceManager *self,<br>
> + SpiceUsbDevice *device,<br>
> + guint lun,<br>
> + const SpiceUsbDeviceLunInfo *lun_info);<br>
> +/* remove lun from the usb device */<br>
> +gboolean<br>
> +spice_usb_device_manager_<wbr>device_lun_remove(<wbr>SpiceUsbDeviceManager *self,<br>
> + SpiceUsbDevice *device,<br>
> + guint lun);<br>
> +<br>
> G_END_DECLS<br>
> <br>
> #endif /* __SPICE_USB_DEVICE_MANAGER_H__ */<br>
> diff --git a/src/usb-device-widget.c b/src/usb-device-widget.c<br>
> index d6e440c..f68a1a9 100644<br>
> --- a/src/usb-device-widget.c<br>
> +++ b/src/usb-device-widget.c<br>
> @@ -25,6 +25,8 @@<br>
> #include "spice-marshal.h"<br>
> #include "usb-device-widget.h"<br>
> <br>
> +#ifndef USE_NEW_USB_WIDGET<br>
> +<br>
> /**<br>
> * SECTION:usb-device-widget<br>
> * @short_description: USB device selection widget<br>
> @@ -62,6 +64,7 @@ static void device_removed_cb(<wbr>SpiceUsbDeviceManager *manager,<br>
> SpiceUsbDevice *device, gpointer user_data);<br>
> static void device_error_cb(<wbr>SpiceUsbDeviceManager *manager,<br>
> SpiceUsbDevice *device, GError *err, gpointer user_data);<br>
> +<br>
> static gboolean spice_usb_device_widget_<wbr>update_status(gpointer user_data);<br>
> <br>
> enum {<br>
> @@ -591,3 +594,5 @@ static void device_error_cb(<wbr>SpiceUsbDeviceManager *manager,<br>
> <br>
> spice_usb_device_widget_<wbr>update_status(self);<br>
> }<br>
> +<br>
> +#endif // !USE_NEW_USB_WIDGET<br>
> diff --git a/src/usbutil.c b/src/usbutil.c<br>
> index e96ab11..025f610 100644<br>
> --- a/src/usbutil.c<br>
> +++ b/src/usbutil.c<br>
> @@ -58,42 +58,6 @@ static GMutex usbids_load_mutex;<br>
> static int usbids_vendor_count = 0; /* < 0: failed, 0: empty, > 0: loaded */<br>
> static usb_vendor_info *usbids_vendor_info = NULL;<br>
> <br>
> -G_GNUC_INTERNAL<br>
> -const char *spice_usbutil_libusb_<wbr>strerror(enum libusb_error error_code)<br>
> -{<br>
> - switch (error_code) {<br>
> - case LIBUSB_SUCCESS:<br>
> - return "Success";<br>
> - case LIBUSB_ERROR_IO:<br>
> - return "Input/output error";<br>
> - case LIBUSB_ERROR_INVALID_PARAM:<br>
> - return "Invalid parameter";<br>
> - case LIBUSB_ERROR_ACCESS:<br>
> - return "Access denied (insufficient permissions)";<br>
> - case LIBUSB_ERROR_NO_DEVICE:<br>
> - return "No such device (it may have been disconnected)";<br>
> - case LIBUSB_ERROR_NOT_FOUND:<br>
> - return "Entity not found";<br>
> - case LIBUSB_ERROR_BUSY:<br>
> - return "Resource busy";<br>
> - case LIBUSB_ERROR_TIMEOUT:<br>
> - return "Operation timed out";<br>
> - case LIBUSB_ERROR_OVERFLOW:<br>
> - return "Overflow";<br>
> - case LIBUSB_ERROR_PIPE:<br>
> - return "Pipe error";<br>
> - case LIBUSB_ERROR_INTERRUPTED:<br>
> - return "System call interrupted (perhaps due to signal)";<br>
> - case LIBUSB_ERROR_NO_MEM:<br>
> - return "Insufficient memory";<br>
> - case LIBUSB_ERROR_NOT_SUPPORTED:<br>
> - return "Operation not supported or unimplemented on this platform";<br>
> - case LIBUSB_ERROR_OTHER:<br>
> - return "Other error";<br>
> - }<br>
> - return "Unknown error";<br>
> -}<br>
> -<br>
> #ifdef __linux__<br>
> /* <Sigh> libusb does not allow getting the manufacturer and product strings<br>
> without opening the device, so grab them directly from sysfs */<br>
> @@ -252,7 +216,8 @@ leave:<br>
> G_GNUC_INTERNAL<br>
> void spice_usb_util_get_device_<wbr>strings(int bus, int address,<br>
> int vendor_id, int product_id,<br>
> - gchar **manufacturer, gchar **product)<br>
> + gchar **manufacturer, gchar **product,<br>
> + gboolean fill_always)<br>
> {<br>
> usb_product_info *product_info;<br>
> int i, j;<br>
> @@ -292,17 +257,20 @@ void spice_usb_util_get_device_<wbr>strings(int bus, int address,<br>
> }<br>
> }<br>
> <br>
> - if (!*manufacturer)<br>
> + if (!*manufacturer && fill_always)<br>
> *manufacturer = g_strdup(_("USB"));<br>
> - if (!*product)<br>
> + if (!*product && fill_always)<br>
> *product = g_strdup(_("Device"));<br>
> <br>
> /* Some devices have unwanted whitespace in their strings */<br>
> - g_strstrip(*manufacturer);<br>
> - g_strstrip(*product);<br>
> + if (*manufacturer)<br>
> + g_strstrip(*manufacturer);<br>
> + if (*product)<br>
> + g_strstrip(*product);<br>
> <br>
> /* Some devices repeat the manufacturer at the beginning of product */<br>
> - if (g_str_has_prefix(*product, *manufacturer) &&<br>
> + if (*manufacturer && *product &&<br>
> + g_str_has_prefix(*product, *manufacturer) &&<br>
> strlen(*product) > strlen(*manufacturer)) {<br>
> gchar *tmp = g_strdup(*product + strlen(*manufacturer));<br>
> g_free(*product);<br>
> diff --git a/src/usbutil.h b/src/usbutil.h<br>
> index de5e92a..c2ff7be 100644<br>
> --- a/src/usbutil.h<br>
> +++ b/src/usbutil.h<br>
> @@ -24,14 +24,13 @@<br>
> #include <glib.h><br>
> <br>
> #ifdef USE_USBREDIR<br>
> -#include <libusb.h><br>
> <br>
> G_BEGIN_DECLS<br>
> <br>
> -const char *spice_usbutil_libusb_<wbr>strerror(enum libusb_error error_code);<br>
> void spice_usb_util_get_device_<wbr>strings(int bus, int address,<br>
> int vendor_id, int product_id,<br>
> - gchar **manufacturer, gchar **product);<br>
> + gchar **manufacturer, gchar **product,<br>
> + gboolean fill_always);<br>
> <br>
> G_END_DECLS<br>
> <br>
> diff --git a/src/win-usb-dev.c b/src/win-usb-dev.c<br>
> index 9a130a3..d5dd2d2 100644<br>
> --- a/src/win-usb-dev.c<br>
> +++ b/src/win-usb-dev.c<br>
> @@ -23,11 +23,13 @@<br>
> #include "config.h"<br>
> <br>
> #include <windows.h><br>
> -#include <libusb.h><br>
> #include "win-usb-dev.h"<br>
> #include "spice-marshal.h"<br>
> #include "spice-util.h"<br>
> #include "usbutil.h"<br>
> +#include "usb-backend.h"<br>
> +<br>
> +#define USB_CLASS_HUB 9<br>
> <br>
> enum {<br>
> PROP_0,<br>
> @@ -35,7 +37,7 @@ enum {<br>
> };<br>
> <br>
> struct _GUdevClientPrivate {<br>
> - libusb_context *ctx;<br>
> + SpiceUsbBackend *ctx;<br>
> GList *udev_list;<br>
> HWND hwnd;<br>
> gboolean redirecting;<br>
> @@ -85,7 +87,7 @@ static GUdevClient *singleton = NULL;<br>
> <br>
> static GUdevDevice *g_udev_device_new(<wbr>GUdevDeviceInfo *udevinfo);<br>
> static LRESULT CALLBACK wnd_proc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam);<br>
> -static gboolean get_usb_dev_info(libusb_device *dev, GUdevDeviceInfo *udevinfo);<br>
> +static gboolean get_usb_dev_info(<wbr>SpiceUsbBackendDevice *dev, GUdevDeviceInfo *udevinfo);<br>
> <br>
> //uncomment to debug gudev device lists.<br>
> //#define DEBUG_GUDEV_DEVICE_LISTS<br>
> @@ -122,8 +124,7 @@ static ssize_t<br>
> g_udev_client_list_devices(<wbr>GUdevClient *self, GList **devs,<br>
> GError **err, const gchar *name)<br>
> {<br>
> - gssize rc;<br>
> - libusb_device **lusb_list, **dev;<br>
> + SpiceUsbBackendDevice **lusb_list, **dev;<br>
> GUdevClientPrivate *priv;<br>
> GUdevDeviceInfo *udevinfo;<br>
> GUdevDevice *udevice;<br>
> @@ -136,13 +137,8 @@ g_udev_client_list_devices(<wbr>GUdevClient *self, GList **devs,<br>
> <br>
> g_return_val_if_fail(self-><wbr>priv->ctx != NULL, -3);<br>
> <br>
> - rc = libusb_get_device_list(priv-><wbr>ctx, &lusb_list);<br>
> - if (rc < 0) {<br>
> - const char *errstr = spice_usbutil_libusb_strerror(<wbr>rc);<br>
> - g_warning("%s: libusb_get_device_list failed - %s", name, errstr);<br>
> - g_set_error(err, G_UDEV_CLIENT_ERROR, G_UDEV_CLIENT_LIBUSB_FAILED,<br>
> - "%s: Error getting device list from libusb: %s [%"G_GSSIZE_FORMAT"]",<br>
> - name, errstr, rc);<br>
> + lusb_list = spice_usb_backend_get_device_<wbr>list(priv->ctx);<br>
> + if (!lusb_list) {<br>
> return -4;<br>
> }<br>
> <br>
> @@ -158,7 +154,7 @@ g_udev_client_list_devices(<wbr>GUdevClient *self, GList **devs,<br>
> *devs = g_list_prepend(*devs, udevice);<br>
> n++;<br>
> }<br>
> - libusb_free_device_list(lusb_<wbr>list, 1);<br>
> + spice_usb_backend_free_device_<wbr>list(lusb_list);<br>
> <br>
> return n;<br>
> }<br>
> @@ -180,7 +176,6 @@ g_udev_client_initable_init(<wbr>GInitable *initable, GCancellable *cancellable,<br>
> GUdevClient *self;<br>
> GUdevClientPrivate *priv;<br>
> WNDCLASS wcls;<br>
> - int rc;<br>
> <br>
> g_return_val_if_fail(G_UDEV_<wbr>IS_CLIENT(initable), FALSE);<br>
> g_return_val_if_fail(<wbr>cancellable == NULL, FALSE);<br>
> @@ -188,19 +183,10 @@ g_udev_client_initable_init(<wbr>GInitable *initable, GCancellable *cancellable,<br>
> self = G_UDEV_CLIENT(initable);<br>
> priv = self->priv;<br>
> <br>
> - rc = libusb_init(&priv->ctx);<br>
> - if (rc < 0) {<br>
> - const char *errstr = spice_usbutil_libusb_strerror(<wbr>rc);<br>
> - g_warning("Error initializing USB support: %s [%i]", errstr, rc);<br>
> - g_set_error(err, G_UDEV_CLIENT_ERROR, G_UDEV_CLIENT_LIBUSB_FAILED,<br>
> - "Error initializing USB support: %s [%i]", errstr, rc);<br>
> + priv->ctx = spice_usb_backend_initialize()<wbr>;<br>
> + if (!priv->ctx) {<br>
> return FALSE;<br>
> }<br>
> -#ifdef G_OS_WIN32<br>
> -#if LIBUSB_API_VERSION >= 0x01000106<br>
> - libusb_set_option(priv->ctx, LIBUSB_OPTION_USE_USBDK);<br>
> -#endif<br>
> -#endif<br>
> <br>
> /* get initial device list */<br>
> if (g_udev_client_list_devices(<wbr>self, &priv->udev_list, err, __FUNCTION__) < 0) {<br>
> @@ -267,7 +253,7 @@ static void g_udev_client_finalize(GObject *gobject)<br>
> <br>
> /* free libusb context initializing by libusb_init() */<br>
> g_warn_if_fail(priv->ctx != NULL);<br>
> - libusb_exit(priv->ctx);<br>
> + spice_usb_backend_finalize(<wbr>priv->ctx);<br>
> <br>
> /* Chain up to the parent class */<br>
> if (G_OBJECT_CLASS(g_udev_client_<wbr>parent_class)->finalize)<br>
> @@ -356,23 +342,18 @@ static void g_udev_client_class_init(<wbr>GUdevClientClass *klass)<br>
> g_object_class_install_<wbr>property(gobject_class, PROP_REDIRECTING, pspec);<br>
> }<br>
> <br>
> -static gboolean get_usb_dev_info(libusb_device *dev, GUdevDeviceInfo *udevinfo)<br>
> +static gboolean get_usb_dev_info(<wbr>SpiceUsbBackendDevice *dev, GUdevDeviceInfo *udevinfo)<br>
> {<br>
> - struct libusb_device_descriptor desc;<br>
> + const UsbDeviceInformation* info = spice_usb_backend_device_get_<wbr>info(dev);<br>
> <br>
> g_return_val_if_fail(dev, FALSE);<br>
> g_return_val_if_fail(udevinfo, FALSE);<br>
> <br>
> - if (libusb_get_device_descriptor(<wbr>dev, &desc) < 0) {<br>
> - g_warning("cannot get device descriptor %p", dev);<br>
> - return FALSE;<br>
> - }<br>
> -<br>
> - udevinfo->bus = libusb_get_bus_number(dev);<br>
> - udevinfo->addr = libusb_get_device_address(dev)<wbr>;<br>
> - udevinfo->class = desc.bDeviceClass;<br>
> - udevinfo->vid = desc.idVendor;<br>
> - udevinfo->pid = desc.idProduct;<br>
> + udevinfo->bus = info->bus;<br>
> + udevinfo->addr = info->address;<br>
> + udevinfo->class = info->class;<br>
> + udevinfo->vid = info->vid;<br>
> + udevinfo->pid = info->pid;<br>
> snprintf(udevinfo->sclass, sizeof(udevinfo->sclass), "%d", udevinfo->class);<br>
> snprintf(udevinfo->sbus, sizeof(udevinfo->sbus), "%d", udevinfo->bus);<br>
> snprintf(udevinfo->saddr, sizeof(udevinfo->saddr), "%d", udevinfo->addr);<br>
> @@ -573,7 +554,14 @@ static gboolean g_udev_skip_search(GUdevDevice *udev)<br>
> #if defined(LIBUSBX_API_VERSION) && (LIBUSBX_API_VERSION >= 0x010000FF)<br>
> (udevinfo->addr == 1) || /* root hub addr for libusbx >= 1.0.13 */<br>
> #endif<br>
> - (udevinfo->class == LIBUSB_CLASS_HUB) || /* hub*/<br>
> + (udevinfo->class == USB_CLASS_HUB) || /* hub*/<br>
> (udevinfo->addr == 0)); /* bad address */<br>
> return skip;<br>
> }<br>
> +<br>
> +void spice_usb_backend_indicate_<wbr>dev_change(void)<br>
> +{<br>
> + GUdevClient *client = g_udev_client_new(NULL);<br>
> + GUdevClientPrivate *priv = client->priv;<br>
> + PostMessage(priv->hwnd, WM_DEVICECHANGE, 0, 0);<br>
> +}<br>
> diff --git a/src/win-usb-dev.h b/src/win-usb-dev.h<br>
> index b5c4fce..ff12aac 100644<br>
> --- a/src/win-usb-dev.h<br>
> +++ b/src/win-usb-dev.h<br>
> @@ -104,6 +104,8 @@ typedef enum<br>
> G_UDEV_CLIENT_WINAPI_FAILED<br>
> } GUdevClientError;<br>
> <br>
> +/* callback to indicate creation of new simulated device */<br>
> +void spice_usb_backend_indicate_<wbr>dev_change(void);<br>
> <br>
> G_END_DECLS<br>
> <br>
> -- <br>
> 2.9.4<br>
> <br>
</div></div>> ______________________________<wbr>_________________<br>
> Spice-devel mailing list<br>
> <a href="mailto:Spice-devel@lists.freedesktop.org">Spice-devel@lists.freedesktop.<wbr>org</a><br>
> <a href="https://lists.freedesktop.org/mailman/listinfo/spice-devel" rel="noreferrer" target="_blank">https://lists.freedesktop.org/<wbr>mailman/listinfo/spice-devel</a><br>
</blockquote></div><br></div>