[Spice-devel] [spice-gtk v2 2/2] cd-sharing: Implementation of CD sharing feature

Yuri Benditovich yuri.benditovich at daynix.com
Thu Sep 13 10:08:15 UTC 2018


Regarding the new widget:
New widget introduced in order to keep the backward compatibility.
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).
I do not take the responsibility to decide that the old widget is not
needed anymore.
When such decision is taken, it is possible to remove the old file in
simple future commit.

Regarding additional split of the 2/2 to 'backend' and 'cd-sharing'
I've checked this option before preparing the combined commit and found
that the content
of 'cd-sharing' changes in 2/2 is not more than 10% and splitting will add
significant time
to test the intermediate patch (as everything shall work without regression
after each patch)
which still stays intermediate as the main motivation of 'backend' part is
the 'cd-sharing' feature.

If there are no very strong reasons for spending time for preparing/testing
such intermediate patch
I'd suggest to continue reviewing the combined patch.

Thanks,
Yuri Benditovich


On Thu, Sep 13, 2018 at 10:43 AM, Christophe Fergeau <cfergeau at redhat.com>
wrote:

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


More information about the Spice-devel mailing list