<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>