we will spawn a separate thread to disconnect usb device, when finish we will update widget to allow further usb redirection operations 1) expose spice_usbredir_channel_connect_device_async function for asynchronous disconnect 2) threads synchronization Signed-off-by: Kirill Moizik <kmoizik@xxxxxxxxxx> --- src/channel-usbredir-priv.h | 8 ++++- src/channel-usbredir.c | 79 ++++++++++++++++++++++++++++++++++++++------- src/map-file | 1 + src/usb-device-manager.c | 62 ++++++++++++++++++++++++++++++++++- src/usb-device-manager.h | 8 +++++ src/usb-device-widget.c | 19 +++++++++-- 6 files changed, 161 insertions(+), 16 deletions(-) diff --git a/src/channel-usbredir-priv.h b/src/channel-usbredir-priv.h index 2c4c6f7..c3ff158 100644 --- a/src/channel-usbredir-priv.h +++ b/src/channel-usbredir-priv.h @@ -33,6 +33,10 @@ G_BEGIN_DECLS void spice_usbredir_channel_set_context(SpiceUsbredirChannel *channel, libusb_context *context); +void spice_usbredir_channel_disconnect_device_async(SpiceUsbredirChannel *channel, + GSimpleAsyncResult *simple, + GCancellable *cancellable); + /* 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( @@ -47,7 +51,9 @@ gboolean spice_usbredir_channel_connect_device_finish( GAsyncResult *res, GError **err); -void spice_usbredir_channel_disconnect_device(SpiceUsbredirChannel *channel); +void spice_usbredir_channel_disconnect_device(GSimpleAsyncResult *simple, + GObject *object, + GCancellable *cancellable); libusb_device *spice_usbredir_channel_get_device(SpiceUsbredirChannel *channel); diff --git a/src/channel-usbredir.c b/src/channel-usbredir.c index e7d5949..1e78350 100644 --- a/src/channel-usbredir.c +++ b/src/channel-usbredir.c @@ -79,6 +79,7 @@ struct _SpiceUsbredirChannelPrivate { GSimpleAsyncResult *result; SpiceUsbAclHelper *acl_helper; #endif + void* redirect_mutex; }; static void channel_set_handlers(SpiceChannelClass *klass); @@ -107,24 +108,52 @@ static void spice_usbredir_channel_init(SpiceUsbredirChannel *channel) { #ifdef USE_USBREDIR channel->priv = SPICE_USBREDIR_CHANNEL_GET_PRIVATE(channel); + channel->priv->redirect_mutex = usbredir_alloc_lock(); #endif } #ifdef USE_USBREDIR +typedef struct _reset_cb_data +{ + gboolean migrating; +} reset_cb_data; + +static void spice_usbredir_channel_reset_cb(GObject *gobject, + GAsyncResult *result, + gpointer user_data) +{ + SpiceChannel *spiceChannel = SPICE_CHANNEL(gobject); + SpiceUsbredirChannel *channel = SPICE_USBREDIR_CHANNEL(spiceChannel); + SpiceUsbredirChannelPrivate *priv = channel->priv; + reset_cb_data *data = user_data; + + usbredirhost_close(priv->host); + priv->host = NULL; + /* Call set_context to re-create the host */ + spice_usbredir_channel_set_context(channel, priv->context); + SPICE_CHANNEL_CLASS(spice_usbredir_channel_parent_class)->channel_reset(spiceChannel, data->migrating); + g_object_unref(result); + g_free(data); +} + static void spice_usbredir_channel_reset(SpiceChannel *c, gboolean migrating) { SpiceUsbredirChannel *channel = SPICE_USBREDIR_CHANNEL(c); SpiceUsbredirChannelPrivate *priv = channel->priv; + GSimpleAsyncResult *result ; 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){ + reset_cb_data *data = g_new(reset_cb_data,1); + data->migrating = migrating; + result = g_simple_async_result_new(G_OBJECT(c), + spice_usbredir_channel_reset_cb, data, + spice_usbredir_channel_reset); + spice_usbredir_channel_disconnect_device_async(channel, result, NULL); + } + } 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 @@ -149,7 +178,7 @@ static void spice_usbredir_channel_dispose(GObject *obj) { SpiceUsbredirChannel *channel = SPICE_USBREDIR_CHANNEL(obj); - spice_usbredir_channel_disconnect_device(channel); + spice_usbredir_channel_disconnect_device(NULL, G_OBJECT(channel), NULL); /* Chain up to the parent class */ if (G_OBJECT_CLASS(spice_usbredir_channel_parent_class)->dispose) @@ -182,6 +211,9 @@ static void spice_usbredir_channel_finalize(GObject *obj) if (channel->priv->host) usbredirhost_close(channel->priv->host); +#ifdef USE_USBREDIR + usbredir_free_lock(channel->priv->redirect_mutex); +#endif /* Chain up to the parent class */ if (G_OBJECT_CLASS(spice_usbredir_channel_parent_class)->finalize) @@ -382,6 +414,7 @@ void spice_usbredir_channel_connect_device_async( spice_usbredir_channel_open_device_async, G_PRIORITY_DEFAULT, cancellable); + g_object_unref(result); return; #endif @@ -408,11 +441,29 @@ gboolean spice_usbredir_channel_connect_device_finish( return TRUE; } -G_GNUC_INTERNAL -void spice_usbredir_channel_disconnect_device(SpiceUsbredirChannel *channel) + +void spice_usbredir_channel_disconnect_device_async(SpiceUsbredirChannel *channel, + GSimpleAsyncResult *simple, + GCancellable *cancellable) { - SpiceUsbredirChannelPrivate *priv = channel->priv; + if (channel) { + g_simple_async_result_run_in_thread(simple, + spice_usbredir_channel_disconnect_device, + G_PRIORITY_DEFAULT, + cancellable); + } else { + g_simple_async_result_complete_in_idle(simple); + } + g_object_unref(simple); +} +void spice_usbredir_channel_disconnect_device(GSimpleAsyncResult *simple, + GObject *object, + GCancellable *cancellable) +{ + + SpiceUsbredirChannel *channel = SPICE_USBREDIR_CHANNEL(object); + SpiceUsbredirChannelPrivate *priv = channel->priv; CHANNEL_DEBUG(channel, "disconnecting device from usb channel %p", channel); switch (priv->state) { @@ -440,7 +491,9 @@ void spice_usbredir_channel_disconnect_device(SpiceUsbredirChannel *channel) spice_usb_device_manager_get(session, NULL)); } /* This also closes the libusb handle we passed from open_device */ + usbredir_lock_lock(channel->priv->redirect_mutex); usbredirhost_set_device(priv->host, NULL); + usbredir_unlock_lock(channel->priv->redirect_mutex); libusb_unref_device(priv->device); priv->device = NULL; g_boxed_free(spice_usb_device_get_type(), priv->spice_device); @@ -613,7 +666,7 @@ static gboolean device_error(gpointer user_data) /* Check that the device has not changed before we manage to run */ if (data->spice_device == priv->spice_device) { - spice_usbredir_channel_disconnect_device(channel); + spice_usbredir_channel_disconnect_device(NULL, G_OBJECT(channel), NULL); spice_usb_device_manager_device_error( spice_usb_device_manager_get( spice_channel_get_session(SPICE_CHANNEL(channel)), NULL), @@ -652,7 +705,9 @@ static void usbredir_handle_msg(SpiceChannel *c, SpiceMsgIn *in) priv->read_buf = buf; priv->read_buf_size = size; + usbredir_lock_lock(priv->redirect_mutex); r = usbredirhost_read_guest_data(priv->host); + usbredir_unlock_lock(priv->redirect_mutex); if (r != 0) { SpiceUsbDevice *spice_device = priv->spice_device; gchar *desc; diff --git a/src/map-file b/src/map-file index d0c24a4..d14a18d 100644 --- a/src/map-file +++ b/src/map-file @@ -119,6 +119,7 @@ spice_usb_device_get_libusb_device; spice_usb_device_get_type; spice_g_udev_set_redirecting; spice_g_udev_handle_device_change; +spice_usb_device_manager_disconnect_device_async; spice_usb_device_manager_can_redirect_device; spice_usb_device_manager_connect_device_async; spice_usb_device_manager_connect_device_finish; diff --git a/src/usb-device-manager.c b/src/usb-device-manager.c index 1434f1e..de98d9c 100644 --- a/src/usb-device-manager.c +++ b/src/usb-device-manager.c @@ -1623,6 +1623,66 @@ gboolean spice_usb_device_manager_connect_device_finish( return TRUE; } +typedef struct _disconnect_cb_data +{ + SpiceUsbDeviceManager *self; + GSimpleAsyncResult *result; + SpiceUsbDevice *device; +} disconnect_cb_data; + +#ifdef USE_USBREDIR +static +void spice_usb_device_manager_disconnect_device_async_cb(GObject *gobject, + GAsyncResult *channel_res, + gpointer user_data) +{ + disconnect_cb_data *data = user_data; + GSimpleAsyncResult *result = G_SIMPLE_ASYNC_RESULT(data->result); + +#ifdef G_OS_WIN32 + SpiceUsbDeviceManager *self = SPICE_USB_DEVICE_MANAGER(data->self); + if (self->priv->use_usbclerk) { + spice_usb_device_manager_driver_uninstall_async(self, data->device); + } +#endif + g_simple_async_result_complete(result); + g_object_unref(result); + g_free(data); +} +#endif + +void spice_usb_device_manager_disconnect_device_async(SpiceUsbDeviceManager *self, + SpiceUsbDevice *device, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ +#ifdef USE_USBREDIR + GSimpleAsyncResult *nested; + GSimpleAsyncResult *result; + 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_simple_async_result_new(G_OBJECT(self), callback, user_data, + spice_usb_device_manager_disconnect_device_async); + disconnect_cb_data *data = g_new(disconnect_cb_data,1); + data->self = self; + data->result = nested; + data->device = device; + + result = g_simple_async_result_new(G_OBJECT(channel), + spice_usb_device_manager_disconnect_device_async_cb, data, + spice_usb_device_manager_disconnect_device_async); + + spice_usbredir_channel_disconnect_device_async(channel,result,cancellable); +#endif +} /** * spice_usb_device_manager_disconnect_device: @@ -1644,7 +1704,7 @@ void spice_usb_device_manager_disconnect_device(SpiceUsbDeviceManager *self, channel = spice_usb_device_manager_get_channel_for_dev(self, device); if (channel) - spice_usbredir_channel_disconnect_device(channel); + spice_usbredir_channel_disconnect_device(NULL, G_OBJECT(channel), NULL); #ifdef G_OS_WIN32 if (self->priv->use_usbclerk) { diff --git a/src/usb-device-manager.h b/src/usb-device-manager.h index 5b4cfbe..025262a 100644 --- a/src/usb-device-manager.h +++ b/src/usb-device-manager.h @@ -106,6 +106,14 @@ 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); diff --git a/src/usb-device-widget.c b/src/usb-device-widget.c index a3e89f6..85f3057 100644 --- a/src/usb-device-widget.c +++ b/src/usb-device-widget.c @@ -470,6 +470,17 @@ static void set_redirecting(SpiceUsbDeviceWidget *self, gboolean val) #endif } +static void disconnect_cb(GObject *gobject, GAsyncResult *res, gpointer user_data) +{ + connect_cb_data *data = user_data; + SpiceUsbDeviceWidget *self = data->self; + set_redirecting (self,FALSE); + spice_usb_device_widget_update_status(self); + g_object_unref(data->check); + g_object_unref(data->self); + g_free(data); +} + static void connect_cb(GObject *gobject, GAsyncResult *res, gpointer user_data) { SpiceUsbDeviceManager *manager = SPICE_USB_DEVICE_MANAGER(gobject); @@ -521,8 +532,12 @@ static void checkbox_clicked_cb(GtkWidget *check, gpointer user_data) 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); } -- 2.1.0 _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/spice-devel