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

Marc-André Lureau marcandre.lureau at gmail.com
Thu Sep 13 12:32:48 UTC 2018


Hi

On Thu, Sep 13, 2018 at 2:08 PM Yuri Benditovich
<yuri.benditovich at daynix.com> wrote:
>
> 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.

Users will need to adopt the "new" widget to have CD sharing if I
understand correctly.

Please describe why you had to write a new widget (instead of
modifying/extending the existing one), and let's just have one widget
provided by spice-gtk.

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

I can see a lot of code that can be easily split. Nobody can review a
+8000 loc patch in one go.

By not doing it, you put the burden on the reviewers and maintainers,
this isn't how we and open-source in general work. You need to help
the others to understand and accept your code. Please do some effort.

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

We can give some general review already, but you'll need to make the
feature easier to digest.

Thanks

>
> 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 _______________________________________________
>> Spice-devel mailing list
>> Spice-devel at lists.freedesktop.org
>> https://lists.freedesktop.org/mailman/listinfo/spice-devel



-- 
Marc-André Lureau


More information about the Spice-devel mailing list