[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