[Spice-commits] 24 commits - src/channel-usbredir.c src/channel-usbredir-priv.h src/Makefile.am src/map-file src/usb-device-manager.c src/usb-device-manager.h src/usb-device-widget.c src/usbdk_api.c src/usbdk_api.h src/win-usb-dev.c

Jonathon Jongsma jjongsma at kemper.freedesktop.org
Tue Mar 29 15:24:13 UTC 2016


 src/Makefile.am             |    2 
 src/channel-usbredir-priv.h |   13 +
 src/channel-usbredir.c      |  149 ++++++++++--
 src/map-file                |    3 
 src/usb-device-manager.c    |  538 ++++++++++++++++++++++++++++++++++----------
 src/usb-device-manager.h    |   16 +
 src/usb-device-widget.c     |   91 +++++--
 src/usbdk_api.c             |  187 +++++++++++++++
 src/usbdk_api.h             |   34 ++
 src/win-usb-dev.c           |  167 ++++++++-----
 10 files changed, 975 insertions(+), 225 deletions(-)

New commits:
commit 7229b0d35f9607259dae4c90d630b792eb5d88eb
Author: Jonathon Jongsma <jjongsma at redhat.com>
Date:   Thu Mar 24 15:02:32 2016 -0500

    usb device widget: don't try to disconnect on failed connect
    
    When you try to redirect a usb device to the guest and it fails, we
    uncheck the checkbox for this device. This causes the 'clicked' signal
    to be emitted, which causes us to try to disconnect the device (which is
    not currently connected, since the connect operation failed).
    
    When we try to disconnect an unconnected device, the device manager
    leaks memory and gets left in an inconsistent state because we allocate
    the task data, call _set_redirecting(self, TRUE) and then return early
    with the following warning printed to the terminal:
    
    (lt-spicy:4638): GSpice-CRITICAL **: spice_usbredir_channel_disconnect_device_async: assertion 'channel != NULL' failed
    
    To avoid this, disable signal handlers for the checkbox 'clicked'
    signal while we're changing the checkbox state in response to a
    connection error. In addition, add an additional check to
    spice_usb_device_manager_disconnect_device_async() to ensure that the
    passed device is actually connected.

diff --git a/src/usb-device-manager.c b/src/usb-device-manager.c
index cc95c68..9487f38 100644
--- a/src/usb-device-manager.c
+++ b/src/usb-device-manager.c
@@ -1827,6 +1827,7 @@ void spice_usb_device_manager_disconnect_device_async(SpiceUsbDeviceManager *sel
     g_return_if_fail(SPICE_IS_USB_DEVICE_MANAGER(self));
 
     g_return_if_fail(device != NULL);
+    g_return_if_fail(spice_usb_device_manager_is_device_connected(self, device);
 
     SPICE_DEBUG("disconnecting device %p", device);
 
diff --git a/src/usb-device-widget.c b/src/usb-device-widget.c
index 085f640..d3ea79a 100644
--- a/src/usb-device-widget.c
+++ b/src/usb-device-widget.c
@@ -478,6 +478,7 @@ static void _disconnect_cb(GObject *gobject, GAsyncResult *res, gpointer user_da
     connect_cb_data_free(data);
 }
 
+static void checkbox_clicked_cb(GtkWidget *check, gpointer user_data);
 static void connect_cb(GObject *gobject, GAsyncResult *res, gpointer user_data)
 {
     SpiceUsbDeviceManager *manager = SPICE_USB_DEVICE_MANAGER(gobject);
@@ -500,7 +501,12 @@ static void connect_cb(GObject *gobject, GAsyncResult *res, gpointer user_data)
         g_signal_emit(self, signals[CONNECT_FAILED], 0, device, err);
         g_error_free(err);
 
+        /* don't trigger a disconnect if connect failed */
+        g_signal_handlers_block_by_func(GTK_TOGGLE_BUTTON(data->check),
+                                        checkbox_clicked_cb, self);
         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->check), FALSE);
+        g_signal_handlers_unblock_by_func(GTK_TOGGLE_BUTTON(data->check),
+                                        checkbox_clicked_cb, self);
     }
 
     connect_cb_data_free(data);
commit ff1f5651723a92c5d42206f0c3aa0210b14946a9
Author: Jonathon Jongsma <jjongsma at redhat.com>
Date:   Thu Mar 24 12:27:20 2016 -0500

    SpiceUsbDeviceManager: propagate errors from sub-tasks
    
    _connect_device_async_cb() just turned TRUE unconditionally even if the
    sub-task had failed. Instead, introduce a
    _spice_usb_device_manager_connect_device_finish() function which passes
    up the result of the subtask.

diff --git a/src/usb-device-manager.c b/src/usb-device-manager.c
index 1bbf6b3..cc95c68 100644
--- a/src/usb-device-manager.c
+++ b/src/usb-device-manager.c
@@ -1563,6 +1563,18 @@ _spice_usb_device_manager_uninstall_driver_async(SpiceUsbDeviceManager *self,
 
 #ifdef USE_USBREDIR
 
+static gboolean
+_spice_usb_device_manager_connect_device_finish(SpiceUsbDeviceManager *self,
+                                                GAsyncResult *res,
+                                                GError **error)
+{
+    GTask *task = G_TASK(res);
+
+    g_return_val_if_fail(g_task_is_valid(task, G_OBJECT(self)), FALSE);
+
+    return g_task_propagate_boolean(task, error);
+}
+
 static void
 _spice_usb_device_manager_connect_device_async(SpiceUsbDeviceManager *self,
                                                SpiceUsbDevice *device,
@@ -1727,10 +1739,13 @@ void _connect_device_async_cb(GObject *gobject,
 {
     SpiceUsbDeviceManager *self = SPICE_USB_DEVICE_MANAGER(gobject);
     GTask *task = user_data;
+    GError *error = NULL;
 
     _set_redirecting(self, FALSE);
-
-    g_task_return_boolean(task, TRUE);
+    if (_spice_usb_device_manager_connect_device_finish(self, channel_res, &error))
+        g_task_return_boolean(task, TRUE);
+    else
+        g_task_return_error(task, error);
     g_object_unref(task);
 }
 #endif
commit d9dad6dacda9ba070b086afbc9cd0f08af2f114e
Author: Dmitry Fleytman <dfleytma at redhat.com>
Date:   Tue Mar 8 16:06:01 2016 +0200

    UsbDeviceManager: Deprecate synchronous disconnection method
    
    Asynchronous disconnection should be used instead.
    
    Signed-off-by: Dmitry Fleytman <dfleytma at redhat.com>
    Acked-by: Jonathon Jongsma <jjongsma at redhat.com>

diff --git a/src/usb-device-manager.h b/src/usb-device-manager.h
index a9a4f7a..6a8d0ac 100644
--- a/src/usb-device-manager.h
+++ b/src/usb-device-manager.h
@@ -130,8 +130,11 @@ gboolean spice_usb_device_manager_connect_device_finish(
 gboolean spice_usb_device_manager_disconnect_device_finish(
     SpiceUsbDeviceManager *self, GAsyncResult *res, GError **err);
 
+#ifndef SPICE_DISABLE_DEPRECATED
+SPICE_DEPRECATED
 void spice_usb_device_manager_disconnect_device(SpiceUsbDeviceManager *manager,
                                                 SpiceUsbDevice *device);
+#endif
 
 gboolean
 spice_usb_device_manager_can_redirect_device(SpiceUsbDeviceManager  *self,
commit 9c253ee761d00c5cf01c93cf5e79b67811100bf6
Author: Kirill Moizik <kmoizik at redhat.com>
Date:   Tue Mar 8 16:06:00 2016 +0200

    UsbDeviceWidget: Use asynchronous disconnect API
    
    Signed-off-by: Kirill Moizik <kmoizik at redhat.com>
    Signed-off-by: Dmitry Fleytman <dfleytma at redhat.com>
    Acked-by: Jonathon Jongsma <jjongsma at redhat.com>

diff --git a/src/usb-device-widget.c b/src/usb-device-widget.c
index ebb2d7d..085f640 100644
--- a/src/usb-device-widget.c
+++ b/src/usb-device-widget.c
@@ -455,6 +455,29 @@ typedef struct _connect_cb_data {
     SpiceUsbDeviceWidget *self;
 } connect_cb_data;
 
+static void connect_cb_data_free(connect_cb_data *data)
+{
+    spice_usb_device_widget_update_status(data->self);
+    g_object_unref(data->check);
+    g_object_unref(data->self);
+    g_free(data);
+}
+
+static void _disconnect_cb(GObject *gobject, GAsyncResult *res, gpointer user_data)
+{
+    SpiceUsbDeviceManager *manager = SPICE_USB_DEVICE_MANAGER(gobject);
+    connect_cb_data *data = user_data;
+    GError *err = NULL;
+
+    spice_usb_device_manager_disconnect_device_finish(manager, res, &err);
+    if (err) {
+        SPICE_DEBUG("Device disconnection failed");
+        g_error_free(err);
+    }
+
+    connect_cb_data_free(data);
+}
+
 static void connect_cb(GObject *gobject, GAsyncResult *res, gpointer user_data)
 {
     SpiceUsbDeviceManager *manager = SPICE_USB_DEVICE_MANAGER(gobject);
@@ -480,10 +503,7 @@ static void connect_cb(GObject *gobject, GAsyncResult *res, gpointer user_data)
         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->check), FALSE);
     }
 
-    spice_usb_device_widget_update_status(self);
-    g_object_unref(data->check);
-    g_object_unref(data->self);
-    g_free(data);
+    connect_cb_data_free(data);
 }
 
 static void checkbox_clicked_cb(GtkWidget *check, gpointer user_data)
@@ -493,19 +513,23 @@ static void checkbox_clicked_cb(GtkWidget *check, gpointer user_data)
     SpiceUsbDevice *device;
 
     device = g_object_get_data(G_OBJECT(check), "usb-device");
+    connect_cb_data *data = g_new(connect_cb_data, 1);
+    data->check = g_object_ref(check);
+    data->self  = g_object_ref(self);
 
     if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check))) {
-        connect_cb_data *data = g_new(connect_cb_data, 1);
-        data->check = g_object_ref(check);
-        data->self  = g_object_ref(self);
         spice_usb_device_manager_connect_device_async(priv->manager,
                                                       device,
                                                       NULL,
                                                       connect_cb,
                                                       data);
     } else {
-        spice_usb_device_manager_disconnect_device(priv->manager,
-                                                   device);
+        spice_usb_device_manager_disconnect_device_async(priv->manager,
+                                                         device,
+                                                         NULL,
+                                                         _disconnect_cb,
+                                                         data);
+
     }
     spice_usb_device_widget_update_status(self);
 }
commit ce5b84238e9355caa188e83cada387ed5ec26018
Author: Kirill Moizik <kmoizik at redhat.com>
Date:   Tue Mar 8 16:05:59 2016 +0200

    UsbDeviceWidget: Consider asynchronous redirection flows
    
    Gray out redirection controls while there are
    asynchronous redirection flows in progress.
    
    Signed-off-by: Kirill Moizik <kmoizik at redhat.com>
    Signed-off-by: Dmitry Fleytman <dfleytma at redhat.com>
    Acked-by: Jonathon Jongsma <jjongsma at redhat.com>

diff --git a/src/usb-device-widget.c b/src/usb-device-widget.c
index 285bfd5..ebb2d7d 100644
--- a/src/usb-device-widget.c
+++ b/src/usb-device-widget.c
@@ -380,27 +380,29 @@ static void check_can_redirect(GtkWidget *widget, gpointer user_data)
         return; /* Non device widget, ie the info_bar */
 
     priv->device_count++;
-    can_redirect = spice_usb_device_manager_can_redirect_device(priv->manager,
-                                                                device, &err);
-    gtk_widget_set_sensitive(widget, can_redirect);
-
-    /* If we cannot redirect this device, append the error message to
-       err_msg, but only if it is *not* already there! */
-    if (!can_redirect) {
-        if (priv->err_msg) {
-            if (!strstr(priv->err_msg, err->message)) {
-                gchar *old_err_msg = priv->err_msg;
 
-                priv->err_msg = g_strdup_printf("%s\n%s", priv->err_msg,
-                                                err->message);
-                g_free(old_err_msg);
+    if (spice_usb_device_manager_is_redirecting(priv->manager)) {
+        can_redirect = FALSE;
+    } else {
+        can_redirect = spice_usb_device_manager_can_redirect_device(priv->manager,
+                                                                    device, &err);
+        /* If we cannot redirect this device, append the error message to
+           err_msg, but only if it is *not* already there! */
+        if (!can_redirect) {
+            if (priv->err_msg) {
+                if (!strstr(priv->err_msg, err->message)) {
+                    gchar *old_err_msg = priv->err_msg;
+                    priv->err_msg = g_strdup_printf("%s\n%s", priv->err_msg,
+                                                    err->message);
+                    g_free(old_err_msg);
+                }
+            } else {
+                priv->err_msg = g_strdup(err->message);
             }
-        } else {
-            priv->err_msg = g_strdup(err->message);
         }
+        g_clear_error(&err);
     }
-
-    g_clear_error(&err);
+    gtk_widget_set_sensitive(widget, can_redirect);
 }
 
 static gboolean spice_usb_device_widget_update_status(gpointer user_data)
@@ -476,9 +478,9 @@ static void connect_cb(GObject *gobject, GAsyncResult *res, gpointer user_data)
         g_error_free(err);
 
         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data->check), FALSE);
-        spice_usb_device_widget_update_status(self);
     }
 
+    spice_usb_device_widget_update_status(self);
     g_object_unref(data->check);
     g_object_unref(data->self);
     g_free(data);
commit 03e541add456485a6cc890064c98bc259f78ade4
Author: Kirill Moizik <kmoizik at redhat.com>
Date:   Tue Mar 8 16:05:58 2016 +0200

    UsbDeviceWidget: Show info bar during redirection flows
    
    Signed-off-by: Kirill Moizik <kmoizik at redhat.com>
    Signed-off-by: Dmitry Fleytman <dfleytma at redhat.com>
    Acked-by: Jonathon Jongsma <jjongsma at redhat.com>

diff --git a/src/map-file b/src/map-file
index 112b5ea..9df9017 100644
--- a/src/map-file
+++ b/src/map-file
@@ -133,6 +133,7 @@ spice_usb_device_manager_connect_device_finish;
 spice_usb_device_manager_disconnect_device;
 spice_usb_device_manager_disconnect_device_async;
 spice_usb_device_manager_disconnect_device_finish;
+spice_usb_device_manager_is_redirecting;
 spice_usb_device_manager_get;
 spice_usb_device_manager_get_devices;
 spice_usb_device_manager_get_devices_with_filter;
diff --git a/src/usb-device-manager.c b/src/usb-device-manager.c
index 1147643..1bbf6b3 100644
--- a/src/usb-device-manager.c
+++ b/src/usb-device-manager.c
@@ -231,6 +231,29 @@ _set_redirecting(SpiceUsbDeviceManager *self, gboolean is_redirecting)
 G_DEFINE_BOXED_TYPE(SpiceUsbDevice, spice_usb_device, g_object_ref, g_object_unref)
 #endif
 
+/**
+* spice_usb_device_manager_is_redirecting:
+* @manager: the #SpiceUsbDeviceManager manager
+*
+* Returns: %TRUE if device redirection negotiation flow is in progress
+*/
+gboolean spice_usb_device_manager_is_redirecting(SpiceUsbDeviceManager *self)
+{
+#ifdef USE_USBREDIR
+
+#ifdef USE_GUDEV
+    gboolean redirecting;
+    g_object_get(self->priv->udev, "redirecting", &redirecting, NULL);
+    return redirecting;
+#else
+    return self->priv->redirecting;
+#endif
+
+#else
+    return FALSE;
+#endif
+}
+
 static void spice_usb_device_manager_initable_iface_init(GInitableIface *iface);
 
 static guint signals[LAST_SIGNAL] = { 0, };
diff --git a/src/usb-device-manager.h b/src/usb-device-manager.h
index d1b0a96..a9a4f7a 100644
--- a/src/usb-device-manager.h
+++ b/src/usb-device-manager.h
@@ -138,6 +138,8 @@ spice_usb_device_manager_can_redirect_device(SpiceUsbDeviceManager  *self,
                                              SpiceUsbDevice         *device,
                                              GError                **err);
 
+gboolean spice_usb_device_manager_is_redirecting(SpiceUsbDeviceManager *self);
+
 G_END_DECLS
 
 #endif /* __SPICE_USB_DEVICE_MANAGER_H__ */
diff --git a/src/usb-device-widget.c b/src/usb-device-widget.c
index 3b060e9..285bfd5 100644
--- a/src/usb-device-widget.c
+++ b/src/usb-device-widget.c
@@ -410,6 +410,9 @@ static gboolean spice_usb_device_widget_update_status(gpointer user_data)
     gchar *str, *markup_str;
     const gchar *free_channels_str;
     int free_channels;
+    gboolean redirecting;
+
+    redirecting = spice_usb_device_manager_is_redirecting(priv->manager);
 
     g_object_get(priv->manager, "free-channels", &free_channels, NULL);
     free_channels_str = ngettext(_("Select USB devices to redirect (%d free channel)"),
@@ -430,6 +433,10 @@ static gboolean spice_usb_device_widget_update_status(gpointer user_data)
                                               GTK_STOCK_DIALOG_WARNING);
         g_free(priv->err_msg);
         priv->err_msg = NULL;
+    } else if (redirecting) {
+        spice_usb_device_widget_show_info_bar(self, _("Redirecting USB Device..."),
+                                              GTK_MESSAGE_INFO,
+                                              GTK_STOCK_DIALOG_INFO);
     } else {
         spice_usb_device_widget_hide_info_bar(self);
     }
commit 9fbf679453d8dbfe797a738cb536136599d7adab
Author: Kirill Moizik <kmoizik at redhat.com>
Date:   Tue Mar 8 16:05:57 2016 +0200

    usbredir: Disconnect USB device asynchronously
    
    Signed-off-by: Kirill Moizik <kmoizik at redhat.com>
    Signed-off-by: Dmitry Fleytman <dfleytma at redhat.com>
    Reviewed-by: Jonathon Jongsma <jjongsma at redhat.com>

diff --git a/src/channel-usbredir.c b/src/channel-usbredir.c
index 3803f77..a251855 100644
--- a/src/channel-usbredir.c
+++ b/src/channel-usbredir.c
@@ -115,20 +115,54 @@ static void spice_usbredir_channel_init(SpiceUsbredirChannel *channel)
 }
 
 #ifdef USE_USBREDIR
+
+static void _channel_reset_finish(SpiceUsbredirChannel *channel)
+{
+    SpiceUsbredirChannelPrivate *priv = channel->priv;
+
+    spice_usbredir_channel_lock(channel);
+
+    usbredirhost_close(priv->host);
+    priv->host = NULL;
+
+    /* Call set_context to re-create the host */
+    spice_usbredir_channel_set_context(channel, priv->context);
+
+    spice_usbredir_channel_unlock(channel);
+}
+
+static void _channel_reset_cb(GObject *gobject,
+                              GAsyncResult *result,
+                              gpointer user_data)
+{
+    SpiceChannel *spice_channel =  SPICE_CHANNEL(gobject);
+    SpiceUsbredirChannel *channel = SPICE_USBREDIR_CHANNEL(spice_channel);
+    gboolean migrating = GPOINTER_TO_UINT(user_data);
+    GError *err = NULL;
+
+    _channel_reset_finish(channel);
+
+    SPICE_CHANNEL_CLASS(spice_usbredir_channel_parent_class)->channel_reset(spice_channel, migrating);
+
+    spice_usbredir_channel_disconnect_device_finish(channel, result, &err);
+    g_object_unref(result);
+}
+
 static void spice_usbredir_channel_reset(SpiceChannel *c, gboolean migrating)
 {
     SpiceUsbredirChannel *channel = SPICE_USBREDIR_CHANNEL(c);
     SpiceUsbredirChannelPrivate *priv = channel->priv;
 
     if (priv->host) {
-        if (priv->state == STATE_CONNECTED)
-            spice_usbredir_channel_disconnect_device(channel);
-        usbredirhost_close(priv->host);
-        priv->host = NULL;
-        /* Call set_context to re-create the host */
-        spice_usbredir_channel_set_context(channel, priv->context);
+        if (priv->state == STATE_CONNECTED) {
+            spice_usbredir_channel_disconnect_device_async(channel, NULL,
+                _channel_reset_cb, GUINT_TO_POINTER(migrating));
+        } else {
+            _channel_reset_finish(channel);
+        }
+    } else {
+        SPICE_CHANNEL_CLASS(spice_usbredir_channel_parent_class)->channel_reset(c, migrating);
     }
-    SPICE_CHANNEL_CLASS(spice_usbredir_channel_parent_class)->channel_reset(c, migrating);
 }
 #endif
 
commit 4214e76d2c8cf5f321ae95a1f49d49efe7e10fa3
Author: Kirill Moizik <kmoizik at redhat.com>
Date:   Tue Mar 8 16:05:56 2016 +0200

    UsbDeviceManager: Track device disconnection operations in progress
    
    During device disconnection, unwanted hotplug events may happen.
    We need to ignore those therefore we track disconnection operations
    in progress.
    
    See also comment to commit
    "Do not process USB hotplug events while redirection is in progress"
    that introduces corresponding filtering out logic.
    
    Signed-off-by: Kirill Moizik <kmoizik at redhat.com>
    Signed-off-by: Dmitry Fleytman <dfleytma at redhat.com>
    Acked-by: Jonathon Jongsma <jjongsma at redhat.com>

diff --git a/src/usb-device-manager.c b/src/usb-device-manager.c
index 312dae7..1147643 100644
--- a/src/usb-device-manager.c
+++ b/src/usb-device-manager.c
@@ -1757,16 +1757,17 @@ void _disconnect_device_async_cb(GObject *gobject,
     SpiceUsbredirChannel *channel = SPICE_USBREDIR_CHANNEL(gobject);
     GTask *task = user_data;
     GError *err = NULL;
-
-#ifdef G_OS_WIN32
     disconnect_cb_data *data = g_task_get_task_data(task);
     SpiceUsbDeviceManager *self = SPICE_USB_DEVICE_MANAGER(data->self);
 
+#ifdef G_OS_WIN32
     if (self->priv->use_usbclerk) {
         _spice_usb_device_manager_uninstall_driver_async(self, data->device);
     }
 #endif
 
+    _set_redirecting(self, FALSE);
+
     spice_usbredir_channel_disconnect_device_finish(channel, channel_res, &err);
     if (err)
         g_task_return_error(task, err);
@@ -1793,6 +1794,8 @@ void spice_usb_device_manager_disconnect_device_async(SpiceUsbDeviceManager *sel
 
     SpiceUsbredirChannel *channel;
 
+    _set_redirecting(self, TRUE);
+
     channel = spice_usb_device_manager_get_channel_for_dev(self, device);
     nested  = g_task_new(G_OBJECT(self), cancellable, callback, user_data);
     disconnect_cb_data *data = g_new(disconnect_cb_data, 1);
commit 205e16d6d95e7966fb5ec69990468f748165c779
Author: Kirill Moizik <kmoizik at redhat.com>
Date:   Tue Mar 8 16:05:55 2016 +0200

    UsbDeviceManager: Implement asynchronous disconnect device flow
    
    This commit introduces functions for asynchronous disconnection flows.
    Following commits will make use of those.
    
    Thread safety is ensured the same way it was done for connection
    flow in previous commits. Disconnect logic is protected by the same
    locks that protect connect/usbredir/channel management logic.
    
    Signed-off-by: Kirill Moizik <kmoizik at redhat.com>
    Signed-off-by: Dmitry Fleytman <dfleytma at redhat.com>
    Signed-off-by: Jonathon Jongsma <jjongsma at redhat.com>

diff --git a/src/channel-usbredir-priv.h b/src/channel-usbredir-priv.h
index c987474..17e9716 100644
--- a/src/channel-usbredir-priv.h
+++ b/src/channel-usbredir-priv.h
@@ -33,6 +33,15 @@ G_BEGIN_DECLS
 void spice_usbredir_channel_set_context(SpiceUsbredirChannel *channel,
                                         libusb_context       *context);
 
+void spice_usbredir_channel_disconnect_device_async(SpiceUsbredirChannel *channel,
+                                                    GCancellable *cancellable,
+                                                    GAsyncReadyCallback callback,
+                                                    gpointer user_data);
+
+gboolean spice_usbredir_channel_disconnect_device_finish(SpiceUsbredirChannel *channel,
+                                                         GAsyncResult         *res,
+                                                         GError              **err);
+
 /* Note the context must be set, and the channel must be brought up
    (through spice_channel_connect()), before calling this. */
 void spice_usbredir_channel_connect_device_async(
diff --git a/src/channel-usbredir.c b/src/channel-usbredir.c
index 6ced764..3803f77 100644
--- a/src/channel-usbredir.c
+++ b/src/channel-usbredir.c
@@ -429,6 +429,8 @@ void spice_usbredir_channel_disconnect_device(SpiceUsbredirChannel *channel)
 
     CHANNEL_DEBUG(channel, "disconnecting device from usb channel %p", channel);
 
+    spice_usbredir_channel_lock(channel);
+
     switch (priv->state) {
     case STATE_DISCONNECTED:
     case STATE_DISCONNECTING:
@@ -462,6 +464,38 @@ void spice_usbredir_channel_disconnect_device(SpiceUsbredirChannel *channel)
         priv->state  = STATE_DISCONNECTED;
         break;
     }
+
+    spice_usbredir_channel_unlock(channel);
+}
+
+static void
+_disconnect_device_thread(GTask *task,
+                          gpointer object,
+                          gpointer task_data,
+                          GCancellable *cancellable)
+{
+    spice_usbredir_channel_disconnect_device(SPICE_USBREDIR_CHANNEL(object));
+    g_task_return_boolean(task, TRUE);
+}
+
+G_GNUC_INTERNAL
+gboolean spice_usbredir_channel_disconnect_device_finish(
+                                               SpiceUsbredirChannel *channel,
+                                               GAsyncResult         *res,
+                                               GError              **err)
+{
+    return g_task_propagate_boolean(G_TASK(res), err);
+}
+
+void spice_usbredir_channel_disconnect_device_async(SpiceUsbredirChannel *channel,
+                                                    GCancellable *cancellable,
+                                                    GAsyncReadyCallback callback,
+                                                    gpointer user_data)
+{
+    GTask* task = g_task_new(channel, cancellable, callback, user_data);
+
+    g_return_if_fail(channel != NULL);
+    g_task_run_in_thread(task, _disconnect_device_thread);
 }
 
 G_GNUC_INTERNAL
diff --git a/src/map-file b/src/map-file
index 123ef96..112b5ea 100644
--- a/src/map-file
+++ b/src/map-file
@@ -131,6 +131,8 @@ spice_usb_device_manager_can_redirect_device;
 spice_usb_device_manager_connect_device_async;
 spice_usb_device_manager_connect_device_finish;
 spice_usb_device_manager_disconnect_device;
+spice_usb_device_manager_disconnect_device_async;
+spice_usb_device_manager_disconnect_device_finish;
 spice_usb_device_manager_get;
 spice_usb_device_manager_get_devices;
 spice_usb_device_manager_get_devices_with_filter;
diff --git a/src/usb-device-manager.c b/src/usb-device-manager.c
index 0f8134f..312dae7 100644
--- a/src/usb-device-manager.c
+++ b/src/usb-device-manager.c
@@ -1676,6 +1676,26 @@ gboolean spice_usb_device_manager_connect_device_finish(
     return g_task_propagate_boolean(task, err);
 }
 
+/**
+ * spice_usb_device_manager_disconnect_device_finish:
+ * @self: a #SpiceUsbDeviceManager.
+ * @res: a #GAsyncResult
+ * @err: (allow-none): a return location for a #GError, or %NULL.
+ *
+ * Finishes an async operation. See spice_usb_device_manager_disconnect_device_async().
+ *
+ * Returns: %TRUE if disconnection is successful
+ */
+gboolean spice_usb_device_manager_disconnect_device_finish(
+    SpiceUsbDeviceManager *self, GAsyncResult *res, GError **err)
+{
+    GTask *task = G_TASK(res);
+
+    g_return_val_if_fail(g_task_is_valid(task, G_OBJECT(self)), FALSE);
+
+    return g_task_propagate_boolean(task, err);
+}
+
 #ifdef USE_USBREDIR
 static
 void _connect_device_async_cb(GObject *gobject,
@@ -1722,6 +1742,70 @@ void spice_usb_device_manager_disconnect_device(SpiceUsbDeviceManager *self,
 #endif
 }
 
+typedef struct _disconnect_cb_data
+{
+    SpiceUsbDeviceManager  *self;
+    SpiceUsbDevice         *device;
+} disconnect_cb_data;
+
+#ifdef USE_USBREDIR
+static
+void _disconnect_device_async_cb(GObject *gobject,
+                                 GAsyncResult *channel_res,
+                                 gpointer user_data)
+{
+    SpiceUsbredirChannel *channel = SPICE_USBREDIR_CHANNEL(gobject);
+    GTask *task = user_data;
+    GError *err = NULL;
+
+#ifdef G_OS_WIN32
+    disconnect_cb_data *data = g_task_get_task_data(task);
+    SpiceUsbDeviceManager *self = SPICE_USB_DEVICE_MANAGER(data->self);
+
+    if (self->priv->use_usbclerk) {
+        _spice_usb_device_manager_uninstall_driver_async(self, data->device);
+    }
+#endif
+
+    spice_usbredir_channel_disconnect_device_finish(channel, channel_res, &err);
+    if (err)
+        g_task_return_error(task, err);
+    else
+        g_task_return_boolean(task, TRUE);
+
+    g_object_unref(task);
+}
+#endif
+
+void spice_usb_device_manager_disconnect_device_async(SpiceUsbDeviceManager *self,
+                                                      SpiceUsbDevice *device,
+                                                      GCancellable *cancellable,
+                                                      GAsyncReadyCallback callback,
+                                                      gpointer user_data)
+{
+#ifdef USE_USBREDIR
+    GTask *nested;
+    g_return_if_fail(SPICE_IS_USB_DEVICE_MANAGER(self));
+
+    g_return_if_fail(device != NULL);
+
+    SPICE_DEBUG("disconnecting device %p", device);
+
+    SpiceUsbredirChannel *channel;
+
+    channel = spice_usb_device_manager_get_channel_for_dev(self, device);
+    nested  = g_task_new(G_OBJECT(self), cancellable, callback, user_data);
+    disconnect_cb_data *data = g_new(disconnect_cb_data, 1);
+    data->self = self;
+    data->device = device;
+    g_task_set_task_data(nested, data, g_free);
+
+    spice_usbredir_channel_disconnect_device_async(channel, cancellable,
+                                                   _disconnect_device_async_cb,
+                                                   nested);
+#endif
+}
+
 /**
  * spice_usb_device_manager_can_redirect_device:
  * @self: the #SpiceUsbDeviceManager manager
diff --git a/src/usb-device-manager.h b/src/usb-device-manager.h
index e05ebae..d1b0a96 100644
--- a/src/usb-device-manager.h
+++ b/src/usb-device-manager.h
@@ -116,9 +116,20 @@ void spice_usb_device_manager_connect_device_async(
                                              GCancellable *cancellable,
                                              GAsyncReadyCallback callback,
                                              gpointer user_data);
+
+void spice_usb_device_manager_disconnect_device_async(
+                                             SpiceUsbDeviceManager *manager,
+                                             SpiceUsbDevice *device,
+                                             GCancellable *cancellable,
+                                             GAsyncReadyCallback callback,
+                                             gpointer user_data);
+
 gboolean spice_usb_device_manager_connect_device_finish(
     SpiceUsbDeviceManager *self, GAsyncResult *res, GError **err);
 
+gboolean spice_usb_device_manager_disconnect_device_finish(
+    SpiceUsbDeviceManager *self, GAsyncResult *res, GError **err);
+
 void spice_usb_device_manager_disconnect_device(SpiceUsbDeviceManager *manager,
                                                 SpiceUsbDevice *device);
 
commit a123feddf906e961c9ee0eed5001d371f825e6d8
Author: Kirill Moizik <kmoizik at redhat.com>
Date:   Tue Mar 8 16:05:54 2016 +0200

    UsbDeviceManager: Track device redirection operations in progress
    
    During device connection, unwanted hotplug events may happen.
    We need to ignore those therefore we track redirection operations
    in progress.
    
    See also comment to commit
    "Do not process USB hotplug events while redirection is in progress"
    that introduces corresponding filtering out logic.
    
    Signed-off-by: Kirill Moizik <kmoizik at redhat.com>
    Signed-off-by: Dmitry Fleytman <dfleytma at redhat.com>

diff --git a/src/usb-device-manager.c b/src/usb-device-manager.c
index 3872bb6..0f8134f 100644
--- a/src/usb-device-manager.c
+++ b/src/usb-device-manager.c
@@ -121,6 +121,7 @@ struct _SpiceUsbDeviceManagerPrivate {
     GUdevClient *udev;
     libusb_device **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
@@ -207,10 +208,25 @@ _spice_usb_device_manager_connect_device_async(SpiceUsbDeviceManager *self,
                                                GAsyncReadyCallback callback,
                                                gpointer user_data);
 
+static
+void _connect_device_async_cb(GObject *gobject,
+                              GAsyncResult *channel_res,
+                              gpointer user_data);
+
 G_DEFINE_BOXED_TYPE(SpiceUsbDevice, spice_usb_device,
                     (GBoxedCopyFunc)spice_usb_device_ref,
                     (GBoxedFreeFunc)spice_usb_device_unref)
 
+static void
+_set_redirecting(SpiceUsbDeviceManager *self, gboolean is_redirecting)
+{
+#ifdef USE_GUDEV
+    g_object_set(self->priv->udev, "redirecting", is_redirecting, NULL);
+#else
+    self->priv->redirecting = is_redirecting;
+#endif
+}
+
 #else
 G_DEFINE_BOXED_TYPE(SpiceUsbDevice, spice_usb_device, g_object_ref, g_object_unref)
 #endif
@@ -1163,7 +1179,7 @@ static void spice_usb_device_manager_drv_install_cb(GObject *gobject,
     SpiceUsbDevice *device;
     UsbInstallCbInfo *cbinfo;
     GCancellable *cancellable;
-    GAsyncReadyCallback callback;
+    gpointer data;
 
     g_return_if_fail(user_data != NULL);
 
@@ -1172,8 +1188,7 @@ static void spice_usb_device_manager_drv_install_cb(GObject *gobject,
     device      = cbinfo->device;
     installer   = cbinfo->installer;
     cancellable = cbinfo->cancellable;
-    callback    = cbinfo->callback;
-    user_data   = cbinfo->user_data;
+    data        = cbinfo->user_data;
 
     g_free(cbinfo);
 
@@ -1195,8 +1210,8 @@ static void spice_usb_device_manager_drv_install_cb(GObject *gobject,
     _spice_usb_device_manager_connect_device_async(self,
                                                    device,
                                                    cancellable,
-                                                   callback,
-                                                   user_data);
+                                                   _connect_device_async_cb,
+                                                   data);
 
     spice_usb_device_unref(device);
 }
@@ -1523,6 +1538,8 @@ _spice_usb_device_manager_uninstall_driver_async(SpiceUsbDeviceManager *self,
 
 #endif
 
+#ifdef USE_USBREDIR
+
 static void
 _spice_usb_device_manager_connect_device_async(SpiceUsbDeviceManager *self,
                                                SpiceUsbDevice *device,
@@ -1539,7 +1556,6 @@ _spice_usb_device_manager_connect_device_async(SpiceUsbDeviceManager *self,
 
     task = g_task_new(self, cancellable, callback, user_data);
 
-#ifdef USE_USBREDIR
     SpiceUsbDeviceManagerPrivate *priv = self->priv;
     libusb_device *libdev;
     guint i;
@@ -1585,17 +1601,16 @@ _spice_usb_device_manager_connect_device_async(SpiceUsbDeviceManager *self,
         libusb_unref_device(libdev);
         return;
     }
-#endif
 
     g_task_return_new_error(task,
                             SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
                             _("No free USB channel"));
-#ifdef USE_USBREDIR
 done:
-#endif
     g_object_unref(task);
 }
 
+#endif
+
 /**
  * spice_usb_device_manager_connect_device_async:
  * @self: a #SpiceUsbDeviceManager.
@@ -1614,11 +1629,19 @@ void spice_usb_device_manager_connect_device_async(SpiceUsbDeviceManager *self,
                                              GAsyncReadyCallback callback,
                                              gpointer user_data)
 {
+    g_return_if_fail(SPICE_IS_USB_DEVICE_MANAGER(self));
 
-#if defined(USE_USBREDIR) && defined(G_OS_WIN32)
+#ifdef USE_USBREDIR
+
+    GTask *task =
+        g_task_new(G_OBJECT(self), cancellable, callback, user_data);
+
+    _set_redirecting(self, TRUE);
+
+#ifdef G_OS_WIN32
     if (self->priv->use_usbclerk) {
         _spice_usb_device_manager_install_driver_async(self, device, cancellable,
-                                                       callback, user_data);
+                                                       callback, task);
         return;
     }
 #endif
@@ -1626,8 +1649,10 @@ void spice_usb_device_manager_connect_device_async(SpiceUsbDeviceManager *self,
     _spice_usb_device_manager_connect_device_async(self,
                                                    device,
                                                    cancellable,
-                                                   callback,
-                                                   user_data);
+                                                   _connect_device_async_cb,
+                                                   task);
+
+#endif
 }
 
 /**
@@ -1651,6 +1676,22 @@ gboolean spice_usb_device_manager_connect_device_finish(
     return g_task_propagate_boolean(task, err);
 }
 
+#ifdef USE_USBREDIR
+static
+void _connect_device_async_cb(GObject *gobject,
+                              GAsyncResult *channel_res,
+                              gpointer user_data)
+{
+    SpiceUsbDeviceManager *self = SPICE_USB_DEVICE_MANAGER(gobject);
+    GTask *task = user_data;
+
+    _set_redirecting(self, FALSE);
+
+    g_task_return_boolean(task, TRUE);
+    g_object_unref(task);
+}
+#endif
+
 /**
  * spice_usb_device_manager_disconnect_device:
  * @manager: the #SpiceUsbDeviceManager manager
commit 4eea5f3afeb4ad8c5f47cc03793474602696ca29
Author: Kirill Moizik <kmoizik at redhat.com>
Date:   Tue Mar 8 16:05:53 2016 +0200

    usbredir: Spawn a different thread for device redirection flow
    
    On Windows when using usbdk, opening and closing USB device handle,
    i.e. calling libusb_open()/libusb_unref_device() can block for a few
    seconds (3-5 second more specifically on patch author's HW).
    
    libusb_open() is called by spice_usbredir_channel_open_device().
    
    libusb_unref_device() is called implicitly via
    usbredirhost_set_device(..., NULL) from
    spice_usbredir_channel_disconnect_device().
    
    Both these calls happen on the main thread. If it blocks,
    this causes the UI to freeze.
    
    This commit makes sure libusb_open() is called from a different
    thread to avoid blocking the mainloop when usbdk is used.
    
    Following commits also move usbredirhost_set_device(..., NULL) call
    to separate threads.
    
    Since this commit introduces additional execution contexts running in
    parallel to the main thread there are thread safety concerns to be secured.
    Mainly there are 3 types of objects accessed by newly introduced threads:
      1. libusb contexts
      2. usbredir context
      3. redirection channels
    
    Fortunately libusb accesses are either thread safe or already
    performed by a separate thread and protected by locks as needed.
    
    As for channels and usbredir, in order to achieve thread safety
    additional locks were introduced by previous patches
    in preparation to adding asynchronous contexts:
    
    1. Channel objects data accesses from different threads protected with a
       new lock (device_connect_mutex);
    2. Handling usbredir messages protected by the same new lock in order to
       ensure there are no messages processing flows in progress when device gets
       connected or disconnected.
    
    Signed-off-by: Kirill Moizik <kmoizik at redhat.com>
    Signed-off-by: Dmitry Fleytman <dfleytma at redhat.com>
    Signed-off-by: Jonathon Jongsma <jjongsma at redhat.com>

diff --git a/src/channel-usbredir.c b/src/channel-usbredir.c
index a2a9343..6ced764 100644
--- a/src/channel-usbredir.c
+++ b/src/channel-usbredir.c
@@ -319,6 +319,36 @@ static void spice_usbredir_channel_open_acl_cb(
 }
 #endif
 
+#ifndef USE_POLKIT
+static void
+_open_device_async_cb(GTask *task,
+                      gpointer object,
+                      gpointer task_data,
+                      GCancellable *cancellable)
+{
+    GError *err = NULL;
+    SpiceUsbredirChannel *channel = SPICE_USBREDIR_CHANNEL(object);
+    SpiceUsbredirChannelPrivate *priv = channel->priv;
+
+    spice_usbredir_channel_lock(channel);
+
+    if (!spice_usbredir_channel_open_device(channel, &err)) {
+        libusb_unref_device(priv->device);
+        priv->device = NULL;
+        g_boxed_free(spice_usb_device_get_type(), priv->spice_device);
+        priv->spice_device = NULL;
+    }
+
+    spice_usbredir_channel_unlock(channel);
+
+    if (err) {
+        g_task_return_error(task, err);
+    } else {
+        g_task_return_boolean(task, TRUE);
+    }
+}
+#endif
+
 G_GNUC_INTERNAL
 void spice_usbredir_channel_connect_device_async(
                                           SpiceUsbredirChannel *channel,
@@ -330,9 +360,6 @@ void spice_usbredir_channel_connect_device_async(
 {
     SpiceUsbredirChannelPrivate *priv = channel->priv;
     GTask *task;
-#ifndef USE_POLKIT
-    GError *err = NULL;
-#endif
 
     g_return_if_fail(SPICE_IS_USBREDIR_CHANNEL(channel));
     g_return_if_fail(device != NULL);
@@ -375,15 +402,7 @@ void spice_usbredir_channel_connect_device_async(
                                         channel);
     return;
 #else
-    if (!spice_usbredir_channel_open_device(channel, &err)) {
-        g_task_return_error(task, err);
-        libusb_unref_device(priv->device);
-        priv->device = NULL;
-        g_boxed_free(spice_usb_device_get_type(), priv->spice_device);
-        priv->spice_device = NULL;
-    } else {
-        g_task_return_boolean(task, TRUE);
-    }
+    g_task_run_in_thread(task, _open_device_async_cb);
 #endif
 
 done:
commit 7fec6cd96176290ffe475612535d139936d31c0b
Author: Kirill Moizik <kmoizik at redhat.com>
Date:   Tue Mar 8 16:05:52 2016 +0200

    usbredir: Protect data accessed by asynchronous redirection flows
    
    This commit adds locking to ensure thread safety required
    after start/stop redirection flows moved to separate threads.
    This is done in preparation to following commits that
    will introduce actual multithreaded access to corresponding
    routines.
    
    Signed-off-by: Kirill Moizik <kmoizik at redhat.com>
    Signed-off-by: Dmitry Fleytman <dfleytma at redhat.com>
    Acked-by: Jonathon Jongsma <jjongsma at redhat.com>

diff --git a/src/channel-usbredir.c b/src/channel-usbredir.c
index ff238e5..a2a9343 100644
--- a/src/channel-usbredir.c
+++ b/src/channel-usbredir.c
@@ -656,6 +656,8 @@ static void usbredir_handle_msg(SpiceChannel *c, SpiceMsgIn *in)
     priv->read_buf = buf;
     priv->read_buf_size = size;
 
+    spice_usbredir_channel_lock(channel);
+
     r = usbredirhost_read_guest_data(priv->host);
     if (r != 0) {
         SpiceUsbDevice *spice_device = priv->spice_device;
@@ -663,7 +665,10 @@ static void usbredir_handle_msg(SpiceChannel *c, SpiceMsgIn *in)
         gchar *desc;
         GError *err;
 
-        g_return_if_fail(spice_device != NULL);
+        if (spice_device == NULL) {
+            spice_usbredir_channel_unlock(channel);
+            return;
+        }
 
         desc = spice_usb_device_get_description(spice_device, NULL);
         switch (r) {
@@ -700,6 +705,8 @@ static void usbredir_handle_msg(SpiceChannel *c, SpiceMsgIn *in)
 
         g_error_free(err);
     }
+
+    spice_usbredir_channel_unlock(channel);
 }
 
 #endif /* USE_USBREDIR */
diff --git a/src/usb-device-manager.c b/src/usb-device-manager.c
index be8b517..3872bb6 100644
--- a/src/usb-device-manager.c
+++ b/src/usb-device-manager.c
@@ -1353,9 +1353,13 @@ 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))
+        if (spice_usb_manager_device_equal_libdev(manager, device, libdev)) {
+            spice_usbredir_channel_unlock(channel);
             return channel;
+        }
+        spice_usbredir_channel_unlock(channel);
     }
 #endif
     return NULL;
@@ -1749,9 +1753,13 @@ spice_usb_device_manager_can_redirect_device(SpiceUsbDeviceManager  *self,
     /* Check if there are free channels */
     for (i = 0; i < priv->channels->len; i++) {
         SpiceUsbredirChannel *channel = g_ptr_array_index(priv->channels, i);
+        spice_usbredir_channel_lock(channel);
 
-        if (!spice_usbredir_channel_get_device(channel))
+        if (!spice_usbredir_channel_get_device(channel)){
+            spice_usbredir_channel_unlock(channel);
             break;
+        }
+        spice_usbredir_channel_unlock(channel);
     }
     if (i == priv->channels->len) {
         g_set_error_literal(err, SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
commit b4b4187813cdd6c137915bf3967bfaeac0f14a36
Author: Kirill Moizik <kmoizik at redhat.com>
Date:   Tue Mar 8 16:05:51 2016 +0200

    GUdevClient: Do not process USB hotplug events while redirection is in progress
    
    USB redirection flow on Windows includes a number of reset requests issued
    to the port that hosts the device deing redirected.
    
    Each port reset emulates device removal and reinsertion and produces
    corresponding hotplug events and a number of device list updates on
    different levels of USB stack and USB redirection engine.
    
    As a result, queriyng USB device list performed by spice-gtk's hotplug
    event handler may return inconsistent results if performed in parallel
    to redirection flow.
    
    This patch suppresses handling of USB hotplug events during redirection
    and injects a simulated hotplug event after redirection completion
    in order to properly process real device list changes in case they
    happened during the redirection flow.
    
    Signed-off-by: Kirill Moizik <kmoizik at redhat.com>
    Signed-off-by: Dmitry Fleytman <dfleytma at redhat.com>
    Acked-by: Jonathon Jongsma <jjongsma at redhat.com>

diff --git a/src/win-usb-dev.c b/src/win-usb-dev.c
index 0e86d39..1cf9f21 100644
--- a/src/win-usb-dev.c
+++ b/src/win-usb-dev.c
@@ -293,6 +293,8 @@ static void g_udev_client_get_property(GObject     *gobject,
     }
 }
 
+static void handle_dev_change(GUdevClient *self);
+
 static void g_udev_client_set_property(GObject       *gobject,
                                        guint          prop_id,
                                        const GValue  *value,
@@ -300,10 +302,18 @@ static void g_udev_client_set_property(GObject       *gobject,
 {
     GUdevClient *self = G_UDEV_CLIENT(gobject);
     GUdevClientPrivate *priv = self->priv;
+    gboolean old_val;
 
     switch (prop_id) {
     case PROP_REDIRECTING:
+        old_val = priv->redirecting;
         priv->redirecting = g_value_get_boolean(value);
+        if (old_val && !priv->redirecting) {
+            /* This is a redirection completion case.
+               Inject hotplug event in case we missed device changes
+               during redirection processing. */
+            handle_dev_change(self);
+        }
         break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, pspec);
@@ -412,6 +422,15 @@ static void handle_dev_change(GUdevClient *self)
     GError *err = NULL;
     GList *now_devs = NULL;
 
+    if (priv->redirecting == TRUE) {
+        /* On Windows, querying USB device list may return inconsistent results
+           if performed in parallel to redirection flow.
+           A simulated hotplug event will be injected after redirection
+           completion in order to process real device list changes that may
+           had taken place during redirection process. */
+        return;
+    }
+
     if(g_udev_client_list_devices(self, &now_devs, &err, __FUNCTION__) < 0) {
         g_warning("could not retrieve device list");
         return;
commit 4ec353ea4c156063128af32a0bfcdfa7358c4d56
Author: Kirill Moizik <kmoizik at redhat.com>
Date:   Tue Mar 8 16:05:50 2016 +0200

    win-usb-dev: Track device redirection operations in progress
    
    This commit introduces redirecting property of GUdevClient
    
    This property indicates when a redirection operation
    is in progress on a device. It's set back to FALSE
    once the device is fully redirected to the guest.
    
    Signed-off-by: Kirill Moizik <kmoizik at redhat.com>
    Signed-off-by: Dmitry Fleytman <dfleytma at redhat.com>
    Acked-by: Jonathon Jongsma <jjongsma at redhat.com>

diff --git a/src/win-usb-dev.c b/src/win-usb-dev.c
index 2cb3108..0e86d39 100644
--- a/src/win-usb-dev.c
+++ b/src/win-usb-dev.c
@@ -32,10 +32,16 @@
 #define G_UDEV_CLIENT_GET_PRIVATE(obj) \
     (G_TYPE_INSTANCE_GET_PRIVATE((obj), G_UDEV_TYPE_CLIENT, GUdevClientPrivate))
 
+enum {
+    PROP_0,
+    PROP_REDIRECTING
+};
+
 struct _GUdevClientPrivate {
     libusb_context *ctx;
     GList *udev_list;
     HWND hwnd;
+    gboolean redirecting;
 };
 
 #define G_UDEV_CLIENT_WINCLASS_NAME  TEXT("G_UDEV_CLIENT")
@@ -269,11 +275,50 @@ static void g_udev_client_finalize(GObject *gobject)
         G_OBJECT_CLASS(g_udev_client_parent_class)->finalize(gobject);
 }
 
+static void g_udev_client_get_property(GObject     *gobject,
+                                       guint        prop_id,
+                                       GValue      *value,
+                                       GParamSpec  *pspec)
+{
+    GUdevClient *self = G_UDEV_CLIENT(gobject);
+    GUdevClientPrivate *priv = self->priv;
+
+    switch (prop_id) {
+    case PROP_REDIRECTING:
+        g_value_set_boolean(value, priv->redirecting);
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, pspec);
+        break;
+    }
+}
+
+static void g_udev_client_set_property(GObject       *gobject,
+                                       guint          prop_id,
+                                       const GValue  *value,
+                                       GParamSpec    *pspec)
+{
+    GUdevClient *self = G_UDEV_CLIENT(gobject);
+    GUdevClientPrivate *priv = self->priv;
+
+    switch (prop_id) {
+    case PROP_REDIRECTING:
+        priv->redirecting = g_value_get_boolean(value);
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, pspec);
+        break;
+    }
+}
+
 static void g_udev_client_class_init(GUdevClientClass *klass)
 {
     GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
+    GParamSpec *pspec;
 
     gobject_class->finalize = g_udev_client_finalize;
+    gobject_class->get_property = g_udev_client_get_property;
+    gobject_class->set_property = g_udev_client_set_property;
 
     signals[UEVENT_SIGNAL] =
         g_signal_new("uevent",
@@ -287,6 +332,20 @@ static void g_udev_client_class_init(GUdevClientClass *klass)
                      G_TYPE_STRING,
                      G_UDEV_TYPE_DEVICE);
 
+    /**
+    * GUdevClient::redirecting:
+    *
+    * This property indicates when a redirection operation
+    * is in progress on a device. It's set back to FALSE
+    * once the device is fully redirected to the guest.
+    */
+    pspec = g_param_spec_boolean("redirecting", "Redirecting",
+                                 "USB redirection operation is in progress",
+                                 FALSE,
+                                 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+    g_object_class_install_property(gobject_class, PROP_REDIRECTING, pspec);
+
     g_type_class_add_private(klass, sizeof(GUdevClientPrivate));
 }
 
commit 8ed40007bd9f441c5e530bfbec6ae72c9b2accfe
Author: Kirill Moizik <kmoizik at redhat.com>
Date:   Tue Mar 8 16:05:49 2016 +0200

    usbredir: Introduce mutex for device (dis)connection
    
    This commit introduces channel mutex to allow usage of
    channel objects in mutithreaded environments.
    
    This mutex will be used by future commits to protect
    thread unsafe usbredir functions and data structures.
    
    Signed-off-by: Kirill Moizik <kmoizik at redhat.com>
    Signed-off-by: Dmitry Fleytman <dfleytma at redhat.com>
    Acked-by: Jonathon Jongsma <jjongsma at redhat.com>

diff --git a/src/channel-usbredir-priv.h b/src/channel-usbredir-priv.h
index 2c4c6f7..c987474 100644
--- a/src/channel-usbredir-priv.h
+++ b/src/channel-usbredir-priv.h
@@ -51,6 +51,10 @@ void spice_usbredir_channel_disconnect_device(SpiceUsbredirChannel *channel);
 
 libusb_device *spice_usbredir_channel_get_device(SpiceUsbredirChannel *channel);
 
+void spice_usbredir_channel_lock(SpiceUsbredirChannel *channel);
+
+void spice_usbredir_channel_unlock(SpiceUsbredirChannel *channel);
+
 void spice_usbredir_channel_get_guest_filter(
                           SpiceUsbredirChannel               *channel,
                           const struct usbredirfilter_rule  **rules_ret,
diff --git a/src/channel-usbredir.c b/src/channel-usbredir.c
index ab90800..ff238e5 100644
--- a/src/channel-usbredir.c
+++ b/src/channel-usbredir.c
@@ -78,6 +78,7 @@ struct _SpiceUsbredirChannelPrivate {
     GTask *task;
     SpiceUsbAclHelper *acl_helper;
 #endif
+    STATIC_MUTEX device_connect_mutex;
 };
 
 static void channel_set_handlers(SpiceChannelClass *klass);
@@ -109,6 +110,7 @@ static void spice_usbredir_channel_init(SpiceUsbredirChannel *channel)
 {
 #ifdef USE_USBREDIR
     channel->priv = SPICE_USBREDIR_CHANNEL_GET_PRIVATE(channel);
+    STATIC_MUTEX_INIT(channel->priv->device_connect_mutex);
 #endif
 }
 
@@ -184,6 +186,9 @@ static void spice_usbredir_channel_finalize(GObject *obj)
 
     if (channel->priv->host)
         usbredirhost_close(channel->priv->host);
+#ifdef USE_USBREDIR
+    STATIC_MUTEX_CLEAR(channel->priv->device_connect_mutex);
+#endif
 
     /* Chain up to the parent class */
     if (G_OBJECT_CLASS(spice_usbredir_channel_parent_class)->finalize)
@@ -566,6 +571,16 @@ static void *usbredir_alloc_lock(void) {
     return mutex;
 }
 
+void spice_usbredir_channel_lock(SpiceUsbredirChannel *channel)
+{
+    STATIC_MUTEX_LOCK(channel->priv->device_connect_mutex);
+}
+
+void spice_usbredir_channel_unlock(SpiceUsbredirChannel *channel)
+{
+    STATIC_MUTEX_UNLOCK(channel->priv->device_connect_mutex);
+}
+
 static void usbredir_lock_lock(void *user_data) {
     GMutex *mutex = user_data;
 
commit 2a01b4a041cfe489549692be06b64034ce5ecfbf
Author: Dmitry Fleytman <dmitry at daynix.com>
Date:   Tue Mar 8 16:05:48 2016 +0200

    win-usb-dev: Fix device (un)plug notification handler
    
    This patch fixes device list change notification handing
    logic for cases when more than one device being plugged
    or unplugged simultaneously.
    
    The simplest way to reproduce the problematic scenario
    is (un)plugging of a usb HUB with a few devices inserted.
    
    Signed-off-by: Dmitry Fleytman <dmitry at daynix.com>
    Acked-by: Jonathon Jongsma <jjongsma at redhat.com>

diff --git a/src/win-usb-dev.c b/src/win-usb-dev.c
index 1e4b2d6..2cb3108 100644
--- a/src/win-usb-dev.c
+++ b/src/win-usb-dev.c
@@ -34,7 +34,6 @@
 
 struct _GUdevClientPrivate {
     libusb_context *ctx;
-    gssize udev_list_size;
     GList *udev_list;
     HWND hwnd;
 };
@@ -196,9 +195,7 @@ g_udev_client_initable_init(GInitable *initable, GCancellable *cancellable,
     }
 
     /* get initial device list */
-    priv->udev_list_size = g_udev_client_list_devices(self, &priv->udev_list,
-                                                      err, __FUNCTION__);
-    if (priv->udev_list_size < 0) {
+    if (g_udev_client_list_devices(self, &priv->udev_list, err, __FUNCTION__) < 0) {
         goto g_udev_client_init_failed;
     }
 
@@ -319,94 +316,60 @@ static gboolean get_usb_dev_info(libusb_device *dev, GUdevDeviceInfo *udevinfo)
 }
 
 /* Only vid:pid are compared */
-static gboolean gudev_devices_are_equal(GUdevDevice *a, GUdevDevice *b)
+static gint gudev_devices_differ(gconstpointer a, gconstpointer b)
 {
     GUdevDeviceInfo *ai, *bi;
     gboolean same_vid;
     gboolean same_pid;
 
-    ai = a->priv->udevinfo;
-    bi = b->priv->udevinfo;
+    ai = G_UDEV_DEVICE(a)->priv->udevinfo;
+    bi = G_UDEV_DEVICE(b)->priv->udevinfo;
 
     same_vid  = (ai->vid == bi->vid);
     same_pid  = (ai->pid == bi->pid);
 
-    return (same_pid && same_vid);
+    return (same_pid && same_vid) ? 0 : -1;
 }
 
+static void notify_dev_state_change(GUdevClient *self,
+                                    GList *old_list,
+                                    GList *new_list,
+                                    const gchar *action)
+{
+    GList *dev;
+
+    for (dev = g_list_first(old_list); dev != NULL; dev = g_list_next(dev)) {
+        if (g_list_find_custom(new_list, dev->data, gudev_devices_differ) == NULL) {
+            /* Found a device that changed its state */
+            g_udev_device_print(dev->data, action);
+            g_signal_emit(self, signals[UEVENT_SIGNAL], 0, action, dev->data);
+        }
+    }
+}
 
-/* Assumes each event stands for a single device change (at most) */
 static void handle_dev_change(GUdevClient *self)
 {
     GUdevClientPrivate *priv = self->priv;
-    GUdevDevice *changed_dev = NULL;
-    ssize_t dev_count;
-    int is_dev_change;
     GError *err = NULL;
     GList *now_devs = NULL;
-    GList *llist, *slist; /* long-list and short-list*/
-    GList *lit, *sit; /* iterators for long-list and short-list */
-    GUdevDevice *ldev, *sdev; /* devices on long-list and short-list */
-
-    dev_count = g_udev_client_list_devices(self, &now_devs, &err,
-                                           __FUNCTION__);
-    g_return_if_fail(dev_count >= 0);
 
-    SPICE_DEBUG("number of current devices %"G_GSSIZE_FORMAT
-                ", I know about %"G_GSSIZE_FORMAT" devices",
-                dev_count, priv->udev_list_size);
-
-    is_dev_change = dev_count - priv->udev_list_size;
-    if (is_dev_change == 0) {
-        g_udev_client_free_device_list(&now_devs);
+    if(g_udev_client_list_devices(self, &now_devs, &err, __FUNCTION__) < 0) {
+        g_warning("could not retrieve device list");
         return;
     }
 
-    if (is_dev_change > 0) {
-        llist  = now_devs;
-        slist = priv->udev_list;
-    } else {
-        llist = priv->udev_list;
-        slist  = now_devs;
-    }
-
-    g_udev_device_print_list(llist, "handle_dev_change: long list:");
-    g_udev_device_print_list(slist, "handle_dev_change: short list:");
-
-    /* Go over the longer list */
-    for (lit = g_list_first(llist); lit != NULL; lit=g_list_next(lit)) {
-        ldev = lit->data;
-        /* Look for dev in the shorther list */
-        for (sit = g_list_first(slist); sit != NULL; sit=g_list_next(sit)) {
-            sdev = sit->data;
-            if (gudev_devices_are_equal(ldev, sdev))
-                break;
-        }
-        if (sit == NULL) {
-            /* Found a device which appears only in the longer list */
-            changed_dev = ldev;
-            break;
-        }
-    }
+    g_udev_device_print_list(now_devs, "handle_dev_change: current list:");
+    g_udev_device_print_list(priv->udev_list, "handle_dev_change: previous list:");
 
-    if (!changed_dev) {
-        g_warning("couldn't find any device change");
-        goto leave;
-    }
+    /* Unregister devices that are not present anymore */
+    notify_dev_state_change(self, priv->udev_list, now_devs, "remove");
 
-    if (is_dev_change > 0) {
-        g_udev_device_print(changed_dev, ">>> USB device inserted");
-        g_signal_emit(self, signals[UEVENT_SIGNAL], 0, "add", changed_dev);
-    } else {
-        g_udev_device_print(changed_dev, "<<< USB device removed");
-        g_signal_emit(self, signals[UEVENT_SIGNAL], 0, "remove", changed_dev);
-    }
+    /* Register newly inserted devices */
+    notify_dev_state_change(self, now_devs, priv->udev_list, "add");
 
-leave:
     /* keep most recent info: free previous list, and keep current list */
     g_udev_client_free_device_list(&priv->udev_list);
     priv->udev_list = now_devs;
-    priv->udev_list_size = dev_count;
 }
 
 static LRESULT CALLBACK wnd_proc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
commit 75b1a5469cfde373f48bc24b05376fdf065c16b5
Author: Dmitry Fleytman <dmitry at daynix.com>
Date:   Thu Feb 11 18:04:54 2016 +0200

    usb-dev-manager: Fix cbinfo leak in case of abnormal return
    
    Signed-off-by: Dmitry Fleytman <dmitry at daynix.com>
    Acked-by: Jonathon Jongsma <jjongsma at redhat.com>

diff --git a/src/usb-device-manager.c b/src/usb-device-manager.c
index a954857..be8b517 100644
--- a/src/usb-device-manager.c
+++ b/src/usb-device-manager.c
@@ -1205,23 +1205,26 @@ static void spice_usb_device_manager_drv_uninstall_cb(GObject *gobject,
                                                       GAsyncResult *res,
                                                       gpointer user_data)
 {
+    GError *err = NULL;
+
     UsbInstallCbInfo *cbinfo = user_data;
     SpiceUsbDeviceManager *self = cbinfo->manager;
-    GError *err = NULL;
+    SpiceUsbDevice *device = cbinfo->device;
+    SpiceWinUsbDriver *installer = cbinfo->installer;
+
+    g_free(cbinfo);
 
     SPICE_DEBUG("Win USB driver uninstall finished");
     g_return_if_fail(SPICE_IS_USB_DEVICE_MANAGER(self));
     g_return_if_fail(self->priv->use_usbclerk);
 
-    if (!spice_win_usb_driver_uninstall_finish(cbinfo->installer, res, &err)) {
+    if (!spice_win_usb_driver_uninstall_finish(installer, res, &err)) {
         g_warning("win usb driver uninstall failed -- %s", err->message);
         g_clear_error(&err);
     }
 
-    spice_usb_device_set_state(cbinfo->device, SPICE_USB_DEVICE_STATE_NONE);
-
-    spice_usb_device_unref(cbinfo->device);
-    g_free(cbinfo);
+    spice_usb_device_set_state(device, SPICE_USB_DEVICE_STATE_NONE);
+    spice_usb_device_unref(device);
 }
 
 #endif
commit 6aa4f155158a1804abcf162db2ec97771d40415f
Author: Dmitry Fleytman <dmitry at daynix.com>
Date:   Thu Feb 11 18:04:53 2016 +0200

    win-usbredir: Use UsbDk backend when available
    
    Signed-off-by: Dmitry Fleytman <dmitry at daynix.com>
    Acked-by: Jonathon Jongsma <jjongsma at redhat.com>

diff --git a/src/usb-device-manager.c b/src/usb-device-manager.c
index e4090aa..a954857 100644
--- a/src/usb-device-manager.c
+++ b/src/usb-device-manager.c
@@ -230,7 +230,8 @@ static void spice_usb_device_manager_init(SpiceUsbDeviceManager *self)
     self->priv = priv;
 
 #if defined(G_OS_WIN32) && defined(USE_USBREDIR)
-    priv->use_usbclerk = TRUE;
+    priv->use_usbclerk = !usbdk_is_driver_installed() ||
+                         !(priv->usbdk_api = usbdk_api_load());
 #endif
     priv->channels = g_ptr_array_new();
 #ifdef USE_USBREDIR
@@ -372,6 +373,7 @@ static void spice_usb_device_manager_finalize(GObject *gobject)
     }
     if (!priv->use_usbclerk) {
         _usbdk_hider_clear(self);
+        usbdk_api_unload(priv->usbdk_api);
     }
 #endif
 #endif
commit 1accb3b6e76aad916aa1cc6290e6f60aa973c30a
Author: Dmitry Fleytman <dmitry at daynix.com>
Date:   Thu Feb 11 18:04:52 2016 +0200

    win-usbredir: Do not use UsbClerk for non-WinUsb backends
    
    Signed-off-by: Dmitry Fleytman <dmitry at daynix.com>
    Acked-by: Jonathon Jongsma <jjongsma at redhat.com>

diff --git a/src/usb-device-manager.c b/src/usb-device-manager.c
index 11c3e4f..e4090aa 100644
--- a/src/usb-device-manager.c
+++ b/src/usb-device-manager.c
@@ -254,10 +254,12 @@ static gboolean spice_usb_device_manager_initable_init(GInitable  *initable,
 #endif
 
 #ifdef G_OS_WIN32
-    priv->installer = spice_win_usb_driver_new(err);
-    if (!priv->installer) {
-        SPICE_DEBUG("failed to initialize winusb driver");
-        return FALSE;
+    if (priv->use_usbclerk) {
+        priv->installer = spice_win_usb_driver_new(err);
+        if (!priv->installer) {
+            SPICE_DEBUG("failed to initialize winusb driver");
+            return FALSE;
+        }
     }
 #endif
 
@@ -364,8 +366,10 @@ static void spice_usb_device_manager_finalize(GObject *gobject)
     free(priv->auto_conn_filter_rules);
     free(priv->redirect_on_connect_rules);
 #ifdef G_OS_WIN32
-    if (priv->installer)
+    if (priv->installer) {
+        g_warn_if_fail(priv->use_usbclerk);
         g_object_unref(priv->installer);
+    }
     if (!priv->use_usbclerk) {
         _usbdk_hider_clear(self);
     }
@@ -963,12 +967,14 @@ static void spice_usb_device_manager_remove_dev(SpiceUsbDeviceManager *self,
     }
 
 #ifdef G_OS_WIN32
-    const guint8 state = spice_usb_device_get_state(device);
-    if ((state == SPICE_USB_DEVICE_STATE_INSTALLING) ||
-        (state == SPICE_USB_DEVICE_STATE_UNINSTALLING)) {
-        SPICE_DEBUG("skipping " DEV_ID_FMT ". It is un/installing its driver",
-                    bus, address);
-        return;
+    if (priv->use_usbclerk) {
+        const guint8 state = spice_usb_device_get_state(device);
+        if ((state == SPICE_USB_DEVICE_STATE_INSTALLING) ||
+            (state == SPICE_USB_DEVICE_STATE_UNINSTALLING)) {
+            SPICE_DEBUG("skipping " DEV_ID_FMT ". It is un/installing its driver",
+                        bus, address);
+            return;
+        }
     }
 #endif
 
@@ -1170,6 +1176,7 @@ static void spice_usb_device_manager_drv_install_cb(GObject *gobject,
     g_free(cbinfo);
 
     g_return_if_fail(SPICE_IS_USB_DEVICE_MANAGER(self));
+    g_return_if_fail(self->priv->use_usbclerk);
     g_return_if_fail(SPICE_IS_WIN_USB_DRIVER(installer));
     g_return_if_fail(device!= NULL);
 
@@ -1202,6 +1209,7 @@ static void spice_usb_device_manager_drv_uninstall_cb(GObject *gobject,
 
     SPICE_DEBUG("Win USB driver uninstall finished");
     g_return_if_fail(SPICE_IS_USB_DEVICE_MANAGER(self));
+    g_return_if_fail(self->priv->use_usbclerk);
 
     if (!spice_win_usb_driver_uninstall_finish(cbinfo->installer, res, &err)) {
         g_warning("win usb driver uninstall failed -- %s", err->message);
@@ -1599,15 +1607,18 @@ void spice_usb_device_manager_connect_device_async(SpiceUsbDeviceManager *self,
 {
 
 #if defined(USE_USBREDIR) && defined(G_OS_WIN32)
-    _spice_usb_device_manager_install_driver_async(self, device, cancellable,
-                                                   callback, user_data);
-#else
+    if (self->priv->use_usbclerk) {
+        _spice_usb_device_manager_install_driver_async(self, device, cancellable,
+                                                       callback, user_data);
+        return;
+    }
+#endif
+
     _spice_usb_device_manager_connect_device_async(self,
                                                    device,
                                                    cancellable,
                                                    callback,
                                                    user_data);
-#endif
 }
 
 /**
@@ -1654,7 +1665,8 @@ void spice_usb_device_manager_disconnect_device(SpiceUsbDeviceManager *self,
         spice_usbredir_channel_disconnect_device(channel);
 
 #ifdef G_OS_WIN32
-    _spice_usb_device_manager_uninstall_driver_async(self, device);
+    if(self->priv->use_usbclerk)
+        _spice_usb_device_manager_uninstall_driver_async(self, device);
 #endif
 
 #endif
commit c6955ad2106e087c38b3cc2ba797dfe16310155d
Author: Dmitry Fleytman <dmitry at daynix.com>
Date:   Thu Feb 11 18:04:51 2016 +0200

    win-usbredir: Move installer interaction logic to separate functions
    
    This is a refactoring done in preparation for the next commits.
    
    Signed-off-by: Dmitry Fleytman <dmitry at daynix.com>
    Acked-by: Jonathon Jongsma <jjongsma at redhat.com>

diff --git a/src/usb-device-manager.c b/src/usb-device-manager.c
index 8f4a047..11c3e4f 100644
--- a/src/usb-device-manager.c
+++ b/src/usb-device-manager.c
@@ -1444,6 +1444,68 @@ gboolean spice_usb_device_manager_is_device_connected(SpiceUsbDeviceManager *sel
     return !!spice_usb_device_manager_get_channel_for_dev(self, device);
 }
 
+#if defined(USE_USBREDIR) && defined(G_OS_WIN32)
+
+static void
+_spice_usb_device_manager_install_driver_async(SpiceUsbDeviceManager *self,
+                                               SpiceUsbDevice *device,
+                                               GCancellable *cancellable,
+                                               GAsyncReadyCallback callback,
+                                               gpointer user_data)
+{
+    SpiceWinUsbDriver *installer;
+    UsbInstallCbInfo *cbinfo;
+
+    g_return_if_fail(self->priv->installer);
+
+    spice_usb_device_set_state(device, SPICE_USB_DEVICE_STATE_INSTALLING);
+
+    installer = self->priv->installer;
+    cbinfo = g_new0(UsbInstallCbInfo, 1);
+    cbinfo->manager     = self;
+    cbinfo->device      = spice_usb_device_ref(device);
+    cbinfo->installer   = installer;
+    cbinfo->cancellable = cancellable;
+    cbinfo->callback    = callback;
+    cbinfo->user_data   = user_data;
+
+    spice_win_usb_driver_install_async(installer, device, cancellable,
+                                       spice_usb_device_manager_drv_install_cb,
+                                       cbinfo);
+}
+
+static void
+_spice_usb_device_manager_uninstall_driver_async(SpiceUsbDeviceManager *self,
+                                                 SpiceUsbDevice *device)
+{
+    SpiceWinUsbDriver *installer;
+    UsbInstallCbInfo *cbinfo;
+    guint8 state;
+
+    g_warn_if_fail(device != NULL);
+    g_return_if_fail(self->priv->installer);
+
+    state = spice_usb_device_get_state(device);
+    if ((state != SPICE_USB_DEVICE_STATE_INSTALLED) &&
+        (state != SPICE_USB_DEVICE_STATE_CONNECTED)) {
+        return;
+    }
+
+    spice_usb_device_set_state(device, SPICE_USB_DEVICE_STATE_UNINSTALLING);
+
+    installer = self->priv->installer;
+    cbinfo = g_new0(UsbInstallCbInfo, 1);
+    cbinfo->manager     = self;
+    cbinfo->device      = spice_usb_device_ref(device);
+    cbinfo->installer   = installer;
+
+    spice_win_usb_driver_uninstall_async(installer, device, NULL,
+                                         spice_usb_device_manager_drv_uninstall_cb,
+                                         cbinfo);
+}
+
+#endif
+
 static void
 _spice_usb_device_manager_connect_device_async(SpiceUsbDeviceManager *self,
                                                SpiceUsbDevice *device,
@@ -1537,25 +1599,8 @@ void spice_usb_device_manager_connect_device_async(SpiceUsbDeviceManager *self,
 {
 
 #if defined(USE_USBREDIR) && defined(G_OS_WIN32)
-    SpiceWinUsbDriver *installer;
-    UsbInstallCbInfo *cbinfo;
-
-    g_return_if_fail(self->priv->installer);
-
-    spice_usb_device_set_state(device, SPICE_USB_DEVICE_STATE_INSTALLING);
-
-    installer = self->priv->installer;
-    cbinfo = g_new0(UsbInstallCbInfo, 1);
-    cbinfo->manager     = self;
-    cbinfo->device      = spice_usb_device_ref(device);
-    cbinfo->installer   = installer;
-    cbinfo->cancellable = cancellable;
-    cbinfo->callback    = callback;
-    cbinfo->user_data   = user_data;
-
-    spice_win_usb_driver_install_async(installer, device, cancellable,
-                                       spice_usb_device_manager_drv_install_cb,
-                                       cbinfo);
+    _spice_usb_device_manager_install_driver_async(self, device, cancellable,
+                                                   callback, user_data);
 #else
     _spice_usb_device_manager_connect_device_async(self,
                                                    device,
@@ -1609,30 +1654,7 @@ void spice_usb_device_manager_disconnect_device(SpiceUsbDeviceManager *self,
         spice_usbredir_channel_disconnect_device(channel);
 
 #ifdef G_OS_WIN32
-    SpiceWinUsbDriver *installer;
-    UsbInstallCbInfo *cbinfo;
-    guint8 state;
-
-    g_warn_if_fail(device != NULL);
-    g_return_if_fail(self->priv->installer);
-
-    state = spice_usb_device_get_state(device);
-    if ((state != SPICE_USB_DEVICE_STATE_INSTALLED) &&
-        (state != SPICE_USB_DEVICE_STATE_CONNECTED)) {
-        return;
-    }
-
-    spice_usb_device_set_state(device, SPICE_USB_DEVICE_STATE_UNINSTALLING);
-
-    installer = self->priv->installer;
-    cbinfo = g_new0(UsbInstallCbInfo, 1);
-    cbinfo->manager     = self;
-    cbinfo->device      = spice_usb_device_ref(device);
-    cbinfo->installer   = installer;
-
-    spice_win_usb_driver_uninstall_async(installer, device, NULL,
-                                         spice_usb_device_manager_drv_uninstall_cb,
-                                         cbinfo);
+    _spice_usb_device_manager_uninstall_driver_async(self, device);
 #endif
 
 #endif
commit 0453325b49999c4c67065e2a0f58b577f6b20ede
Author: Dmitry Fleytman <dmitry at daynix.com>
Date:   Thu Feb 11 18:04:50 2016 +0200

    usbdk: Load hide rules for auto-redirected devices
    
    Hide rules order UsbDk to avoid showing specific USB
    devices to Windows PnP manager.
    
    Spice-gtk loads hide rules for devices that should be
    automatically redirected on connection to prevent Windows
    from showing "New Hardware Found" wizard window for USB
    devices that do not have driver on the local system.
    
    Signed-off-by: Dmitry Fleytman <dmitry at daynix.com>

diff --git a/src/usb-device-manager.c b/src/usb-device-manager.c
index d719b03..8f4a047 100644
--- a/src/usb-device-manager.c
+++ b/src/usb-device-manager.c
@@ -27,6 +27,10 @@
 #include <errno.h>
 #include <libusb.h>
 
+#ifdef G_OS_WIN32
+#include "usbdk_api.h"
+#endif
+
 #if defined(USE_GUDEV)
 #include <gudev/gudev.h>
 #elif defined(G_OS_WIN32)
@@ -120,6 +124,8 @@ struct _SpiceUsbDeviceManagerPrivate {
     libusb_hotplug_callback_handle hp_handle;
 #endif
 #ifdef G_OS_WIN32
+    usbdk_api_wrapper     *usbdk_api;
+    HANDLE                 usbdk_hider_handle;
     SpiceWinUsbDriver     *installer;
 #endif
     gboolean               use_usbclerk;
@@ -182,6 +188,9 @@ static void spice_usb_device_unref(SpiceUsbDevice *device);
 #ifdef G_OS_WIN32
 static guint8 spice_usb_device_get_state(SpiceUsbDevice *device);
 static void  spice_usb_device_set_state(SpiceUsbDevice *device, guint8 s);
+
+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,
@@ -357,6 +366,9 @@ static void spice_usb_device_manager_finalize(GObject *gobject)
 #ifdef G_OS_WIN32
     if (priv->installer)
         g_object_unref(priv->installer);
+    if (!priv->use_usbclerk) {
+        _usbdk_hider_clear(self);
+    }
 #endif
 #endif
 
@@ -428,6 +440,11 @@ static void spice_usb_device_manager_set_property(GObject       *gobject,
         break;
     case PROP_AUTO_CONNECT:
         priv->auto_connect = g_value_get_boolean(value);
+#if defined(G_OS_WIN32) && defined(USE_USBREDIR)
+        if (!priv->use_usbclerk) {
+            _usbdk_hider_update(self);
+        }
+#endif
         break;
     case PROP_AUTO_CONNECT_FILTER: {
         const gchar *filter = g_value_get_string(value);
@@ -450,6 +467,12 @@ static void spice_usb_device_manager_set_property(GObject       *gobject,
 #endif
         g_free(priv->auto_connect_filter);
         priv->auto_connect_filter = g_strdup(filter);
+
+#if defined(G_OS_WIN32) && defined(USE_USBREDIR)
+        if (!priv->use_usbclerk) {
+            _usbdk_hider_update(self);
+        }
+#endif
         break;
     }
     case PROP_REDIRECT_ON_CONNECT: {
@@ -1852,6 +1875,65 @@ guint8 spice_usb_device_get_state(SpiceUsbDevice *device)
 
     return info->state;
 }
+
+static
+gboolean _usbdk_hider_prepare(SpiceUsbDeviceManager *manager)
+{
+    SpiceUsbDeviceManagerPrivate *priv = manager->priv;
+
+    g_return_val_if_fail(!priv->use_usbclerk, FALSE);
+
+    if (priv->usbdk_hider_handle == NULL) {
+        priv->usbdk_hider_handle = usbdk_create_hider_handle(priv->usbdk_api);
+        if (priv->usbdk_hider_handle == NULL) {
+            g_warning("Failed to instantiate UsbDk hider interface");
+            return FALSE;
+        }
+    }
+
+    return TRUE;
+}
+
+static
+void _usbdk_hider_clear(SpiceUsbDeviceManager *manager)
+{
+    SpiceUsbDeviceManagerPrivate *priv = manager->priv;
+
+    g_return_if_fail(!priv->use_usbclerk);
+
+    if (priv->usbdk_hider_handle != NULL) {
+        usbdk_clear_hide_rules(priv->usbdk_api, priv->usbdk_hider_handle);
+        usbdk_close_hider_handle(priv->usbdk_api, priv->usbdk_hider_handle);
+        priv->usbdk_hider_handle = NULL;
+    }
+}
+
+static
+void _usbdk_hider_update(SpiceUsbDeviceManager *manager)
+{
+    SpiceUsbDeviceManagerPrivate *priv = manager->priv;
+
+    g_return_if_fail(!priv->use_usbclerk);
+
+    if (priv->auto_connect_filter == NULL) {
+        SPICE_DEBUG("No autoredirect rules, no hider setup needed");
+        _usbdk_hider_clear(manager);
+        return;
+    }
+
+    if (!priv->auto_connect) {
+        SPICE_DEBUG("Auto-connect disabled, no hider setup needed");
+        _usbdk_hider_clear(manager);
+        return;
+    }
+
+    if(_usbdk_hider_prepare(manager)) {
+        usbdk_api_set_hide_rules(priv->usbdk_api,
+                                 priv->usbdk_hider_handle,
+                                 priv->auto_connect_filter);
+    }
+}
+
 #endif
 
 static SpiceUsbDevice *spice_usb_device_ref(SpiceUsbDevice *device)
commit bb42f8a41d7b6a9aec7744f15d8fab2e0d38687b
Author: Dmitry Fleytman <dmitry at daynix.com>
Date:   Thu Feb 11 18:04:49 2016 +0200

    win-usbredir: Only match USB devices by VID:PID when WinUsb used
    
    In other cases match devices by BUS:ADDR.
    
    This commit introduces use_usbclerk flag which is set TRUE
    unconditionally for Windows builds for now. Next patches
    will introduce UsbDk backend integrartion which will set
    this flag in accordance to UsbDk presence on the system.
    
    Signed-off-by: Dmitry Fleytman <dmitry at daynix.com>
    Acked-by: Jonathon Jongsma <jjongsma at redhat.com>

diff --git a/src/usb-device-manager.c b/src/usb-device-manager.c
index a6b5e51..d719b03 100644
--- a/src/usb-device-manager.c
+++ b/src/usb-device-manager.c
@@ -122,6 +122,7 @@ struct _SpiceUsbDeviceManagerPrivate {
 #ifdef G_OS_WIN32
     SpiceWinUsbDriver     *installer;
 #endif
+    gboolean               use_usbclerk;
 #endif
     GPtrArray *devices;
     GPtrArray *channels;
@@ -219,6 +220,9 @@ static void spice_usb_device_manager_init(SpiceUsbDeviceManager *self)
     priv = SPICE_USB_DEVICE_MANAGER_GET_PRIVATE(self);
     self->priv = priv;
 
+#if defined(G_OS_WIN32) && defined(USE_USBREDIR)
+    priv->use_usbclerk = TRUE;
+#endif
     priv->channels = g_ptr_array_new();
 #ifdef USE_USBREDIR
     priv->devices  = g_ptr_array_new_with_free_func((GDestroyNotify)
@@ -678,13 +682,15 @@ static gboolean spice_usb_device_manager_get_udev_bus_n_address(
 
     *bus = *address = 0;
 
-#ifndef G_OS_WIN32
-    bus_str = g_udev_device_get_property(udev, "BUSNUM");
-    address_str = g_udev_device_get_property(udev, "DEVNUM");
-#else /* Windows -- request vid:pid instead */
-    bus_str = g_udev_device_get_property(udev, "VID");
-    address_str = g_udev_device_get_property(udev, "PID");
-#endif
+    if (manager->priv->use_usbclerk) {
+       /* Windows WinUsb/UsbClerk -- request vid:pid instead */
+        bus_str = g_udev_device_get_property(udev, "VID");
+        address_str = g_udev_device_get_property(udev, "PID");
+    } else {
+       /* Linux or UsbDk backend on Windows*/
+        bus_str = g_udev_device_get_property(udev, "BUSNUM");
+        address_str = g_udev_device_get_property(udev, "DEVNUM");
+    }
     if (bus_str)
         *bus = atoi(bus_str);
     if (address_str)
@@ -823,13 +829,17 @@ static void spice_usb_device_manager_auto_connect_cb(GObject      *gobject,
     spice_usb_device_unref(device);
 }
 
-#ifndef G_OS_WIN32 /* match functions for Linux -- match by bus.addr */
 static gboolean
 spice_usb_device_manager_device_match(SpiceUsbDeviceManager *self, SpiceUsbDevice *device,
                                       const int bus, const int address)
 {
-    return (spice_usb_device_get_busnum(device) == bus &&
-            spice_usb_device_get_devaddr(device) == address);
+   if (self->priv->use_usbclerk) {
+        return (spice_usb_device_get_vid(device) == bus &&
+                spice_usb_device_get_pid(device) == address);
+    } else {
+        return (spice_usb_device_get_busnum(device) == bus &&
+                spice_usb_device_get_devaddr(device) == address);
+    }
 }
 
 #ifdef USE_GUDEV
@@ -837,32 +847,21 @@ static gboolean
 spice_usb_device_manager_libdev_match(SpiceUsbDeviceManager *self, libusb_device *libdev,
                                       const int bus, const int address)
 {
-    return (libusb_get_bus_number(libdev) == bus &&
-            libusb_get_device_address(libdev) == address);
-}
-#endif
-
-#else /* Win32 -- match functions for Windows -- match by vid:pid */
-static gboolean
-spice_usb_device_manager_device_match(SpiceUsbDeviceManager *self, SpiceUsbDevice *device,
-                                      const int vid, const int pid)
-{
-    return (spice_usb_device_get_vid(device) == vid &&
-            spice_usb_device_get_pid(device) == pid);
-}
+    if (self->priv->use_usbclerk) {
+        /* WinUSB -- match functions for Windows -- match by vid:pid */
+        int vid, pid;
 
-static gboolean
-spice_usb_device_manager_libdev_match(SpiceUsbDeviceManager *self, libusb_device *libdev,
-                                      const int vid, const int pid)
-{
-    int vid2, pid2;
-
-    if (!spice_usb_device_manager_get_libdev_vid_pid(libdev, &vid2, &pid2)) {
-        return FALSE;
+        if (!spice_usb_device_manager_get_libdev_vid_pid(libdev, &vid, &pid)) {
+            return FALSE;
+        }
+        return (bus == vid && address == pid);
+    } else {
+        /* match functions for Linux/UsbDk -- match by bus.addr */
+        return (libusb_get_bus_number(libdev) == bus &&
+                libusb_get_device_address(libdev) == address);
     }
-    return (vid == vid2 && pid == pid2);
 }
-#endif /* of Win32 -- match functions */
+#endif
 
 static SpiceUsbDevice*
 spice_usb_device_manager_find_device(SpiceUsbDeviceManager *self,
@@ -1900,14 +1899,21 @@ spice_usb_manager_device_equal_libdev(SpiceUsbDeviceManager *manager,
                                       SpiceUsbDevice *device,
                                       libusb_device  *libdev)
 {
-    int vid, pid;
+    int busnum, devaddr;
 
     if ((device == NULL) || (libdev == NULL))
         return FALSE;
 
-    vid = spice_usb_device_get_vid(device);
-    pid = spice_usb_device_get_pid(device);
-    return spice_usb_device_manager_libdev_match(manager, libdev, vid, pid);
+    if (manager->priv->use_usbclerk) {
+        busnum = spice_usb_device_get_vid(device);
+        devaddr = spice_usb_device_get_pid(device);
+    } else {
+        busnum = spice_usb_device_get_busnum(device);
+        devaddr = spice_usb_device_get_devaddr(device);
+    }
+
+    return spice_usb_device_manager_libdev_match(manager, libdev,
+                                                 busnum, devaddr);
 }
 #endif
 
commit d6551c43d2e4ae627235e99a79b061e1dec9a4d5
Author: Christophe Fergeau <cfergeau at redhat.com>
Date:   Thu Feb 11 18:04:48 2016 +0200

    Add SpiceUsbDeviceManager parameter to device comparison functions
    
    This additional parameter is currently unused, but this is in
    preparation for the next commits.
    Acked-by: Jonathon Jongsma <jjongsma at redhat.com>

diff --git a/src/usb-device-manager.c b/src/usb-device-manager.c
index 85231a1..a6b5e51 100644
--- a/src/usb-device-manager.c
+++ b/src/usb-device-manager.c
@@ -183,8 +183,9 @@ static guint8 spice_usb_device_get_state(SpiceUsbDevice *device);
 static void  spice_usb_device_set_state(SpiceUsbDevice *device, guint8 s);
 #endif
 
-static gboolean spice_usb_device_equal_libdev(SpiceUsbDevice *device,
-                                              libusb_device *libdev);
+static gboolean spice_usb_manager_device_equal_libdev(SpiceUsbDeviceManager *manager,
+                                                      SpiceUsbDevice *device,
+                                                      libusb_device *libdev);
 static libusb_device *
 spice_usb_device_manager_device_to_libdev(SpiceUsbDeviceManager *self,
                                           SpiceUsbDevice *device);
@@ -670,7 +671,8 @@ static void spice_usb_device_manager_class_init(SpiceUsbDeviceManagerClass *klas
 
 #ifdef USE_GUDEV
 static gboolean spice_usb_device_manager_get_udev_bus_n_address(
-    GUdevDevice *udev, int *bus, int *address)
+    SpiceUsbDeviceManager *manager, GUdevDevice *udev,
+    int *bus, int *address)
 {
     const gchar *bus_str, *address_str;
 
@@ -823,7 +825,7 @@ static void spice_usb_device_manager_auto_connect_cb(GObject      *gobject,
 
 #ifndef G_OS_WIN32 /* match functions for Linux -- match by bus.addr */
 static gboolean
-spice_usb_device_manager_device_match(SpiceUsbDevice *device,
+spice_usb_device_manager_device_match(SpiceUsbDeviceManager *self, SpiceUsbDevice *device,
                                       const int bus, const int address)
 {
     return (spice_usb_device_get_busnum(device) == bus &&
@@ -832,7 +834,7 @@ spice_usb_device_manager_device_match(SpiceUsbDevice *device,
 
 #ifdef USE_GUDEV
 static gboolean
-spice_usb_device_manager_libdev_match(libusb_device *libdev,
+spice_usb_device_manager_libdev_match(SpiceUsbDeviceManager *self, libusb_device *libdev,
                                       const int bus, const int address)
 {
     return (libusb_get_bus_number(libdev) == bus &&
@@ -842,7 +844,7 @@ spice_usb_device_manager_libdev_match(libusb_device *libdev,
 
 #else /* Win32 -- match functions for Windows -- match by vid:pid */
 static gboolean
-spice_usb_device_manager_device_match(SpiceUsbDevice *device,
+spice_usb_device_manager_device_match(SpiceUsbDeviceManager *self, SpiceUsbDevice *device,
                                       const int vid, const int pid)
 {
     return (spice_usb_device_get_vid(device) == vid &&
@@ -850,7 +852,7 @@ spice_usb_device_manager_device_match(SpiceUsbDevice *device,
 }
 
 static gboolean
-spice_usb_device_manager_libdev_match(libusb_device *libdev,
+spice_usb_device_manager_libdev_match(SpiceUsbDeviceManager *self, libusb_device *libdev,
                                       const int vid, const int pid)
 {
     int vid2, pid2;
@@ -872,7 +874,7 @@ spice_usb_device_manager_find_device(SpiceUsbDeviceManager *self,
 
     for (i = 0; i < priv->devices->len; i++) {
         curr = g_ptr_array_index(priv->devices, i);
-        if (spice_usb_device_manager_device_match(curr, bus, address)) {
+        if (spice_usb_device_manager_device_match(self, curr, bus, address)) {
             device = curr;
             break;
         }
@@ -975,7 +977,7 @@ static void spice_usb_device_manager_add_udev(SpiceUsbDeviceManager  *self,
     if (!devtype || strcmp(devtype, "usb_device"))
         return;
 
-    if (!spice_usb_device_manager_get_udev_bus_n_address(udev, &bus, &address)) {
+    if (!spice_usb_device_manager_get_udev_bus_n_address(self, udev, &bus, &address)) {
         g_warning("USB device without bus number or device address");
         return;
     }
@@ -996,7 +998,7 @@ static void spice_usb_device_manager_add_udev(SpiceUsbDeviceManager  *self,
         libusb_get_device_list(priv->context, &dev_list);
 
     for (i = 0; dev_list && dev_list[i]; i++) {
-        if (spice_usb_device_manager_libdev_match(dev_list[i], bus, address)) {
+        if (spice_usb_device_manager_libdev_match(self, dev_list[i], bus, address)) {
             libdev = dev_list[i];
             break;
         }
@@ -1017,7 +1019,7 @@ static void spice_usb_device_manager_remove_udev(SpiceUsbDeviceManager  *self,
 {
     int bus, address;
 
-    if (!spice_usb_device_manager_get_udev_bus_n_address(udev, &bus, &address))
+    if (!spice_usb_device_manager_get_udev_bus_n_address(self, udev, &bus, &address))
         return;
 
     spice_usb_device_manager_remove_dev(self, bus, address);
@@ -1317,7 +1319,7 @@ 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);
         libusb_device *libdev = spice_usbredir_channel_get_device(channel);
-        if (spice_usb_device_equal_libdev(device, libdev))
+        if (spice_usb_manager_device_equal_libdev(manager, device, libdev))
             return channel;
     }
 #endif
@@ -1881,8 +1883,9 @@ static void spice_usb_device_unref(SpiceUsbDevice *device)
 
 #ifndef G_OS_WIN32 /* Linux -- directly compare libdev */
 static gboolean
-spice_usb_device_equal_libdev(SpiceUsbDevice *device,
-                              libusb_device  *libdev)
+spice_usb_manager_device_equal_libdev(SpiceUsbDeviceManager *manager,
+                                      SpiceUsbDevice *device,
+                                      libusb_device  *libdev)
 {
     SpiceUsbDeviceInfo *info = (SpiceUsbDeviceInfo *)device;
 
@@ -1893,8 +1896,9 @@ spice_usb_device_equal_libdev(SpiceUsbDevice *device,
 }
 #else /* Windows -- compare vid:pid of device and libdev */
 static gboolean
-spice_usb_device_equal_libdev(SpiceUsbDevice *device,
-                              libusb_device  *libdev)
+spice_usb_manager_device_equal_libdev(SpiceUsbDeviceManager *manager,
+                                      SpiceUsbDevice *device,
+                                      libusb_device  *libdev)
 {
     int vid, pid;
 
@@ -1903,7 +1907,7 @@ spice_usb_device_equal_libdev(SpiceUsbDevice *device,
 
     vid = spice_usb_device_get_vid(device);
     pid = spice_usb_device_get_pid(device);
-    return spice_usb_device_manager_libdev_match(libdev, vid, pid);
+    return spice_usb_device_manager_libdev_match(manager, libdev, vid, pid);
 }
 #endif
 
@@ -1936,7 +1940,7 @@ spice_usb_device_manager_device_to_libdev(SpiceUsbDeviceManager *self,
         return NULL;
 
     for (i = 0; (d = devlist[i]) != NULL; i++) {
-        if (spice_usb_device_equal_libdev(device, d)) {
+        if (spice_usb_manager_device_equal_libdev(self, device, d)) {
             libusb_ref_device(d);
             break;
         }
commit 293e481d4af8b05fda7d810e5cde199c4f29e825
Author: Dmitry Fleytman <dmitry at daynix.com>
Date:   Thu Feb 11 18:04:47 2016 +0200

    win-usbredir: Introduce UsbDk wrapper
    
    Introduce UsbDk API definitions and binding code.
    
    UsbDk API DLL is loaded dynamically and wrapped by
    a thin glue code layer. This approach was chosen in
    order to make spice-gtk functional without UsbDk
    installed.
    
    Next patches will introduce dynamic backend selection
    logic, i.e. spice-gtk will try to use UsbDk by
    default and fallback to the old WinUsb/usbclerk
    scheme in case UsbDk is not available.
    
    Signed-off-by: Kirill Moizik <kirillm at daynix.com>
    Signed-off-by: Dmitry Fleytman <dmitry at daynix.com>
    Acked-by: Jonathon Jongsma <jjongsma at redhat.com>

diff --git a/src/Makefile.am b/src/Makefile.am
index 330c85d..0ef3bea 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -355,6 +355,8 @@ WIN_USB_FILES= \
 	win-usb-clerk.h			\
 	win-usb-driver-install.h	\
 	win-usb-driver-install.c	\
+	usbdk_api.h			\
+	usbdk_api.c			\
 	$(NULL)
 
 if OS_WIN32
diff --git a/src/usbdk_api.c b/src/usbdk_api.c
new file mode 100644
index 0000000..f5009e5
--- /dev/null
+++ b/src/usbdk_api.c
@@ -0,0 +1,187 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+   Copyright (C) 2014-2015 Red Hat, Inc.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, see <http://www.gnu.org/licenses/>.
+
+  Authors:
+    Dmitry Fleytman <dmitry at daynix.com>
+    Kirill Moizik <kirill at daynix.com>
+*/
+#include <config.h>
+
+#include <windows.h>
+#include <glib-object.h>
+#include "usbdk_api.h"
+#include "channel-usbredir-priv.h"
+
+#define USB_DK_HIDE_RULE_MATCH_ALL ((ULONG64)(-1))
+typedef struct tag_USB_DK_HIDE_RULE
+{
+    ULONG64 Hide;
+    ULONG64 Class;
+    ULONG64 VID;
+    ULONG64 PID;
+    ULONG64 BCD;
+} USB_DK_HIDE_RULE, *PUSB_DK_HIDE_RULE;
+
+typedef HANDLE(__cdecl *USBDK_CREATEHIDERHANDLE)(void);
+typedef BOOL(__cdecl * USBDK_ADDHIDERULE)(HANDLE hider_handle, PUSB_DK_HIDE_RULE rule);
+typedef BOOL(__cdecl *USBDK_CLEARHIDERULES)(HANDLE hider_handle);
+typedef void(__cdecl *USBDK_CLOSEHIDERHANDLE)(HANDLE hider_handle);
+
+struct tag_usbdk_api_wrapper
+{
+    HMODULE                                 module;
+    USBDK_CREATEHIDERHANDLE                 CreateHiderHandle;
+    USBDK_ADDHIDERULE                       AddRule;
+    USBDK_CLEARHIDERULES                    ClearRules;
+    USBDK_CLOSEHIDERHANDLE                  CloseHiderHandle;
+};
+
+BOOL usbdk_is_driver_installed(void)
+{
+    gboolean usbdk_installed = FALSE;
+    SC_HANDLE managerHandle = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
+
+    if (managerHandle) {
+        SC_HANDLE serviceHandle = OpenService(managerHandle, TEXT("UsbDk"), GENERIC_READ);
+
+        if (serviceHandle) {
+            SPICE_DEBUG("UsbDk driver is installed.");
+            usbdk_installed = TRUE;
+            CloseServiceHandle(serviceHandle);
+        }
+        CloseServiceHandle(managerHandle);
+    }
+    return usbdk_installed;
+}
+
+void usbdk_api_unload(usbdk_api_wrapper *usbdk_api)
+{
+    if (usbdk_api != NULL) {
+        if (usbdk_api->module != NULL) {
+            SPICE_DEBUG("Unloading UsbDk API DLL");
+            FreeLibrary(usbdk_api->module);
+        }
+        g_free(usbdk_api);
+    }
+}
+
+usbdk_api_wrapper *usbdk_api_load(void)
+{
+    usbdk_api_wrapper *usbdk_api = g_new0(usbdk_api_wrapper, 1);
+
+    SPICE_DEBUG("Loading UsbDk API DLL");
+    usbdk_api->module = LoadLibraryA("UsbDkHelper");
+    if (usbdk_api->module == NULL) {
+        g_warning("Failed to load UsbDkHelper.dll, error %lu", GetLastError());
+        goto error_unload;
+    }
+
+    usbdk_api->CreateHiderHandle = (USBDK_CREATEHIDERHANDLE)
+        GetProcAddress(usbdk_api->module, "UsbDk_CreateHiderHandle");
+    if (usbdk_api->CreateHiderHandle == NULL) {
+        g_warning("Failed to find CreateHandle entry point");
+        goto error_unload;
+    }
+
+    usbdk_api->AddRule = (USBDK_ADDHIDERULE)
+        GetProcAddress(usbdk_api->module, "UsbDk_AddHideRule");
+    if (usbdk_api->AddRule == NULL) {
+        g_warning("Failed to find AddRule entry point");
+        goto error_unload;
+    }
+
+    usbdk_api->ClearRules = (USBDK_CLEARHIDERULES)
+        GetProcAddress(usbdk_api->module, "UsbDk_ClearHideRules");
+    if (usbdk_api->ClearRules == NULL) {
+        g_warning("Failed to find ClearRules entry point");
+        goto error_unload;
+    }
+
+    usbdk_api->CloseHiderHandle = (USBDK_CLOSEHIDERHANDLE)
+        GetProcAddress(usbdk_api->module, "UsbDk_CloseHiderHandle");
+    if (usbdk_api->CloseHiderHandle == NULL) {
+        g_warning("Failed to find CloseHiderHandle  entry point");
+        goto error_unload;
+    }
+    return usbdk_api;
+
+error_unload:
+    usbdk_api_unload(usbdk_api);
+    return NULL;
+}
+
+static uint64_t usbdk_usbredir_field_to_usbdk(int value)
+{
+    if (value >= 0) {
+        return value;
+    }
+
+    g_warn_if_fail(value == -1);
+
+    return USB_DK_HIDE_RULE_MATCH_ALL;
+}
+
+static BOOL usbdk_add_hide_rule(usbdk_api_wrapper *usbdk_api,
+                                HANDLE hider_handle,
+                                PUSB_DK_HIDE_RULE rule)
+{
+    return usbdk_api->AddRule(hider_handle, rule);
+}
+
+void usbdk_api_set_hide_rules(usbdk_api_wrapper *usbdk_api, HANDLE hider_handle,
+                              gchar *redirect_on_connect)
+{
+    struct usbredirfilter_rule *rules;
+    int r, count;
+
+    r = usbredirfilter_string_to_rules(redirect_on_connect, ",", "|",
+                                       &rules, &count);
+    if (r) {
+        g_warning("auto-connect rules parsing failed with error %d", r);
+        return;
+    }
+
+    for (int i = 0; i < count; i++) {
+        USB_DK_HIDE_RULE rule;
+        rule.Hide  = usbdk_usbredir_field_to_usbdk(rules[i].allow);
+        rule.Class = usbdk_usbredir_field_to_usbdk(rules[i].device_class);
+        rule.VID   = usbdk_usbredir_field_to_usbdk(rules[i].vendor_id);
+        rule.PID   = usbdk_usbredir_field_to_usbdk(rules[i].product_id);
+        rule.BCD   = usbdk_usbredir_field_to_usbdk(rules[i].device_version_bcd);
+        if (!usbdk_add_hide_rule(usbdk_api, hider_handle, &rule)) {
+            SPICE_DEBUG("UsbDk add hide rule API failed");
+        }
+    }
+
+    free(rules);
+}
+
+HANDLE usbdk_create_hider_handle(usbdk_api_wrapper *usbdk_api)
+{
+    HANDLE handle = usbdk_api->CreateHiderHandle();
+    return (handle == INVALID_HANDLE_VALUE) ? NULL : handle;
+}
+
+BOOL usbdk_clear_hide_rules(usbdk_api_wrapper *usbdk_api, HANDLE hider_handle)
+{
+    return usbdk_api->ClearRules(hider_handle);
+}
+
+void usbdk_close_hider_handle(usbdk_api_wrapper *usbdk_api, HANDLE hider_handle)
+{
+    return usbdk_api->CloseHiderHandle(hider_handle);
+}
diff --git a/src/usbdk_api.h b/src/usbdk_api.h
new file mode 100644
index 0000000..8e5406c
--- /dev/null
+++ b/src/usbdk_api.h
@@ -0,0 +1,34 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+   Copyright (C) 2014-2015 Red Hat, Inc.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, see <http://www.gnu.org/licenses/>.
+
+  Authors:
+    Dmitry Fleytman <dmitry at daynix.com>
+    Kirill Moizik <kirill at daynix.com>
+*/
+#ifndef USBDK_HEADER
+#define USBDK_HEADER
+
+typedef struct tag_usbdk_api_wrapper usbdk_api_wrapper;
+
+usbdk_api_wrapper *usbdk_api_load(void);
+void     usbdk_api_unload(usbdk_api_wrapper *usbdk_api);
+BOOL     usbdk_is_driver_installed(void);
+HANDLE   usbdk_create_hider_handle(usbdk_api_wrapper *usbdk_api);
+void     usbdk_api_set_hide_rules(usbdk_api_wrapper *usbdk_api, HANDLE hider_handle, gchar *redirect_on_connect);
+BOOL     usbdk_clear_hide_rules(usbdk_api_wrapper *usbdk_api, HANDLE hider_handle);
+void     usbdk_close_hider_handle(usbdk_api_wrapper *usbdk_api, HANDLE hider_handle);
+#endif


More information about the Spice-commits mailing list