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