[Spice-commits] 22 commits - configure.ac gtk/channel-usbredir.c gtk/controller gtk/Makefile.am gtk/spicy.c gtk/usb-device-manager.c gtk/usb-device-manager-priv.h gtk/win-usb-clerk.h gtk/win-usb-dev.c gtk/win-usb-dev.h gtk/win-usb-driver-install.c gtk/win-usb-driver-install.h

Uri Lublin uril at kemper.freedesktop.org
Tue Jul 10 07:48:43 PDT 2012


 configure.ac                  |   15 +
 gtk/Makefile.am               |   18 +
 gtk/channel-usbredir.c        |    2 
 gtk/controller/test.c         |    4 
 gtk/spicy.c                   |    2 
 gtk/usb-device-manager-priv.h |   11 
 gtk/usb-device-manager.c      |  574 +++++++++++++++++++++++++++++++++++++-----
 gtk/win-usb-clerk.h           |   35 ++
 gtk/win-usb-dev.c             |  532 ++++++++++++++++++++++++++++++++++++++
 gtk/win-usb-dev.h             |  110 ++++++++
 gtk/win-usb-driver-install.c  |  384 ++++++++++++++++++++++++++++
 gtk/win-usb-driver-install.h  |  104 +++++++
 12 files changed, 1725 insertions(+), 66 deletions(-)

New commits:
commit 00cbbf53af886b79a30655726d57f1d9e60f8167
Author: Uri Lublin <uril at redhat.com>
Date:   Thu Jul 5 23:44:08 2012 +0300

    usb-device-manager: mingw: ignore "remove" udev event when un/installing a driver

diff --git a/gtk/usb-device-manager.c b/gtk/usb-device-manager.c
index 5228394..58dde67 100644
--- a/gtk/usb-device-manager.c
+++ b/gtk/usb-device-manager.c
@@ -719,6 +719,16 @@ static void spice_usb_device_manager_remove_dev(SpiceUsbDeviceManager  *self,
         return;
     }
 
+#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 device at %d.%d. It is un/installing it's driver",
+                    bus, address);
+        return;
+    }
+#endif
+
     spice_usb_device_manager_disconnect_device(self, device);
 
     SPICE_DEBUG("device removed %p", device);
commit 62e9488868e96ac69fb97d24dd1b509db2fdaf68
Author: Uri Lublin <uril at redhat.com>
Date:   Thu Jul 5 23:44:07 2012 +0300

    usb-device-manager: mingw: keep driver install/uninstall state of a device
    
    Currently only driver install/unsinstall is of interest, such that
    extra udev events can be ignored.

diff --git a/gtk/usb-device-manager.c b/gtk/usb-device-manager.c
index 2670713..5228394 100644
--- a/gtk/usb-device-manager.c
+++ b/gtk/usb-device-manager.c
@@ -820,6 +820,8 @@ static void spice_usb_device_manager_drv_install_cb(GObject *gobject,
     g_object_unref(installer);
     spice_usb_device_unref(device);
 
+    spice_usb_device_set_state(device, SPICE_USB_DEVICE_STATE_NONE);
+
     if (err) {
         g_warning("win usb driver %s failed -- %s", opstr, err->message);
         g_error_free(err);
@@ -1091,6 +1093,7 @@ void spice_usb_device_manager_connect_device_async(SpiceUsbDeviceManager *self,
     SpiceWinUsbDriver *installer;
     UsbInstallCbInfo *cbinfo;
 
+    spice_usb_device_set_state(device, SPICE_USB_DEVICE_STATE_INSTALLING);
     installer = spice_win_usb_driver_new();
     cbinfo = g_new0(UsbInstallCbInfo, 1);
     cbinfo->manager     = self;
@@ -1156,6 +1159,7 @@ void spice_usb_device_manager_disconnect_device(SpiceUsbDeviceManager *self,
 
     g_warn_if_fail(device != NULL);
 
+    spice_usb_device_set_state(device, SPICE_USB_DEVICE_STATE_UNINSTALLING);
     installer = spice_win_usb_driver_new();
     cbinfo = g_new0(UsbInstallCbInfo, 1);
     cbinfo->manager     = self;
commit fb6eb40ab48b437b9d8e5f884765844b56f39d9c
Author: Uri Lublin <uril at redhat.com>
Date:   Thu Jul 5 23:44:06 2012 +0300

    usb-device-manager: add 'state' field to SpiceUsbDeviceInfo
    
    To be used on Win32 to ignore extra udev events
    received during driver install/uninstall.

diff --git a/gtk/usb-device-manager.c b/gtk/usb-device-manager.c
index e5b02c0..2670713 100644
--- a/gtk/usb-device-manager.c
+++ b/gtk/usb-device-manager.c
@@ -110,6 +110,16 @@ struct _SpiceUsbDeviceManagerPrivate {
     GPtrArray *channels;
 };
 
+enum {
+    SPICE_USB_DEVICE_STATE_NONE = 0, /* this is also DISCONNECTED */
+    SPICE_USB_DEVICE_STATE_CONNECTING,
+    SPICE_USB_DEVICE_STATE_CONNECTED,
+    SPICE_USB_DEVICE_STATE_DISCONNECTING,
+    SPICE_USB_DEVICE_STATE_INSTALLING,
+    SPICE_USB_DEVICE_STATE_UNINSTALLING,
+    SPICE_USB_DEVICE_STATE_MAX
+};
+
 #ifdef USE_USBREDIR
 
 typedef struct _SpiceUsbDeviceInfo {
@@ -117,6 +127,8 @@ typedef struct _SpiceUsbDeviceInfo {
     guint8  devaddr;
     guint16 vid;
     guint16 pid;
+    guint8  state;
+    guint8  reserved;
     gint    ref;
 } SpiceUsbDeviceInfo;
 
@@ -136,6 +148,11 @@ static SpiceUsbDeviceInfo *spice_usb_device_new(libusb_device *libdev);
 static SpiceUsbDevice *spice_usb_device_ref(SpiceUsbDevice *device);
 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);
+#endif
+
 static gboolean spice_usb_device_equal_libdev(SpiceUsbDevice *device,
                                               libusb_device *libdev);
 static SpiceUsbDevice *
@@ -1364,6 +1381,26 @@ guint16 spice_usb_device_get_pid(SpiceUsbDevice *device)
     return info->pid;
 }
 
+#ifdef G_OS_WIN32
+void spice_usb_device_set_state(SpiceUsbDevice *device, guint8 state)
+{
+    SpiceUsbDeviceInfo *info = (SpiceUsbDeviceInfo *)device;
+
+    g_return_if_fail(info != NULL);
+
+    info->state = state;
+}
+
+guint8 spice_usb_device_get_state(SpiceUsbDevice *device)
+{
+    SpiceUsbDeviceInfo *info = (SpiceUsbDeviceInfo *)device;
+
+    g_return_val_if_fail(info != NULL, 0);
+
+    return info->state;
+}
+#endif
+
 static SpiceUsbDevice *spice_usb_device_ref(SpiceUsbDevice *device)
 {
     SpiceUsbDeviceInfo *info = (SpiceUsbDeviceInfo *)device;
commit e26f4bffa41ec73ec7abdbde149117d5649d7ad0
Author: Uri Lublin <uril at redhat.com>
Date:   Thu Jul 5 23:44:02 2012 +0300

    Win32/mingw: win-usb-dev: skip hubs
    
    also skip devices with bad (0) device-address.

diff --git a/gtk/win-usb-dev.c b/gtk/win-usb-dev.c
index bc21e08..c188807 100644
--- a/gtk/win-usb-dev.c
+++ b/gtk/win-usb-dev.c
@@ -94,6 +94,8 @@ static void g_udev_device_print_list(GList *l, const gchar *msg) {}
 #endif
 static void g_udev_device_print(GUdevDevice *udev, const gchar *msg);
 
+static gboolean g_udev_skip_search(GUdevDevice *udev);
+
 GQuark g_udev_client_error_quark(void)
 {
     return g_quark_from_static_string("win-gudev-client-error-quark");
@@ -142,13 +144,17 @@ g_udev_client_list_devices(GUdevClient *self, GList **devs,
         return -4;
     }
 
-    n = rc;
-
+    n = 0;
     for (dev = lusb_list; *dev; dev++) {
         udevinfo = g_new0(GUdevDeviceInfo, 1);
         get_usb_dev_info(*dev, udevinfo);
         udevice = g_udev_device_new(udevinfo);
+        if (g_udev_skip_search(udevice)) {
+            g_object_unref(udevice);
+            continue;
+        }
         *devs = g_list_prepend(*devs, udevice);
+        n++;
     }
     libusb_free_device_list(lusb_list, 1);
 
@@ -508,3 +514,19 @@ static void g_udev_device_print(GUdevDevice *udev, const gchar *msg)
                 udevinfo->bus, udevinfo->addr,
                 udevinfo->vid, udevinfo->pid, udevinfo->class);
 }
+
+static gboolean g_udev_skip_search(GUdevDevice *udev)
+{
+    GUdevDeviceInfo* udevinfo;
+    gboolean skip;
+
+    g_return_val_if_fail(G_UDEV_DEVICE(udev), FALSE);
+
+    udevinfo = udev->priv->udevinfo;
+    g_return_val_if_fail(udevinfo != NULL, FALSE);
+
+    skip = ((udevinfo->addr == 0xff) ||  /* root hub (HCD) */
+            (udevinfo->class == LIBUSB_CLASS_HUB) || /* hub*/
+            (udevinfo->addr == 0)); /* bad address */
+    return skip;
+}
commit 6b806a6e1e31489db78e7ebb8d51ab677ef4a92c
Author: Uri Lublin <uril at redhat.com>
Date:   Sun Jul 8 18:12:39 2012 +0300

    Win32/mingw: usb-device-manager: uninstall win usb driver upon device disconnect

diff --git a/gtk/usb-device-manager.c b/gtk/usb-device-manager.c
index 2f0d6f1..e5b02c0 100644
--- a/gtk/usb-device-manager.c
+++ b/gtk/usb-device-manager.c
@@ -746,6 +746,7 @@ typedef struct _UsbInstallCbInfo {
     GCancellable          *cancellable;
     GAsyncReadyCallback   callback;
     gpointer              user_data;
+    gboolean              is_install;
 } UsbInstallCbInfo;
 
 /**
@@ -774,8 +775,8 @@ static void spice_usb_device_manager_drv_install_cb(GObject *gobject,
     UsbInstallCbInfo *cbinfo;
     GCancellable *cancellable;
     GAsyncReadyCallback callback;
-
-    SPICE_DEBUG("Win USB driver Installation finished");
+    gboolean is_install;
+    const gchar *opstr;
 
     g_return_if_fail(user_data != NULL);
 
@@ -786,6 +787,7 @@ static void spice_usb_device_manager_drv_install_cb(GObject *gobject,
     cancellable = cbinfo->cancellable;
     callback    = cbinfo->callback;
     user_data   = cbinfo->user_data;
+    is_install  = cbinfo->is_install;
 
     g_free(cbinfo);
 
@@ -793,19 +795,25 @@ static void spice_usb_device_manager_drv_install_cb(GObject *gobject,
     g_return_if_fail(SPICE_IS_WIN_USB_DRIVER(installer));
     g_return_if_fail(device!= NULL);
 
+    opstr = is_install ? "install" : "uninstall";
+    SPICE_DEBUG("Win USB driver %s finished", opstr);
+
     status = spice_win_usb_driver_install_finish(installer, res, &err);
 
     g_object_unref(installer);
     spice_usb_device_unref(device);
 
     if (err) {
-        g_warning("win usb driver installation failed -- %s",
-                  err->message);
+        g_warning("win usb driver %s failed -- %s", opstr, err->message);
         g_error_free(err);
     }
 
     if (!status) {
-        g_warning("failed to install win usb driver (status=0)");
+        g_warning("failed to %s win usb driver (status=0)", opstr);
+    }
+
+    if (! is_install) {
+        return;
     }
 
     /* device is already ref'ed */
@@ -1074,6 +1082,8 @@ void spice_usb_device_manager_connect_device_async(SpiceUsbDeviceManager *self,
     cbinfo->cancellable = cancellable;
     cbinfo->callback    = callback;
     cbinfo->user_data   = user_data;
+    cbinfo->is_install  = TRUE;
+
     spice_win_usb_driver_install(installer, device, cancellable,
                                  spice_usb_device_manager_drv_install_cb,
                                  cbinfo);
@@ -1122,6 +1132,28 @@ 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);
+
+#ifdef G_OS_WIN32
+    SpiceWinUsbDriver *installer;
+    UsbInstallCbInfo *cbinfo;
+
+    g_warn_if_fail(device != NULL);
+
+    installer = spice_win_usb_driver_new();
+    cbinfo = g_new0(UsbInstallCbInfo, 1);
+    cbinfo->manager     = self;
+    cbinfo->device      = spice_usb_device_ref(device);
+    cbinfo->installer   = installer;
+    cbinfo->cancellable = NULL;
+    cbinfo->callback    = NULL;
+    cbinfo->user_data   = NULL;
+    cbinfo->is_install  = FALSE;
+
+    spice_win_usb_driver_uninstall(installer, device, NULL,
+                                   spice_usb_device_manager_drv_install_cb,
+                                   cbinfo);
+#endif
+
 #endif
 }
 
commit 147c0d1bb293c3888b34880f7a7adcf7ac622523
Author: Uri Lublin <uril at redhat.com>
Date:   Thu Jul 5 23:44:00 2012 +0300

    win-usb-driver-install: add capability to remove (uninstall) a win usb driver

diff --git a/gtk/win-usb-driver-install.c b/gtk/win-usb-driver-install.c
index 5b9db62..8b326b5 100644
--- a/gtk/win-usb-driver-install.c
+++ b/gtk/win-usb-driver-install.c
@@ -246,29 +246,19 @@ SpiceWinUsbDriver *spice_win_usb_driver_new(void)
     return SPICE_WIN_USB_DRIVER(obj);
 }
 
-/**
- * spice_win_usb_driver_install:
- * Start libusb driver installation for @device
- *
- * A new NamedPipe is created for each request.
- *
- * Returns: TRUE if a request was sent to usbclerk
- *          FALSE upon failure to send a request.
- */
-G_GNUC_INTERNAL
-void spice_win_usb_driver_install(SpiceWinUsbDriver *self,
-                                  SpiceUsbDevice *device,
-                                  GCancellable *cancellable,
-                                  GAsyncReadyCallback callback,
-                                  gpointer user_data)
+static
+void spice_win_usb_driver_op(SpiceWinUsbDriver *self,
+                             SpiceUsbDevice *device,
+                             guint16 op_type,
+                             GCancellable *cancellable,
+                             GAsyncReadyCallback callback,
+                             gpointer user_data)
 {
     guint16 vid, pid;
     GError *err = NULL;
     GSimpleAsyncResult *result;
     SpiceWinUsbDriverPrivate *priv;
 
-    SPICE_DEBUG("Win usb driver installation started");
-
     g_return_if_fail(SPICE_IS_WIN_USB_DRIVER(self));
     g_return_if_fail(device != NULL);
 
@@ -277,7 +267,7 @@ void spice_win_usb_driver_install(SpiceWinUsbDriver *self,
     g_return_if_fail(priv->result == NULL);
 
     result = g_simple_async_result_new(G_OBJECT(self), callback, user_data,
-                                       spice_win_usb_driver_install);
+                                       spice_win_usb_driver_op);
 
     vid = spice_usb_device_get_vid(device);
     pid = spice_usb_device_get_pid(device);
@@ -300,7 +290,7 @@ void spice_win_usb_driver_install(SpiceWinUsbDriver *self,
         goto failed_request;
     }
 
-    if (!spice_win_usb_driver_send_request(self, USB_CLERK_DRIVER_INSTALL,
+    if (!spice_win_usb_driver_send_request(self, op_type,
                                            vid, pid, &err)) {
         g_warning("failed to send a request to usbclerk %s", err->message);
         g_simple_async_result_take_error(result, err);
@@ -323,6 +313,43 @@ void spice_win_usb_driver_install(SpiceWinUsbDriver *self,
 }
 
 
+
+/**
+ * spice_win_usb_driver_install:
+ * Start libusb driver installation for @device
+ *
+ * A new NamedPipe is created for each request.
+ *
+ * Returns: TRUE if a request was sent to usbclerk
+ *          FALSE upon failure to send a request.
+ */
+G_GNUC_INTERNAL
+void spice_win_usb_driver_install(SpiceWinUsbDriver *self,
+                                  SpiceUsbDevice *device,
+                                  GCancellable *cancellable,
+                                  GAsyncReadyCallback callback,
+                                  gpointer user_data)
+{
+    SPICE_DEBUG("Win usb driver installation started");
+
+    spice_win_usb_driver_op(self, device, USB_CLERK_DRIVER_INSTALL, cancellable,
+                            callback, user_data);
+}
+
+G_GNUC_INTERNAL
+void spice_win_usb_driver_uninstall(SpiceWinUsbDriver *self,
+                                    SpiceUsbDevice *device,
+                                    GCancellable *cancellable,
+                                    GAsyncReadyCallback callback,
+                                    gpointer user_data)
+{
+    SPICE_DEBUG("Win usb driver uninstall operation started");
+
+    spice_win_usb_driver_op(self, device, USB_CLERK_DRIVER_REMOVE, cancellable,
+                            callback, user_data);
+}
+
+
 /**
  * Returns: currently returns 0 (failure) and 1 (success)
  * possibly later we'll add error-codes
@@ -335,7 +362,7 @@ gint spice_win_usb_driver_install_finish(SpiceWinUsbDriver *self,
 
     g_return_val_if_fail(SPICE_IS_WIN_USB_DRIVER(self), 0);
     g_return_val_if_fail(g_simple_async_result_is_valid(res, G_OBJECT(self),
-                                                        spice_win_usb_driver_install),
+                                                        spice_win_usb_driver_op),
                          FALSE);
     if (g_simple_async_result_propagate_error(result, err))
         return 0;
diff --git a/gtk/win-usb-driver-install.h b/gtk/win-usb-driver-install.h
index b0ccf33..034abf9 100644
--- a/gtk/win-usb-driver-install.h
+++ b/gtk/win-usb-driver-install.h
@@ -70,6 +70,12 @@ void spice_win_usb_driver_install(SpiceWinUsbDriver *self,
                                   GAsyncReadyCallback callback,
                                   gpointer user_data);
 
+void spice_win_usb_driver_uninstall(SpiceWinUsbDriver *self,
+                                    SpiceUsbDevice *device,
+                                    GCancellable *cancellable,
+                                    GAsyncReadyCallback callback,
+                                    gpointer user_data);
+
 gint spice_win_usb_driver_install_finish(SpiceWinUsbDriver *self,
                                          GAsyncResult *res, GError **err);
 
commit 714def3df0dc5547c459dcdcae5f8aa3232999ff
Author: Uri Lublin <uril at redhat.com>
Date:   Thu Jul 5 23:43:58 2012 +0300

    Windows mingw: usb: Dynamically install a libusb driver for USB devices
    
    - Added win-usb-driver-install.[ch]
    - Added win-usb-clerk.h
    
    Operation (on Windows, spice-gtk point of view):
    - After some sanity checks, just before redir'ing a USB device
      a libusb driver needs to be installed (before libusb can open the device)
    - A connection (NamedPipe) is established with usb-clerk, a libusb
      driver installation service, and a request for driver installation
      is sent.
    - Installation status is asynchronously read from the pipe, and
      spice_usb_drv_install_finished() is called.
    - Upon a successful intallation, usbredir continues.
    
    Linux operation is not changed.

diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index 8edea9a..fcfc086 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -316,6 +316,9 @@ endif
 WIN_USB_FILES= \
 	win-usb-dev.h			\
 	win-usb-dev.c			\
+	win-usb-clerk.h			\
+	win-usb-driver-install.h	\
+	win-usb-driver-install.c	\
 	$(NULL)
 
 if OS_WIN32
diff --git a/gtk/usb-device-manager.c b/gtk/usb-device-manager.c
index c183158..2f0d6f1 100644
--- a/gtk/usb-device-manager.c
+++ b/gtk/usb-device-manager.c
@@ -33,6 +33,7 @@
 #include <gudev/gudev.h>
 #elif defined(G_OS_WIN32)
 #include "win-usb-dev.h"
+#include "win-usb-driver-install.h"
 #else
 #warning "Expecting one of G_OS_WIN32 and USE_GUDEV to be defined"
 #endif
@@ -144,6 +145,13 @@ static libusb_device *
 spice_usb_device_manager_device_to_libdev(SpiceUsbDeviceManager *self,
                                           SpiceUsbDevice *device);
 
+static void
+_spice_usb_device_manager_connect_device_async(SpiceUsbDeviceManager *self,
+                                               SpiceUsbDevice *device,
+                                               GCancellable *cancellable,
+                                               GAsyncReadyCallback callback,
+                                               gpointer user_data);
+
 G_DEFINE_BOXED_TYPE(SpiceUsbDevice, spice_usb_device,
                     (GBoxedCopyFunc)spice_usb_device_ref,
                     (GBoxedFreeFunc)spice_usb_device_unref)
@@ -154,6 +162,12 @@ G_DEFINE_BOXED_TYPE(SpiceUsbDevice, spice_usb_device, g_object_ref, g_object_unr
 
 static void spice_usb_device_manager_initable_iface_init(GInitableIface *iface);
 
+#ifdef G_OS_WIN32
+static void spice_usb_device_manager_drv_install_cb(GObject *gobject,
+                                                    GAsyncResult *res,
+                                                    gpointer user_data);
+#endif
+
 static guint signals[LAST_SIGNAL] = { 0, };
 
 G_DEFINE_TYPE_WITH_CODE(SpiceUsbDeviceManager, spice_usb_device_manager, G_TYPE_OBJECT,
@@ -723,6 +737,87 @@ static void spice_usb_device_manager_channel_connect_cb(
     g_object_unref(result);
 }
 
+#ifdef G_OS_WIN32
+
+typedef struct _UsbInstallCbInfo {
+    SpiceUsbDeviceManager *manager;
+    SpiceUsbDevice        *device;
+    SpiceWinUsbDriver     *installer;
+    GCancellable          *cancellable;
+    GAsyncReadyCallback   callback;
+    gpointer              user_data;
+} UsbInstallCbInfo;
+
+/**
+ * spice_usb_device_manager_drv_install_cb:
+ * @gobject: #SpiceWinUsbDriver in charge of installing the driver
+ * @res: #GAsyncResult of async win usb driver installation
+ * @user_data: #SpiceUsbDeviceManager requested the installation
+ *
+ * Called when an Windows libusb driver installation completed.
+ *
+ * If the driver installation was successful, continue with USB
+ * device redirection
+ *
+ * Always call _spice_usb_device_manager_connect_device_async.
+ * When installation fails, libusb_open fails too, but cleanup would be better.
+ */
+static void spice_usb_device_manager_drv_install_cb(GObject *gobject,
+                                                    GAsyncResult *res,
+                                                    gpointer user_data)
+{
+    SpiceUsbDeviceManager *self;
+    SpiceWinUsbDriver *installer;
+    gint status;
+    GError *err = NULL;
+    SpiceUsbDevice *device;
+    UsbInstallCbInfo *cbinfo;
+    GCancellable *cancellable;
+    GAsyncReadyCallback callback;
+
+    SPICE_DEBUG("Win USB driver Installation finished");
+
+    g_return_if_fail(user_data != NULL);
+
+    cbinfo = user_data;
+    self        = cbinfo->manager;
+    device      = cbinfo->device;
+    installer   = cbinfo->installer;
+    cancellable = cbinfo->cancellable;
+    callback    = cbinfo->callback;
+    user_data   = cbinfo->user_data;
+
+    g_free(cbinfo);
+
+    g_return_if_fail(SPICE_IS_USB_DEVICE_MANAGER(self));
+    g_return_if_fail(SPICE_IS_WIN_USB_DRIVER(installer));
+    g_return_if_fail(device!= NULL);
+
+    status = spice_win_usb_driver_install_finish(installer, res, &err);
+
+    g_object_unref(installer);
+    spice_usb_device_unref(device);
+
+    if (err) {
+        g_warning("win usb driver installation failed -- %s",
+                  err->message);
+        g_error_free(err);
+    }
+
+    if (!status) {
+        g_warning("failed to install win usb driver (status=0)");
+    }
+
+    /* device is already ref'ed */
+    _spice_usb_device_manager_connect_device_async(self,
+                                                   device,
+                                                   cancellable,
+                                                   callback,
+                                                   user_data);
+
+}
+#endif
+
 /* ------------------------------------------------------------------ */
 /* private api                                                        */
 
@@ -903,11 +998,12 @@ gboolean spice_usb_device_manager_is_device_connected(SpiceUsbDeviceManager *sel
  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
  * @user_data: data to pass to callback
  */
-void spice_usb_device_manager_connect_device_async(SpiceUsbDeviceManager *self,
-                                             SpiceUsbDevice *device,
-                                             GCancellable *cancellable,
-                                             GAsyncReadyCallback callback,
-                                             gpointer user_data)
+static void
+_spice_usb_device_manager_connect_device_async(SpiceUsbDeviceManager *self,
+                                               SpiceUsbDevice *device,
+                                               GCancellable *cancellable,
+                                               GAsyncReadyCallback callback,
+                                               gpointer user_data)
 {
     GSimpleAsyncResult *result;
 
@@ -958,6 +1054,38 @@ done:
     g_object_unref(result);
 }
 
+
+void spice_usb_device_manager_connect_device_async(SpiceUsbDeviceManager *self,
+                                             SpiceUsbDevice *device,
+                                             GCancellable *cancellable,
+                                             GAsyncReadyCallback callback,
+                                             gpointer user_data)
+{
+
+#if defined(USE_USBREDIR) && defined(G_OS_WIN32)
+    SpiceWinUsbDriver *installer;
+    UsbInstallCbInfo *cbinfo;
+
+    installer = spice_win_usb_driver_new();
+    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(installer, device, cancellable,
+                                 spice_usb_device_manager_drv_install_cb,
+                                 cbinfo);
+#else
+    _spice_usb_device_manager_connect_device_async(self,
+                                                   device,
+                                                   cancellable,
+                                                   callback,
+                                                   user_data);
+#endif
+}
+
 gboolean spice_usb_device_manager_connect_device_finish(
     SpiceUsbDeviceManager *self, GAsyncResult *res, GError **err)
 {
diff --git a/gtk/win-usb-clerk.h b/gtk/win-usb-clerk.h
new file mode 100644
index 0000000..5b1e3cf
--- /dev/null
+++ b/gtk/win-usb-clerk.h
@@ -0,0 +1,35 @@
+#ifndef _H_USBCLERK
+#define _H_USBCLERK
+
+#include <windows.h>
+
+#define USB_CLERK_PIPE_NAME     TEXT("\\\\.\\pipe\\usbclerkpipe")
+#define USB_CLERK_MAGIC         0xDADA
+#define USB_CLERK_VERSION       0x0002
+
+typedef struct USBClerkHeader {
+    UINT16 magic;
+    UINT16 version;
+    UINT16 type;
+    UINT16 size;
+} USBClerkHeader;
+
+enum {
+    USB_CLERK_DRIVER_INSTALL = 1,
+    USB_CLERK_DRIVER_REMOVE,
+    USB_CLERK_REPLY,
+    USB_CLERK_END_MESSAGE,
+};
+
+typedef struct USBClerkDriverOp {
+    USBClerkHeader hdr;
+    UINT16 vid;
+    UINT16 pid;
+} USBClerkDriverOp;
+
+typedef struct USBClerkReply {
+    USBClerkHeader hdr;
+    UINT32 status;
+} USBClerkReply;
+
+#endif
diff --git a/gtk/win-usb-driver-install.c b/gtk/win-usb-driver-install.c
new file mode 100644
index 0000000..5b9db62
--- /dev/null
+++ b/gtk/win-usb-driver-install.c
@@ -0,0 +1,357 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+   Copyright (C) 2011 Red Hat, Inc.
+
+   Red Hat Authors:
+   Uri Lublin <uril at redhat.com>
+
+   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/>.
+*/
+
+/*
+ * Some notes:
+ * Each installer (instance) opens a named-pipe to talk with win-usb-clerk.
+ * Each installer (instance) requests driver installation for a single device.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <windows.h>
+#include <gio/gio.h>
+#include <gio/gwin32inputstream.h>
+#include <gio/gwin32outputstream.h>
+#include "spice-util.h"
+#include "win-usb-clerk.h"
+#include "win-usb-driver-install.h"
+#include "usb-device-manager-priv.h"
+
+/* ------------------------------------------------------------------ */
+/* gobject glue                                                       */
+
+#define SPICE_WIN_USB_DRIVER_GET_PRIVATE(obj)     \
+    (G_TYPE_INSTANCE_GET_PRIVATE ((obj), SPICE_TYPE_WIN_USB_DRIVER, SpiceWinUsbDriverPrivate))
+
+struct _SpiceWinUsbDriverPrivate {
+    USBClerkReply         reply;
+    GSimpleAsyncResult    *result;
+    GCancellable          *cancellable;
+    HANDLE                handle;
+    SpiceUsbDevice        *device;
+};
+
+
+
+G_DEFINE_TYPE(SpiceWinUsbDriver, spice_win_usb_driver, G_TYPE_OBJECT);
+
+static void spice_win_usb_driver_init(SpiceWinUsbDriver *self)
+{
+    self->priv = SPICE_WIN_USB_DRIVER_GET_PRIVATE(self);
+}
+
+static void spice_win_usb_driver_close(SpiceWinUsbDriver *self)
+{
+    if (self->priv->handle) {
+        CloseHandle(self->priv->handle);
+        self->priv->handle = 0;
+    }
+}
+
+static void spice_win_usb_driver_finalize(GObject *gobject)
+{
+    SpiceWinUsbDriver *self = SPICE_WIN_USB_DRIVER(gobject);
+    SpiceWinUsbDriverPrivate *priv = self->priv;
+
+    spice_win_usb_driver_close(self);
+    g_clear_object(&priv->result);
+}
+
+static void spice_win_usb_driver_class_init(SpiceWinUsbDriverClass *klass)
+{
+    GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+    gobject_class->finalize     = spice_win_usb_driver_finalize;
+
+    g_type_class_add_private(klass, sizeof(SpiceWinUsbDriverPrivate));
+}
+
+/* ------------------------------------------------------------------ */
+/* callbacks                                                          */
+
+void win_usb_driver_handle_reply_cb(GObject *gobject,
+                                    GAsyncResult *read_res,
+                                    gpointer user_data)
+{
+    SpiceWinUsbDriver *self;
+    SpiceWinUsbDriverPrivate *priv;
+
+    GInputStream *istream;
+    GError *err = NULL;
+    gssize bytes;
+
+    g_return_if_fail(SPICE_IS_WIN_USB_DRIVER(user_data));
+    self = SPICE_WIN_USB_DRIVER(user_data);
+    priv = self->priv;
+    istream = G_INPUT_STREAM(gobject);
+
+    bytes = g_input_stream_read_finish(istream, read_res, &err);
+
+    SPICE_DEBUG("Finished reading reply-msg from usbclerk: bytes=%ld "
+                "err_exist?=%d", (long)bytes, err!=NULL);
+
+    g_warn_if_fail(g_input_stream_close(istream, NULL, NULL));
+    g_clear_object(&istream);
+    spice_win_usb_driver_close(self);
+
+    if (err) {
+        g_warning("failed to read reply from usbclerk (%s)", err->message);
+        g_simple_async_result_take_error(priv->result, err);
+        goto failed_reply;
+    }
+
+    if (bytes == 0) {
+        g_warning("unexpected EOF from usbclerk");
+        g_simple_async_result_set_error(priv->result,
+                                        SPICE_WIN_USB_DRIVER_ERROR,
+                                        SPICE_WIN_USB_DRIVER_ERROR_FAILED,
+                                        "unexpected EOF from usbclerk");
+        goto failed_reply;
+    }
+
+    if (bytes != sizeof(priv->reply)) {
+        g_warning("usbclerk size mismatch: read %d bytes, expected %d (header %d, size in header %d)",
+                  bytes, sizeof(priv->reply), sizeof(priv->reply.hdr), priv->reply.hdr.size);
+        /* For now just warn, do not fail */
+    }
+
+    if (priv->reply.hdr.magic != USB_CLERK_MAGIC) {
+        g_warning("usbclerk magic mismatch: mine=0x%04x  server=0x%04x",
+                  USB_CLERK_MAGIC, priv->reply.hdr.magic);
+        g_simple_async_result_set_error(priv->result,
+                                        SPICE_WIN_USB_DRIVER_ERROR,
+                                        SPICE_WIN_USB_DRIVER_ERROR_MESSAGE,
+                                        "usbclerk magic mismatch");
+        goto failed_reply;
+    }
+
+    if (priv->reply.hdr.version != USB_CLERK_VERSION) {
+        g_warning("usbclerk version mismatch: mine=0x%04x  server=0x%04x",
+                  USB_CLERK_VERSION, priv->reply.hdr.version);
+        /* For now just warn, do not fail */
+    }
+
+    if (priv->reply.hdr.type != USB_CLERK_REPLY) {
+        g_warning("usbclerk message with unexpected type %d",
+                  priv->reply.hdr.type);
+        g_simple_async_result_set_error(priv->result,
+                                        SPICE_WIN_USB_DRIVER_ERROR,
+                                        SPICE_WIN_USB_DRIVER_ERROR_MESSAGE,
+                                        "usbclerk message with unexpected type");
+        goto failed_reply;
+    }
+
+    if (priv->reply.hdr.size != bytes) {
+        g_warning("usbclerk message size mismatch: read %d bytes  hdr.size=%d",
+                  bytes, priv->reply.hdr.size);
+        g_simple_async_result_set_error(priv->result,
+                                        SPICE_WIN_USB_DRIVER_ERROR,
+                                        SPICE_WIN_USB_DRIVER_ERROR_MESSAGE,
+                                        "usbclerk message with unexpected size");
+        goto failed_reply;
+    }
+
+ failed_reply:
+    g_simple_async_result_complete_in_idle(priv->result);
+    g_clear_object(&priv->result);
+}
+
+/* ------------------------------------------------------------------ */
+/* helper functions                                                   */
+
+static
+gboolean spice_win_usb_driver_send_request(SpiceWinUsbDriver *self, guint16 op,
+                                           guint16 vid, guint16 pid, GError **err)
+{
+    USBClerkDriverOp req = {0,};
+    GOutputStream *ostream;
+    SpiceWinUsbDriverPrivate *priv;
+    gsize bytes;
+    gboolean ret;
+
+    SPICE_DEBUG("sending a request to usbclerk service (op=%d vid=0x%04x pid=0x%04x",
+                op, vid, pid);
+
+    g_return_val_if_fail(SPICE_IS_WIN_USB_DRIVER(self), FALSE);
+    priv = self->priv;
+
+    req.hdr.magic   = USB_CLERK_MAGIC;
+    req.hdr.version = USB_CLERK_VERSION;
+    req.hdr.type    = op;
+    req.hdr.size    = sizeof(req);
+    req.vid = vid;
+    req.pid = pid;
+
+    ostream = g_win32_output_stream_new(priv->handle, FALSE);
+
+    ret = g_output_stream_write_all(ostream, &req, sizeof(req), &bytes, NULL, err);
+    g_warn_if_fail(g_output_stream_close(ostream, NULL, NULL));
+    g_object_unref(ostream);
+    SPICE_DEBUG("write_all request returned %d written bytes %u expecting %u",
+                ret, bytes, sizeof(req));
+    return ret;
+}
+
+static
+void spice_win_usb_driver_read_reply_async(SpiceWinUsbDriver *self)
+{
+    SpiceWinUsbDriverPrivate *priv;
+    GInputStream  *istream;
+
+    g_return_if_fail(SPICE_IS_WIN_USB_DRIVER(self));
+    priv = self->priv;
+
+    SPICE_DEBUG("waiting for a reply from usbclerk");
+
+    istream = g_win32_input_stream_new(priv->handle, FALSE);
+
+    g_input_stream_read_async(istream, &priv->reply, sizeof(priv->reply),
+                              G_PRIORITY_DEFAULT, priv->cancellable,
+                              win_usb_driver_handle_reply_cb, self);
+}
+
+
+/* ------------------------------------------------------------------ */
+/* private api                                                        */
+
+
+G_GNUC_INTERNAL
+SpiceWinUsbDriver *spice_win_usb_driver_new(void)
+{
+    GObject *obj;
+
+    obj = g_object_new(SPICE_TYPE_WIN_USB_DRIVER, NULL);
+
+    return SPICE_WIN_USB_DRIVER(obj);
+}
+
+/**
+ * spice_win_usb_driver_install:
+ * Start libusb driver installation for @device
+ *
+ * A new NamedPipe is created for each request.
+ *
+ * Returns: TRUE if a request was sent to usbclerk
+ *          FALSE upon failure to send a request.
+ */
+G_GNUC_INTERNAL
+void spice_win_usb_driver_install(SpiceWinUsbDriver *self,
+                                  SpiceUsbDevice *device,
+                                  GCancellable *cancellable,
+                                  GAsyncReadyCallback callback,
+                                  gpointer user_data)
+{
+    guint16 vid, pid;
+    GError *err = NULL;
+    GSimpleAsyncResult *result;
+    SpiceWinUsbDriverPrivate *priv;
+
+    SPICE_DEBUG("Win usb driver installation started");
+
+    g_return_if_fail(SPICE_IS_WIN_USB_DRIVER(self));
+    g_return_if_fail(device != NULL);
+
+    priv = self->priv;
+
+    g_return_if_fail(priv->result == NULL);
+
+    result = g_simple_async_result_new(G_OBJECT(self), callback, user_data,
+                                       spice_win_usb_driver_install);
+
+    vid = spice_usb_device_get_vid(device);
+    pid = spice_usb_device_get_pid(device);
+
+    SPICE_DEBUG("win-usb-driver-install: connecting to usbclerk named pipe");
+    priv->handle = CreateFile(USB_CLERK_PIPE_NAME,
+                              GENERIC_READ | GENERIC_WRITE,
+                              0, NULL,
+                              OPEN_EXISTING,
+                              FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
+                              NULL);
+    if (priv->handle == INVALID_HANDLE_VALUE) {
+        DWORD errval  = GetLastError();
+        gchar *errstr = g_win32_error_message(errval);
+        g_warning("failed to create a named pipe to usbclerk (%ld) %s",
+                  errval,errstr);
+        g_simple_async_result_set_error(result,
+                  G_IO_ERROR, G_IO_ERROR_FAILED,
+                  "Failed to create named pipe (%ld) %s", errval, errstr);
+        goto failed_request;
+    }
+
+    if (!spice_win_usb_driver_send_request(self, USB_CLERK_DRIVER_INSTALL,
+                                           vid, pid, &err)) {
+        g_warning("failed to send a request to usbclerk %s", err->message);
+        g_simple_async_result_take_error(result, err);
+        goto failed_request;
+    }
+
+    /* set up for async read */
+    priv->result = result;
+    priv->device = device;
+    priv->cancellable = cancellable;
+
+    spice_win_usb_driver_read_reply_async(self);
+
+    return;
+
+ failed_request:
+    spice_win_usb_driver_close(self);
+    g_simple_async_result_complete_in_idle(result);
+    g_clear_object(&result);
+}
+
+
+/**
+ * Returns: currently returns 0 (failure) and 1 (success)
+ * possibly later we'll add error-codes
+ */
+G_GNUC_INTERNAL
+gint spice_win_usb_driver_install_finish(SpiceWinUsbDriver *self,
+                                          GAsyncResult *res, GError **err)
+{
+    GSimpleAsyncResult *result = G_SIMPLE_ASYNC_RESULT(res);
+
+    g_return_val_if_fail(SPICE_IS_WIN_USB_DRIVER(self), 0);
+    g_return_val_if_fail(g_simple_async_result_is_valid(res, G_OBJECT(self),
+                                                        spice_win_usb_driver_install),
+                         FALSE);
+    if (g_simple_async_result_propagate_error(result, err))
+        return 0;
+
+    return self->priv->reply.status;
+}
+
+G_GNUC_INTERNAL
+SpiceUsbDevice *spice_win_usb_driver_get_device(SpiceWinUsbDriver *self)
+{
+    g_return_val_if_fail(SPICE_IS_WIN_USB_DRIVER(self), 0);
+
+    return self->priv->device;
+}
+
+GQuark spice_win_usb_driver_error_quark(void)
+{
+    return g_quark_from_static_string("spice-win-usb-driver-error-quark");
+}
diff --git a/gtk/win-usb-driver-install.h b/gtk/win-usb-driver-install.h
new file mode 100644
index 0000000..b0ccf33
--- /dev/null
+++ b/gtk/win-usb-driver-install.h
@@ -0,0 +1,98 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+   Copyright (C) 2011 Red Hat, Inc.
+
+   Red Hat Authors:
+   Uri Lublin <uril at redhat.com>
+
+   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/>.
+*/
+
+#ifndef SPICE_WIN_USB_DRIVER_H
+#define SPICE_WIN_USB_DRIVER_H
+
+#include "usb-device-manager.h"
+
+G_BEGIN_DECLS
+
+GQuark win_usb_driver_error_quark(void);
+
+
+#define SPICE_TYPE_WIN_USB_DRIVER      (spice_win_usb_driver_get_type ())
+#define SPICE_WIN_USB_DRIVER(obj)      (G_TYPE_CHECK_INSTANCE_CAST ((obj),    \
+            SPICE_TYPE_WIN_USB_DRIVER, SpiceWinUsbDriver))
+#define SPICE_IS_WIN_USB_DRIVER(obj)   (G_TYPE_CHECK_INSTANCE_TYPE ((obj),    \
+            SPICE_TYPE_WIN_USB_DRIVER))
+#define SPICE_WIN_USB_DRIVER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass),  \
+            SPICE_TYPE_WIN_USB_DRIVER, SpiceWinUsbDriverClass))
+#define SPICE_IS_WIN_USB_DRIVER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),\
+            SPICE_TYPE_WIN_USB_DRIVER))
+#define SPICE_WIN_USB_DRIVER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),\
+            SPICE_TYPE_WIN_USB_DRIVER, SpiceWinUsbDriverClass))
+
+typedef struct _SpiceWinUsbDriver          SpiceWinUsbDriver;
+typedef struct _SpiceWinUsbDriverClass     SpiceWinUsbDriverClass;
+typedef struct _SpiceWinUsbDriverPrivate   SpiceWinUsbDriverPrivate;
+
+struct _SpiceWinUsbDriver
+{
+    GObject parent;
+
+    /*< private >*/
+    SpiceWinUsbDriverPrivate *priv;
+    /* Do not add fields to this struct */
+};
+
+struct _SpiceWinUsbDriverClass
+{
+    GObjectClass parent_class;
+};
+
+GType spice_win_usb_driver_get_type(void);
+
+SpiceWinUsbDriver *spice_win_usb_driver_new(void);
+
+
+void spice_win_usb_driver_install(SpiceWinUsbDriver *self,
+                                  SpiceUsbDevice *device,
+                                  GCancellable *cancellable,
+                                  GAsyncReadyCallback callback,
+                                  gpointer user_data);
+
+gint spice_win_usb_driver_install_finish(SpiceWinUsbDriver *self,
+                                         GAsyncResult *res, GError **err);
+
+
+SpiceUsbDevice *spice_win_usb_driver_get_device(SpiceWinUsbDriver *self);
+
+#define SPICE_WIN_USB_DRIVER_ERROR spice_win_usb_driver_error_quark()
+
+/**
+ * SpiceWinUsbDriverError:
+ * @SPICE_WIN_USB_DRIVER_ERROR_FAILED: generic error code
+ * @SPICE_WIN_USB_DRIVER_ERROR_MESSAGE: bad message read from clerk
+ *
+ * Error codes returned by spice-client API.
+ */
+typedef enum
+{
+    SPICE_WIN_USB_DRIVER_ERROR_FAILED,
+    SPICE_WIN_USB_DRIVER_ERROR_MESSAGE,
+} SpiceWinUsbDriverError;
+
+GQuark spice_win_usb_driver_error_quark(void);
+
+G_END_DECLS
+
+#endif /* SPICE_WIN_USB_DRIVER_H */
commit 5525ce973402704cfa48b275516509448210dcc0
Author: Arnon Gilboa <agilboa at redhat.com>
Date:   Thu Jul 5 23:43:57 2012 +0300

    Windows mingw: usb: implement GUdevDevice & GUdevClient for windows
    
    - Added win-usb-dev.[ch]
    - Added GUdevDevice and GUdevClient like classes
    - Added uevent signal based on WM_DEVICECHANGE

diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index 10fa9cd..8edea9a 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -312,6 +312,19 @@ libspice_client_glib_2_0_la_SOURCES += coroutine_gthread.c
 libspice_client_glib_2_0_la_LIBADD += $(GTHREAD_LIBS)
 endif
 
+
+WIN_USB_FILES= \
+	win-usb-dev.h			\
+	win-usb-dev.c			\
+	$(NULL)
+
+if OS_WIN32
+if WITH_USBREDIR
+libspice_client_glib_2_0_la_SOURCES += \
+	$(WIN_USB_FILES)
+endif
+endif
+
 displaysrc =					\
 	glib-compat.h				\
 	display/edid.h				\
diff --git a/gtk/usb-device-manager.c b/gtk/usb-device-manager.c
index a20c414..c183158 100644
--- a/gtk/usb-device-manager.c
+++ b/gtk/usb-device-manager.c
@@ -28,7 +28,15 @@
 #ifdef USE_USBREDIR
 #include <errno.h>
 #include <libusb.h>
+
+#if defined(USE_GUDEV)
 #include <gudev/gudev.h>
+#elif defined(G_OS_WIN32)
+#include "win-usb-dev.h"
+#else
+#warning "Expecting one of G_OS_WIN32 and USE_GUDEV to be defined"
+#endif
+
 #include "channel-usbredir-priv.h"
 #include "usbredirhost.h"
 #include "usbutil.h"
diff --git a/gtk/win-usb-dev.c b/gtk/win-usb-dev.c
new file mode 100644
index 0000000..bc21e08
--- /dev/null
+++ b/gtk/win-usb-dev.c
@@ -0,0 +1,510 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+   Copyright (C) 2012 Red Hat, Inc.
+
+   Red Hat Authors:
+   Arnon Gilboa <agilboa at redhat.com>
+   Uri Lublin   <uril at redhat.com>
+
+   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/>.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <windows.h>
+#include <libusb.h>
+#include "win-usb-dev.h"
+#include "spice-marshal.h"
+#include "spice-util.h"
+#include "usbutil.h"
+
+#define G_UDEV_CLIENT_GET_PRIVATE(obj) \
+    (G_TYPE_INSTANCE_GET_PRIVATE((obj), G_UDEV_TYPE_CLIENT, GUdevClientPrivate))
+
+struct _GUdevClientPrivate {
+    libusb_context *ctx;
+    gssize udev_list_size;
+    GList *udev_list;
+    HWND hwnd;
+};
+
+#define G_UDEV_CLIENT_WINCLASS_NAME  TEXT("G_UDEV_CLIENT")
+
+static void g_udev_client_initable_iface_init(GInitableIface  *iface);
+
+G_DEFINE_TYPE_WITH_CODE(GUdevClient, g_udev_client, G_TYPE_OBJECT,
+                        G_IMPLEMENT_INTERFACE(G_TYPE_INITABLE, g_udev_client_initable_iface_init));
+
+
+typedef struct _GUdevDeviceInfo GUdevDeviceInfo;
+
+struct _GUdevDeviceInfo {
+    guint16 bus;
+    guint16 addr;
+    guint16 vid;
+    guint16 pid;
+    guint16 class;
+    gchar sclass[4];
+    gchar sbus[4];
+    gchar saddr[4];
+};
+
+struct _GUdevDevicePrivate
+{
+    /* FixMe: move above fields to this structure and access them directly */
+    GUdevDeviceInfo *udevinfo;
+};
+
+G_DEFINE_TYPE(GUdevDevice, g_udev_device, G_TYPE_OBJECT)
+
+
+enum
+{
+    UEVENT_SIGNAL,
+    LAST_SIGNAL,
+};
+
+static guint signals[LAST_SIGNAL] = { 0, };
+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);
+
+//uncomment to debug gudev device lists.
+//#define DEBUG_GUDEV_DEVICE_LISTS
+
+#ifdef DEBUG_GUDEV_DEVICE_LISTS
+static void g_udev_device_print_list(GList *l, const gchar *msg);
+#else
+static void g_udev_device_print_list(GList *l, const gchar *msg) {}
+#endif
+static void g_udev_device_print(GUdevDevice *udev, const gchar *msg);
+
+GQuark g_udev_client_error_quark(void)
+{
+    return g_quark_from_static_string("win-gudev-client-error-quark");
+}
+
+GUdevClient *g_udev_client_new(const gchar* const *subsystems)
+{
+    if (!singleton) {
+        singleton = g_initable_new(G_UDEV_TYPE_CLIENT, NULL, NULL, NULL);
+        return singleton;
+    } else {
+        return g_object_ref(singleton);
+    }
+}
+
+
+/*
+ * devs [in,out] an empty devs list in, full devs list out
+ * Returns: number-of-devices, or a negative value on error.
+ */
+static ssize_t
+g_udev_client_list_devices(GUdevClient *self, GList **devs,
+                           GError **err, const gchar *name)
+{
+    gssize rc;
+    libusb_device **lusb_list, **dev;
+    GUdevClientPrivate *priv;
+    GUdevDeviceInfo *udevinfo;
+    GUdevDevice *udevice;
+    ssize_t n;
+
+    g_return_val_if_fail(G_UDEV_IS_CLIENT(self), -1);
+    g_return_val_if_fail(devs != NULL, -2);
+
+    priv = self->priv;
+
+    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", name);
+        g_set_error(err, G_UDEV_CLIENT_ERROR, G_UDEV_CLIENT_LIBUSB_FAILED,
+                    "%s: Error getting device list from libusb: %s [%i]",
+                    name, errstr, rc);
+        return -4;
+    }
+
+    n = rc;
+
+    for (dev = lusb_list; *dev; dev++) {
+        udevinfo = g_new0(GUdevDeviceInfo, 1);
+        get_usb_dev_info(*dev, udevinfo);
+        udevice = g_udev_device_new(udevinfo);
+        *devs = g_list_prepend(*devs, udevice);
+    }
+    libusb_free_device_list(lusb_list, 1);
+
+    return n;
+}
+
+static void g_udev_client_free_device_list(GList **devs)
+{
+    g_return_if_fail(devs != NULL);
+    if (*devs) {
+        g_list_free_full(*devs, g_object_unref);
+        *devs = NULL;
+    }
+}
+
+
+static gboolean
+g_udev_client_initable_init(GInitable *initable, GCancellable *cancellable,
+                            GError **err)
+{
+    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);
+
+    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);
+        return FALSE;
+    }
+
+    /* 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) {
+        goto g_udev_client_init_failed;
+    }
+
+    g_udev_device_print_list(priv->udev_list, "init: first list is: ");
+
+    /* create a hidden window */
+    memset(&wcls, 0, sizeof(wcls));
+    wcls.lpfnWndProc = wnd_proc;
+    wcls.lpszClassName = G_UDEV_CLIENT_WINCLASS_NAME;
+    if (!RegisterClass(&wcls)) {
+        DWORD e = GetLastError();
+        g_warning("RegisterClass failed , %ld", (long)e);
+        g_set_error(err, G_UDEV_CLIENT_ERROR, G_UDEV_CLIENT_WINAPI_FAILED,
+                    "RegisterClass failed: %ld", (long)e);
+        goto g_udev_client_init_failed;
+    }
+    priv->hwnd = CreateWindow(G_UDEV_CLIENT_WINCLASS_NAME,
+                              NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL);
+    if (!priv->hwnd) {
+        DWORD e = GetLastError();
+        g_warning("CreateWindow failed: %ld", (long)e);
+        g_set_error(err, G_UDEV_CLIENT_ERROR, G_UDEV_CLIENT_LIBUSB_FAILED,
+                    "CreateWindow failed: %ld", (long)e);
+        goto g_udev_client_init_failed_unreg;
+    }
+
+    return TRUE;
+
+ g_udev_client_init_failed_unreg:
+    UnregisterClass(G_UDEV_CLIENT_WINCLASS_NAME, NULL);
+ g_udev_client_init_failed:
+    libusb_exit(priv->ctx);
+    priv->ctx = NULL;
+
+    return FALSE;
+}
+
+static void g_udev_client_initable_iface_init(GInitableIface *iface)
+{
+    iface->init = g_udev_client_initable_init;
+}
+
+GList *g_udev_client_query_by_subsystem(GUdevClient *self, const gchar *subsystem)
+{
+    GList *l = g_list_copy(self->priv->udev_list);
+    g_list_foreach(l, (GFunc)g_object_ref, NULL);
+    return l;
+}
+
+static void g_udev_client_init(GUdevClient *self)
+{
+    self->priv = G_UDEV_CLIENT_GET_PRIVATE(self);
+}
+
+static void g_udev_client_finalize(GObject *gobject)
+{
+    GUdevClient *self = G_UDEV_CLIENT(gobject);
+    GUdevClientPrivate *priv = self->priv;
+
+    singleton = NULL;
+    DestroyWindow(priv->hwnd);
+    UnregisterClass(G_UDEV_CLIENT_WINCLASS_NAME, NULL);
+    g_udev_client_free_device_list(&priv->udev_list);
+
+    /* free libusb context initializing by libusb_init() */
+    g_warn_if_fail(priv->ctx != NULL);
+    libusb_exit(priv->ctx);
+
+    /* Chain up to the parent class */
+    if (G_OBJECT_CLASS(g_udev_client_parent_class)->finalize)
+        G_OBJECT_CLASS(g_udev_client_parent_class)->finalize(gobject);
+}
+
+static void g_udev_client_class_init(GUdevClientClass *klass)
+{
+    GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
+
+    gobject_class->finalize = g_udev_client_finalize;
+
+    signals[UEVENT_SIGNAL] =
+        g_signal_new("uevent",
+                     G_OBJECT_CLASS_TYPE(klass),
+                     G_SIGNAL_RUN_FIRST,
+                     G_STRUCT_OFFSET(GUdevClientClass, uevent),
+                     NULL, NULL,
+                     g_cclosure_user_marshal_VOID__BOXED_BOXED,
+                     G_TYPE_NONE,
+                     2,
+                     G_TYPE_STRING,
+                     G_UDEV_TYPE_DEVICE);
+
+    g_type_class_add_private(klass, sizeof(GUdevClientPrivate));
+}
+
+static gboolean get_usb_dev_info(libusb_device *dev, GUdevDeviceInfo *udevinfo)
+{
+    struct libusb_device_descriptor desc;
+
+    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;
+    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);
+    return TRUE;
+}
+
+/* Only bus,addr are compared */
+static gboolean gudev_devices_are_equal(GUdevDevice *a, GUdevDevice *b)
+{
+    GUdevDeviceInfo *ai, *bi;
+    gboolean same_bus;
+    gboolean same_addr;
+
+    ai = a->priv->udevinfo;
+    bi = b->priv->udevinfo;
+
+    same_bus  = (ai->bus  == bi->bus);
+    same_addr = (ai->addr == bi->addr);
+
+    return (same_bus && same_addr);
+}
+
+
+/* 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;
+    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 %d, I know about %d 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);
+        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;
+        }
+    }
+
+    if (!changed_dev) {
+        g_warning("couldn't find any device change");
+        goto leave;
+    }
+
+    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);
+    }
+
+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)
+{
+    /* Only DBT_DEVNODES_CHANGED recieved */
+    if (message == WM_DEVICECHANGE) {
+        handle_dev_change(singleton);
+    }
+    return DefWindowProc(hwnd, message, wparam, lparam);
+}
+
+/*** GUdevDevice ***/
+
+static void g_udev_device_finalize(GObject *object)
+{
+    GUdevDevice *device =  G_UDEV_DEVICE(object);
+
+    g_free(device->priv->udevinfo);
+    if (G_OBJECT_CLASS(g_udev_device_parent_class)->finalize != NULL)
+        (* G_OBJECT_CLASS(g_udev_device_parent_class)->finalize)(object);
+}
+
+static void g_udev_device_class_init(GUdevDeviceClass *klass)
+{
+    GObjectClass *gobject_class = (GObjectClass *) klass;
+
+    gobject_class->finalize = g_udev_device_finalize;
+    g_type_class_add_private (klass, sizeof(GUdevDevicePrivate));
+}
+
+static void g_udev_device_init(GUdevDevice *device)
+{
+    device->priv = G_TYPE_INSTANCE_GET_PRIVATE(device, G_UDEV_TYPE_DEVICE, GUdevDevicePrivate);
+}
+
+static GUdevDevice *g_udev_device_new(GUdevDeviceInfo *udevinfo)
+{
+    GUdevDevice *device;
+
+    g_return_val_if_fail(udevinfo != NULL, NULL);
+
+    device =  G_UDEV_DEVICE(g_object_new(G_UDEV_TYPE_DEVICE, NULL));
+    device->priv->udevinfo = udevinfo;
+    return device;
+}
+
+const gchar *g_udev_device_get_property(GUdevDevice *udev, const gchar *property)
+{
+    GUdevDeviceInfo* udevinfo;
+
+    g_return_val_if_fail(G_UDEV_DEVICE(udev), NULL);
+    g_return_val_if_fail(property != NULL, NULL);
+
+    udevinfo = udev->priv->udevinfo;
+    g_return_val_if_fail(udevinfo != NULL, NULL);
+
+    if (g_strcmp0(property, "BUSNUM") == 0) {
+        return udevinfo->sbus;
+    } else if (g_strcmp0(property, "DEVNUM") == 0) {
+        return udevinfo->saddr;
+    } else if (g_strcmp0(property, "DEVTYPE") == 0) {
+        return "usb_device";
+    }
+
+    g_warn_if_reached();
+    return NULL;
+}
+
+const gchar *g_udev_device_get_sysfs_attr(GUdevDevice *udev, const gchar *attr)
+{
+    GUdevDeviceInfo* udevinfo;
+
+    g_return_val_if_fail(G_UDEV_DEVICE(udev), NULL);
+    g_return_val_if_fail(attr != NULL, NULL);
+
+    udevinfo = udev->priv->udevinfo;
+    g_return_val_if_fail(udevinfo != NULL, NULL);
+
+
+    if (g_strcmp0(attr, "bDeviceClass") == 0) {
+        return udevinfo->sclass;
+    }
+    g_warn_if_reached();
+    return NULL;
+}
+
+#ifdef DEBUG_GUDEV_DEVICE_LISTS
+static void g_udev_device_print_list(GList *l, const gchar *msg)
+{
+    GList *it;
+
+    for (it = g_list_first(l); it != NULL; it=g_list_next(it)) {
+        g_udev_device_print(it->data, msg);
+    }
+}
+#endif
+
+static void g_udev_device_print(GUdevDevice *udev, const gchar *msg)
+{
+    GUdevDeviceInfo* udevinfo;
+
+    g_return_if_fail(G_UDEV_DEVICE(udev));
+
+    udevinfo = udev->priv->udevinfo;
+    g_return_if_fail(udevinfo != NULL);
+
+    SPICE_DEBUG("%s: %d.%d 0x%04x:0x%04x class %d", msg,
+                udevinfo->bus, udevinfo->addr,
+                udevinfo->vid, udevinfo->pid, udevinfo->class);
+}
diff --git a/gtk/win-usb-dev.h b/gtk/win-usb-dev.h
new file mode 100644
index 0000000..b5c4fce
--- /dev/null
+++ b/gtk/win-usb-dev.h
@@ -0,0 +1,110 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+   Copyright (C) 2012 Red Hat, Inc.
+
+   Red Hat Authors:
+   Arnon Gilboa <agilboa at redhat.com>
+   Uri Lublin   <uril at redhat.com>
+
+   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/>.
+*/
+#ifndef __WIN_USB_DEV_H__
+#define __WIN_USB_DEV_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+/* GUdevDevice */
+
+#define G_UDEV_TYPE_DEVICE         (g_udev_device_get_type())
+#define G_UDEV_DEVICE(o)           (G_TYPE_CHECK_INSTANCE_CAST((o), G_UDEV_TYPE_DEVICE, GUdevDevice))
+#define G_UDEV_DEVICE_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_UDEV_TYPE_DEVICE, GUdevDeviceClass))
+#define G_UDEV_IS_DEVICE(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_UDEV_TYPE_DEVICE))
+#define G_UDEV_IS_DEVICE_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE((k), G_UDEV_TYPE_DEVICE))
+#define G_UDEV_DEVICE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), G_UDEV_TYPE_DEVICE, GUdevDeviceClass))
+
+typedef struct _GUdevDevice GUdevDevice;
+typedef struct _GUdevDeviceClass GUdevDeviceClass;
+typedef struct _GUdevDevicePrivate GUdevDevicePrivate;
+
+struct _GUdevDevice
+{
+  GObject parent;
+  GUdevDevicePrivate *priv;
+};
+
+struct _GUdevDeviceClass
+{
+  GObjectClass parent_class;
+};
+
+/* GUdevClient */
+
+#define G_UDEV_TYPE_CLIENT         (g_udev_client_get_type())
+#define G_UDEV_CLIENT(o)           (G_TYPE_CHECK_INSTANCE_CAST((o), G_UDEV_TYPE_CLIENT, GUdevClient))
+#define G_UDEV_CLIENT_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_UDEV_TYPE_CLIENT, GUdevClientClass))
+#define G_UDEV_IS_CLIENT(o)        (G_TYPE_CHECK_INSTANCE_TYPE((o), G_UDEV_TYPE_CLIENT))
+#define G_UDEV_IS_CLIENT_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE((k), G_UDEV_TYPE_CLIENT))
+#define G_UDEV_CLIENT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), G_UDEV_TYPE_CLIENT, GUdevClientClass))
+
+typedef struct _GUdevClient GUdevClient;
+typedef struct _GUdevClientClass GUdevClientClass;
+typedef struct _GUdevClientPrivate GUdevClientPrivate;
+
+struct _GUdevClient
+{
+    GObject parent;
+
+    GUdevClientPrivate *priv;
+};
+
+struct _GUdevClientClass
+{
+    GObjectClass parent_class;
+
+    /* signals */
+    void (*uevent)(GUdevClient *client, const gchar *action, GUdevDevice  *device);
+};
+
+GType g_udev_client_get_type(void) G_GNUC_CONST;
+GUdevClient *g_udev_client_new(const gchar* const *subsystems);
+GList *g_udev_client_query_by_subsystem(GUdevClient *client, const gchar *subsystem);
+
+GType g_udev_device_get_type(void) G_GNUC_CONST;
+const gchar *g_udev_device_get_property(GUdevDevice *udev, const gchar *property);
+const gchar *g_udev_device_get_sysfs_attr(GUdevDevice *udev, const gchar *attr);
+
+GQuark g_udev_client_error_quark(void);
+#define G_UDEV_CLIENT_ERROR g_udev_client_error_quark()
+
+/**
+ * GUdevClientError:
+ * @G_UDEV_CLIENT_ERROR_FAILED: generic error code
+ * @G_UDEV_CLIENT_LIBUSB_FAILED: a libusb call failed
+ * @G_UDEV_CLIENT_WINAPI_FAILED: a winapi call failed
+ *
+ * Error codes returned by spice-client API.
+ */
+typedef enum
+{
+    G_UDEV_CLIENT_ERROR_FAILED = 1,
+    G_UDEV_CLIENT_LIBUSB_FAILED,
+    G_UDEV_CLIENT_WINAPI_FAILED
+} GUdevClientError;
+
+
+G_END_DECLS
+
+#endif /* __WIN_USB_DEV_H__ */
commit cb68b670bb616e9886b9382667a01050f5a3e073
Author: Uri Lublin <uril at redhat.com>
Date:   Thu Jul 5 23:43:55 2012 +0300

    Make SpiceUsbDevice a box for SpiceUsbDeviceInfo, instead of a box for libusb_device
    
    Note that this change may affect performance a bit, as sometimes there is
    a need to find the libusb_device or the SpiceUsbDevice. Likely it's negligible.

diff --git a/gtk/usb-device-manager.c b/gtk/usb-device-manager.c
index abfac2b..a20c414 100644
--- a/gtk/usb-device-manager.c
+++ b/gtk/usb-device-manager.c
@@ -129,13 +129,21 @@ static void spice_usb_device_unref(SpiceUsbDevice *device);
 
 static gboolean spice_usb_device_equal_libdev(SpiceUsbDevice *device,
                                               libusb_device *libdev);
+static SpiceUsbDevice *
+spice_usb_device_manager_libdev_to_device(SpiceUsbDeviceManager *self,
+                                          libusb_device *libdev);
+static libusb_device *
+spice_usb_device_manager_device_to_libdev(SpiceUsbDeviceManager *self,
+                                          SpiceUsbDevice *device);
+
 G_DEFINE_BOXED_TYPE(SpiceUsbDevice, spice_usb_device,
-                    (GBoxedCopyFunc)libusb_ref_device,
-                    (GBoxedFreeFunc)libusb_unref_device)
+                    (GBoxedCopyFunc)spice_usb_device_ref,
+                    (GBoxedFreeFunc)spice_usb_device_unref)
 
 #else
 G_DEFINE_BOXED_TYPE(SpiceUsbDevice, spice_usb_device, g_object_ref, g_object_unref)
 #endif
+
 static void spice_usb_device_manager_initable_iface_init(GInitableIface *iface);
 
 static guint signals[LAST_SIGNAL] = { 0, };
@@ -153,7 +161,7 @@ static void spice_usb_device_manager_init(SpiceUsbDeviceManager *self)
     priv->channels = g_ptr_array_new();
 #ifdef USE_USBREDIR
     priv->devices  = g_ptr_array_new_with_free_func((GDestroyNotify)
-                                                    libusb_unref_device);
+                                                    spice_usb_device_unref);
 #endif
 }
 
@@ -549,7 +557,7 @@ 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);
     }
-    libusb_unref_device((libusb_device*)device);
+    spice_usb_device_unref(device);
 }
 
 static SpiceUsbDevice*
@@ -562,8 +570,8 @@ 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 (libusb_get_bus_number((libusb_device*)curr) == bus &&
-               libusb_get_device_address((libusb_device*)curr) == address) {
+        if (spice_usb_device_get_busnum(curr) == bus &&
+               spice_usb_device_get_devaddr(curr) == address) {
             device = curr;
             break;
         }
@@ -618,7 +626,7 @@ static void spice_usb_device_manager_add_dev(SpiceUsbDeviceManager  *self,
     }
 
     if (libdev)
-        device = (SpiceUsbDevice*)libusb_ref_device(libdev);
+        device = (SpiceUsbDevice*)spice_usb_device_new(libdev);
 
     if (device && priv->auto_connect) {
         auto_ok = usbredirhost_check_device_filter(
@@ -648,7 +656,7 @@ static void spice_usb_device_manager_add_dev(SpiceUsbDeviceManager  *self,
             spice_usb_device_manager_connect_device_async(self,
                                    device, NULL,
                                    spice_usb_device_manager_auto_connect_cb,
-                                   libusb_ref_device(libdev));
+                                   spice_usb_device_ref(device));
     }
 
     SPICE_DEBUG("device added %p", device);
@@ -772,22 +780,28 @@ void spice_usb_device_manager_stop_event_listening(
 void spice_usb_device_manager_device_error(
     SpiceUsbDeviceManager *self, libusb_device *libdev, GError *err)
 {
-    SpiceUsbDevice *device = (SpiceUsbDevice *)libdev;
+    SpiceUsbDevice *device;
+
+    g_return_if_fail(SPICE_IS_USB_DEVICE_MANAGER(self));
+    g_return_if_fail(libdev != 0);
+
+    device = spice_usb_device_manager_libdev_to_device(self, libdev);
+
     g_signal_emit(self, signals[DEVICE_ERROR], 0, device, err);
 }
 #endif
 
 static SpiceUsbredirChannel *spice_usb_device_manager_get_channel_for_dev(
-    SpiceUsbDeviceManager *manager, SpiceUsbDevice *_device)
+    SpiceUsbDeviceManager *manager, SpiceUsbDevice *device)
 {
 #ifdef USE_USBREDIR
     SpiceUsbDeviceManagerPrivate *priv = manager->priv;
-    libusb_device *device = (libusb_device *)_device;
     guint i;
 
     for (i = 0; i < priv->channels->len; i++) {
         SpiceUsbredirChannel *channel = g_ptr_array_index(priv->channels, i);
-        if (spice_usbredir_channel_get_device(channel) == device)
+        libusb_device *libdev = spice_usbredir_channel_get_device(channel);
+        if (spice_usb_device_equal_libdev(device, libdev))
             return channel;
     }
 #endif
@@ -847,10 +861,10 @@ GPtrArray* spice_usb_device_manager_get_devices(SpiceUsbDeviceManager *self)
     guint i;
 
     devices_copy = g_ptr_array_new_with_free_func((GDestroyNotify)
-                                                  libusb_unref_device);
+                                                  spice_usb_device_unref);
     for (i = 0; i < priv->devices->len; i++) {
-        libusb_device *device = g_ptr_array_index(priv->devices, i);
-        g_ptr_array_add(devices_copy, libusb_ref_device(device));
+        SpiceUsbDevice *device = g_ptr_array_index(priv->devices, i);
+        g_ptr_array_add(devices_copy, spice_usb_device_ref(device));
     }
 #endif
 
@@ -899,6 +913,7 @@ void spice_usb_device_manager_connect_device_async(SpiceUsbDeviceManager *self,
 
 #ifdef USE_USBREDIR
     SpiceUsbDeviceManagerPrivate *priv = self->priv;
+    libusb_device *libdev;
     guint i;
 
     if (spice_usb_device_manager_is_device_connected(self, device)) {
@@ -914,11 +929,13 @@ void 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);
         spice_usbredir_channel_connect_device_async(channel,
-                                 (libusb_device *)device,
+                                 libdev,
                                  cancellable,
                                  spice_usb_device_manager_channel_connect_cb,
                                  result);
+        libusb_unref_device(libdev);
         return;
     }
 #endif
@@ -1010,13 +1027,21 @@ spice_usb_device_manager_can_redirect_device(SpiceUsbDeviceManager  *self,
         g_ptr_array_index(priv->channels, 0),
         &guest_filter_rules, &guest_filter_rules_count);
 
-    if (guest_filter_rules &&
-            usbredirhost_check_device_filter(
+    if (guest_filter_rules) {
+        gboolean filter_ok;
+        libusb_device *libdev;
+
+        libdev = spice_usb_device_manager_device_to_libdev(self, device);
+        g_return_val_if_fail(libdev != NULL, FALSE);
+        filter_ok = (usbredirhost_check_device_filter(
                             guest_filter_rules, guest_filter_rules_count,
-                            (libusb_device *)device, 0) != 0) {
-        g_set_error_literal(err, SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
-                            _("Some USB devices are blocked by host policy"));
-        return FALSE;
+                            libdev, 0) == 0);
+        libusb_unref_device(libdev);
+        if (!filter_ok) {
+            g_set_error_literal(err, SPICE_CLIENT_ERROR, SPICE_CLIENT_ERROR_FAILED,
+                                _("Some USB devices are blocked by host policy"));
+            return FALSE;
+        }
     }
 
     /* Check if there are free channels */
@@ -1060,28 +1085,21 @@ spice_usb_device_manager_can_redirect_device(SpiceUsbDeviceManager  *self,
  *
  * Returns: a newly-allocated string holding the description, or %NULL if failed
  */
-gchar *spice_usb_device_get_description(SpiceUsbDevice *_device, const gchar *format)
+gchar *spice_usb_device_get_description(SpiceUsbDevice *device, const gchar *format)
 {
 #ifdef USE_USBREDIR
-    libusb_device *device = (libusb_device *)_device;
-    struct libusb_device_descriptor desc;
     int bus, address, vid, pid;
     gchar *description, *descriptor, *manufacturer = NULL, *product = NULL;
 
     g_return_val_if_fail(device != NULL, NULL);
 
-    bus     = libusb_get_bus_number(device);
-    address = libusb_get_device_address(device);
-    vid     = -1;
-    pid     = -1;
-
-    if (libusb_get_device_descriptor(device, &desc) == LIBUSB_SUCCESS) {
-        vid = desc.idVendor;
-        pid = desc.idProduct;
-    }
+    bus     = spice_usb_device_get_busnum(device);
+    address = spice_usb_device_get_devaddr(device);
+    vid     = spice_usb_device_get_vid(device);
+    pid     = spice_usb_device_get_pid(device);
 
     if ((vid > 0) && (pid > 0)) {
-        descriptor = g_strdup_printf("[%04x:%04x]", desc.idVendor, desc.idProduct);
+        descriptor = g_strdup_printf("[%04x:%04x]", vid, pid);
     } else {
         descriptor = g_strdup("");
     }
@@ -1217,4 +1235,53 @@ spice_usb_device_equal_libdev(SpiceUsbDevice *device,
 
     return ((bus1 == bus2) && (addr1 == addr2));
 }
+
+static SpiceUsbDevice *
+spice_usb_device_manager_libdev_to_device(SpiceUsbDeviceManager *self,
+                                          libusb_device *libdev)
+{
+    guint8 bus, addr;
+
+    bus  = libusb_get_bus_number(libdev);
+    addr = libusb_get_device_address(libdev);
+
+    return spice_usb_device_manager_find_device(self, bus, addr);
+}
+
+/*
+ * Caller must libusb_unref_device the libusb_device returned by this function.
+ * Returns a libusb_device, or NULL upon failure
+ */
+static libusb_device *
+spice_usb_device_manager_device_to_libdev(SpiceUsbDeviceManager *self,
+                                          SpiceUsbDevice *device)
+{
+    libusb_device *d, **devlist;
+    guint8 bus, addr;
+    int i;
+
+    g_return_val_if_fail(SPICE_IS_USB_DEVICE_MANAGER(self), NULL);
+    g_return_val_if_fail(device != NULL, NULL);
+    g_return_val_if_fail(self->priv != NULL, NULL);
+    g_return_val_if_fail(self->priv->context != NULL, NULL);
+
+    bus  = spice_usb_device_get_busnum(device);
+    addr = spice_usb_device_get_devaddr(device);
+
+    libusb_get_device_list(self->priv->context, &devlist);
+    if (!devlist)
+        return NULL;
+
+    for (i = 0; (d = devlist[i]) != NULL; i++) {
+        if ((libusb_get_bus_number(d) == bus) &&
+            (libusb_get_device_address(d) == addr)) {
+            libusb_ref_device(d);
+            break;
+        }
+    }
+
+    libusb_free_device_list(devlist, 1);
+
+    return d;
+}
 #endif /* USE_USBREDIR */
commit 4c1202f994fe52aa1242fe0e88e6fd51a3b24207
Author: Uri Lublin <uril at redhat.com>
Date:   Thu Jul 5 23:43:54 2012 +0300

    Introduce SpiceUsbDeviceInfo to be kept instead of a libusb_device
    
    For Windows, it's better not to keep references for libusb_devices
    that are not used.
    So instead of makeing SpiceUsbDevice a box for a libusb_device
    it is going to be a box for a SpiceUsbDeviceInfo.

diff --git a/gtk/usb-device-manager-priv.h b/gtk/usb-device-manager-priv.h
index 079f638..a8617ba 100644
--- a/gtk/usb-device-manager-priv.h
+++ b/gtk/usb-device-manager-priv.h
@@ -35,6 +35,12 @@ void spice_usb_device_manager_stop_event_listening(
 #include <libusb.h>
 void spice_usb_device_manager_device_error(
     SpiceUsbDeviceManager *manager, libusb_device *libdev, GError *err);
+
+guint8 spice_usb_device_get_busnum(SpiceUsbDevice *device);
+guint8 spice_usb_device_get_devaddr(SpiceUsbDevice *device);
+guint16 spice_usb_device_get_vid(SpiceUsbDevice *device);
+guint16 spice_usb_device_get_pid(SpiceUsbDevice *device);
+
 #endif
 
 G_END_DECLS
diff --git a/gtk/usb-device-manager.c b/gtk/usb-device-manager.c
index 09cb1f9..abfac2b 100644
--- a/gtk/usb-device-manager.c
+++ b/gtk/usb-device-manager.c
@@ -102,6 +102,16 @@ struct _SpiceUsbDeviceManagerPrivate {
 };
 
 #ifdef USE_USBREDIR
+
+typedef struct _SpiceUsbDeviceInfo {
+    guint8  busnum;
+    guint8  devaddr;
+    guint16 vid;
+    guint16 pid;
+    gint    ref;
+} SpiceUsbDeviceInfo;
+
+
 static void channel_new(SpiceSession *session, SpiceChannel *channel,
                         gpointer user_data);
 static void channel_destroy(SpiceSession *session, SpiceChannel *channel,
@@ -113,6 +123,12 @@ static void spice_usb_device_manager_uevent_cb(GUdevClient     *client,
 static void spice_usb_device_manager_add_dev(SpiceUsbDeviceManager  *self,
                                              GUdevDevice            *udev);
 
+static SpiceUsbDeviceInfo *spice_usb_device_new(libusb_device *libdev);
+static SpiceUsbDevice *spice_usb_device_ref(SpiceUsbDevice *device);
+static void spice_usb_device_unref(SpiceUsbDevice *device);
+
+static gboolean spice_usb_device_equal_libdev(SpiceUsbDevice *device,
+                                              libusb_device *libdev);
 G_DEFINE_BOXED_TYPE(SpiceUsbDevice, spice_usb_device,
                     (GBoxedCopyFunc)libusb_ref_device,
                     (GBoxedFreeFunc)libusb_unref_device)
@@ -1087,3 +1103,118 @@ gchar *spice_usb_device_get_description(SpiceUsbDevice *_device, const gchar *fo
     return NULL;
 #endif
 }
+
+
+
+#ifdef USE_USBREDIR
+/*
+ * SpiceUsbDeviceInfo
+ */
+static SpiceUsbDeviceInfo *spice_usb_device_new(libusb_device *libdev)
+{
+    SpiceUsbDeviceInfo *info;
+    struct libusb_device_descriptor desc;
+    int errcode;
+    const gchar *errstr;
+    guint8 bus, addr;
+
+    g_return_val_if_fail(libdev != NULL, NULL);
+
+    bus = libusb_get_bus_number(libdev);
+    addr = libusb_get_device_address(libdev);
+
+    errcode = libusb_get_device_descriptor(libdev, &desc);
+    if (errcode < 0) {
+        errstr = spice_usbutil_libusb_strerror(errcode);
+        g_warning("cannot get device descriptor for (%p) %d.%d -- %s(%d)",
+                  libdev, bus, addr, errstr, errcode);
+        return NULL;
+    }
+
+    info = g_new0(SpiceUsbDeviceInfo, 1);
+
+    info->busnum  = bus;
+    info->devaddr = addr;
+    info->vid = desc.idVendor;
+    info->pid = desc.idProduct;
+    info->ref = 1;
+
+    return info;
+}
+
+guint8 spice_usb_device_get_busnum(SpiceUsbDevice *device)
+{
+    SpiceUsbDeviceInfo *info = (SpiceUsbDeviceInfo *)device;
+
+    g_return_val_if_fail(info != NULL, 0);
+
+    return info->busnum;
+}
+
+guint8 spice_usb_device_get_devaddr(SpiceUsbDevice *device)
+{
+    SpiceUsbDeviceInfo *info = (SpiceUsbDeviceInfo *)device;
+
+    g_return_val_if_fail(info != NULL, 0);
+
+    return info->devaddr;
+}
+
+guint16 spice_usb_device_get_vid(SpiceUsbDevice *device)
+{
+    SpiceUsbDeviceInfo *info = (SpiceUsbDeviceInfo *)device;
+
+    g_return_val_if_fail(info != NULL, 0);
+
+    return info->vid;
+}
+
+guint16 spice_usb_device_get_pid(SpiceUsbDevice *device)
+{
+    SpiceUsbDeviceInfo *info = (SpiceUsbDeviceInfo *)device;
+
+    g_return_val_if_fail(info != NULL, 0);
+
+    return info->pid;
+}
+
+static SpiceUsbDevice *spice_usb_device_ref(SpiceUsbDevice *device)
+{
+    SpiceUsbDeviceInfo *info = (SpiceUsbDeviceInfo *)device;
+
+    g_return_val_if_fail(info != NULL, NULL);
+    g_atomic_int_inc(&info->ref);
+    return device;
+}
+
+static void spice_usb_device_unref(SpiceUsbDevice *device)
+{
+    gboolean ref_count_is_0;
+
+    SpiceUsbDeviceInfo *info = (SpiceUsbDeviceInfo *)device;
+
+    g_return_if_fail(info != NULL);
+
+    ref_count_is_0 = g_atomic_int_dec_and_test(&info->ref);
+    if (ref_count_is_0) {
+        g_free(info);
+    }
+}
+
+static gboolean
+spice_usb_device_equal_libdev(SpiceUsbDevice *device,
+                              libusb_device  *libdev)
+{
+    guint8 addr1, addr2, bus1, bus2;
+
+    if ((device == NULL) || (libdev == NULL))
+        return FALSE;
+
+    bus1  = spice_usb_device_get_busnum(device);
+    addr1 = spice_usb_device_get_devaddr(device);
+    bus2  = libusb_get_bus_number(libdev);
+    addr2 = libusb_get_device_address(libdev);
+
+    return ((bus1 == bus2) && (addr1 == addr2));
+}
+#endif /* USE_USBREDIR */
commit 42216eb7de4228db8fd30b4ad87846b21a76cc70
Author: Uri Lublin <uril at redhat.com>
Date:   Thu Jul 5 23:43:56 2012 +0300

    Windows mingw: usb: configure.ac: do not require GUDEV for USBREDIR
    
    For windows GUDEV is not required
    For Linux GUDEV is checked as a part of USBREDIR block, but
    as a separate check.

diff --git a/configure.ac b/configure.ac
index 3841c56..233ce4d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -343,7 +343,7 @@ if test "x$enable_usbredir" = "xno"; then
   have_usbredir="no"
 else
   PKG_CHECK_MODULES([USBREDIR],
-                    [gudev-1.0 libusb-1.0 >= 1.0.9 libusbredirhost >= 0.4.2 libusbredirparser >= 0.4],
+                    [libusb-1.0 >= 1.0.9 libusbredirhost >= 0.4.2 libusbredirparser >= 0.4],
                     [have_usbredir=yes],
                     [have_usbredir=no])
   if test "x$have_usbredir" = "xno" && test "x$enable_usbredir" = "xyes"; then
@@ -352,6 +352,19 @@ else
   if test "x$have_usbredir" = "xyes"; then
     AC_DEFINE(USE_USBREDIR, [1], [Define if supporting usbredir proxying])
   fi
+
+  # Require gudev for non-windows
+  if test "x$os_win32" = "xno"; then
+    PKG_CHECK_MODULES([GUDEV],
+                      [gudev-1.0],
+                      [have_gudev=yes],
+                      [have_gudev=no])
+
+    if test "x$have_usbredir" = "xyes" && test "x$have_gudev" = "xno"; then
+      AC_MSG_ERROR([usbredir requested but required gudev is not available])
+    fi
+    AC_DEFINE(USE_GUDEV, [1], [Define if supporting gudev])
+  fi
 fi
 AM_CONDITIONAL([WITH_USBREDIR], [test "x$have_usbredir" = "xyes"])
 
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index f5f6bc6..10fa9cd 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -83,6 +83,7 @@ SPICE_COMMON_CPPFLAGS =						\
 	$(GST_CFLAGS)						\
 	$(SMARTCARD_CFLAGS)					\
 	$(USBREDIR_CFLAGS)					\
+	$(GUDEV_CFLAGS)						\
 	$(NULL)
 
 AM_CPPFLAGS =					\
@@ -183,6 +184,7 @@ libspice_client_glib_2_0_la_LIBADD =					\
 	$(SASL_LIBS)							\
 	$(SMARTCARD_LIBS)						\
 	$(USBREDIR_LIBS)						\
+	$(GUDEV_LIBS)							\
 	$(NULL)
 
 if WITH_POLKIT
commit f871ed2176abb95a4335882a438be6c77a3f1b69
Author: Uri Lublin <uril at redhat.com>
Date:   Thu Jul 5 23:44:05 2012 +0300

    usb-device-manager: mingw: add_dev: ignore already known devices
    
    Sometimes on a Windows client, udev events are received while
    the driver is being un/installed. so just ignore it

diff --git a/gtk/usb-device-manager.c b/gtk/usb-device-manager.c
index ffddb3b..09cb1f9 100644
--- a/gtk/usb-device-manager.c
+++ b/gtk/usb-device-manager.c
@@ -580,6 +580,14 @@ static void spice_usb_device_manager_add_dev(SpiceUsbDeviceManager  *self,
         return;
     }
 
+#ifdef G_OS_WIN32
+    device = spice_usb_device_manager_find_device(self, bus, address);
+    if (device) {
+        SPICE_DEBUG("USB device at %d.%d already exists, ignored", bus, address);
+        return;
+    }
+#endif
+
     if (priv->coldplug_list)
         dev_list = priv->coldplug_list;
     else
commit 235e878181dfb97e0a0f116ff33e25dcfad66361
Author: Uri Lublin <uril at redhat.com>
Date:   Thu Jul 5 23:44:03 2012 +0300

    usb-device-manager: add a helper function to find a usb device <bus, addr>
    
    And use it in spice_usb_device_manager_remove_dev

diff --git a/gtk/usb-device-manager.c b/gtk/usb-device-manager.c
index 04c3169..ffddb3b 100644
--- a/gtk/usb-device-manager.c
+++ b/gtk/usb-device-manager.c
@@ -536,6 +536,25 @@ static void spice_usb_device_manager_auto_connect_cb(GObject      *gobject,
     libusb_unref_device((libusb_device*)device);
 }
 
+static SpiceUsbDevice*
+spice_usb_device_manager_find_device(SpiceUsbDeviceManager *self,
+                                     guint8 bus, guint8 address)
+{
+    SpiceUsbDeviceManagerPrivate *priv = self->priv;
+    SpiceUsbDevice *curr, *device = NULL;
+    guint i;
+
+    for (i = 0; i < priv->devices->len; i++) {
+        curr = g_ptr_array_index(priv->devices, i);
+        if (libusb_get_bus_number((libusb_device*)curr) == bus &&
+               libusb_get_device_address((libusb_device*)curr) == address) {
+            device = curr;
+            break;
+        }
+    }
+    return device;
+}
+
 static void spice_usb_device_manager_add_dev(SpiceUsbDeviceManager  *self,
                                              GUdevDevice            *udev)
 {
@@ -616,22 +635,13 @@ static void spice_usb_device_manager_remove_dev(SpiceUsbDeviceManager  *self,
                                                 GUdevDevice            *udev)
 {
     SpiceUsbDeviceManagerPrivate *priv = self->priv;
-    SpiceUsbDevice *curr, *device = NULL;
+    SpiceUsbDevice *device = NULL;
     int bus, address;
-    guint i;
 
     if (!spice_usb_device_manager_get_udev_bus_n_address(udev, &bus, &address))
         return;
 
-    for (i = 0; i < priv->devices->len; i++) {
-        curr = g_ptr_array_index(priv->devices, i);
-        if (libusb_get_bus_number((libusb_device*)curr) == bus &&
-               libusb_get_device_address((libusb_device*)curr) == address) {
-            device = curr;
-            break;
-        }
-    }
-
+    device = spice_usb_device_manager_find_device(self, bus, address);
     if (!device) {
         g_warning("Could not find USB device to remove at busnum %d devaddr %d",
                   bus, address);
commit 0ba45213b0f44660e9e06da3c68b9d28e1736a14
Author: Uri Lublin <uril at redhat.com>
Date:   Sun Jul 8 13:16:31 2012 +0300

    spice_usb_device_get_description: use device-descriptor only to get <vid,pid>
    
    In preparation for a different SpiceUsbDevice.
    
    With the new SpiceUsbDeviceInfo, <vid,pid> will be provided by
    SpiceUsbDevice, and not by the device_descriptor (from libusb)

diff --git a/gtk/usb-device-manager.c b/gtk/usb-device-manager.c
index 9f29c76..04c3169 100644
--- a/gtk/usb-device-manager.c
+++ b/gtk/usb-device-manager.c
@@ -1031,25 +1031,30 @@ gchar *spice_usb_device_get_description(SpiceUsbDevice *_device, const gchar *fo
 #ifdef USE_USBREDIR
     libusb_device *device = (libusb_device *)_device;
     struct libusb_device_descriptor desc;
-    int bus, address;
+    int bus, address, vid, pid;
     gchar *description, *descriptor, *manufacturer = NULL, *product = NULL;
 
     g_return_val_if_fail(device != NULL, NULL);
 
     bus     = libusb_get_bus_number(device);
     address = libusb_get_device_address(device);
+    vid     = -1;
+    pid     = -1;
 
     if (libusb_get_device_descriptor(device, &desc) == LIBUSB_SUCCESS) {
-        spice_usb_util_get_device_strings(bus, address,
-                                          desc.idVendor, desc.idProduct,
-                                          &manufacturer, &product);
+        vid = desc.idVendor;
+        pid = desc.idProduct;
+    }
+
+    if ((vid > 0) && (pid > 0)) {
         descriptor = g_strdup_printf("[%04x:%04x]", desc.idVendor, desc.idProduct);
     } else {
-        spice_usb_util_get_device_strings(bus, address, -1, -1,
-                                          &manufacturer, &product);
         descriptor = g_strdup("");
     }
 
+    spice_usb_util_get_device_strings(bus, address, vid, pid,
+                                      &manufacturer, &product);
+
     if (!format)
         format = _("%s %s %s at %d-%d");
 
commit 15b4e7890faf3e5d65490d4650f3bf4649b3fd5a
Author: Uri Lublin <uril at redhat.com>
Date:   Sun Jul 8 12:40:59 2012 +0300

    spice_usb_device_manager_device_error: replace SpiceUsbDevice with libusb_device
    
    Its only user is channel-usbredir, which needs the libusb_device.
    
    In preparations for a different SpiceUsbDevice.

diff --git a/gtk/channel-usbredir.c b/gtk/channel-usbredir.c
index 3d57152..354d2e1 100644
--- a/gtk/channel-usbredir.c
+++ b/gtk/channel-usbredir.c
@@ -569,7 +569,7 @@ static void do_emit_main_context(GObject *object, int event, gpointer params)
             spice_usb_device_manager_device_error(
                 spice_usb_device_manager_get(
                     spice_channel_get_session(SPICE_CHANNEL(channel)), NULL),
-                (SpiceUsbDevice *)p->device, p->error);
+                p->device, p->error);
         }
         break;
     }
diff --git a/gtk/usb-device-manager-priv.h b/gtk/usb-device-manager-priv.h
index 912e3bf..079f638 100644
--- a/gtk/usb-device-manager-priv.h
+++ b/gtk/usb-device-manager-priv.h
@@ -31,8 +31,11 @@ gboolean spice_usb_device_manager_start_event_listening(
 void spice_usb_device_manager_stop_event_listening(
     SpiceUsbDeviceManager *manager);
 
+#ifdef USE_USBREDIR
+#include <libusb.h>
 void spice_usb_device_manager_device_error(
-    SpiceUsbDeviceManager *manager, SpiceUsbDevice *device, GError *err);
+    SpiceUsbDeviceManager *manager, libusb_device *libdev, GError *err);
+#endif
 
 G_END_DECLS
 
diff --git a/gtk/usb-device-manager.c b/gtk/usb-device-manager.c
index dbf3493..9f29c76 100644
--- a/gtk/usb-device-manager.c
+++ b/gtk/usb-device-manager.c
@@ -736,8 +736,9 @@ void spice_usb_device_manager_stop_event_listening(
 }
 
 void spice_usb_device_manager_device_error(
-    SpiceUsbDeviceManager *self, SpiceUsbDevice *device, GError *err)
+    SpiceUsbDeviceManager *self, libusb_device *libdev, GError *err)
 {
+    SpiceUsbDevice *device = (SpiceUsbDevice *)libdev;
     g_signal_emit(self, signals[DEVICE_ERROR], 0, device, err);
 }
 #endif
commit c1799ead7e0577a201663ade9acc2935db1a9b9e
Author: Uri Lublin <uril at redhat.com>
Date:   Sun Jul 8 16:21:15 2012 +0300

    spice_usb_device_manager_auto_connect_cb: use type SpiceUsbDevice for "device"
    
    Currently SpiceUsbDevice is a BOX for libusb_device.
    In preparation for a different SpiceUsbDevice.

diff --git a/gtk/usb-device-manager.c b/gtk/usb-device-manager.c
index d4ad1f9..dbf3493 100644
--- a/gtk/usb-device-manager.c
+++ b/gtk/usb-device-manager.c
@@ -520,12 +520,12 @@ static void spice_usb_device_manager_auto_connect_cb(GObject      *gobject,
                                                      gpointer      user_data)
 {
     SpiceUsbDeviceManager *self = SPICE_USB_DEVICE_MANAGER(gobject);
-    libusb_device *device = user_data;
+    SpiceUsbDevice *device = user_data;
     GError *err = NULL;
 
     spice_usb_device_manager_connect_device_finish(self, res, &err);
     if (err) {
-        gchar *desc = spice_usb_device_get_description((SpiceUsbDevice *)device, NULL);
+        gchar *desc = spice_usb_device_get_description(device, NULL);
         g_prefix_error(&err, "Could not auto-redirect %s: ", desc);
         g_free(desc);
 
@@ -533,7 +533,7 @@ 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);
     }
-    libusb_unref_device(device);
+    libusb_unref_device((libusb_device*)device);
 }
 
 static void spice_usb_device_manager_add_dev(SpiceUsbDeviceManager  *self,
commit 97740e38e641de0fa5a90a049bfb40125c07ab45
Author: Uri Lublin <uril at redhat.com>
Date:   Sun Jul 8 12:12:38 2012 +0300

    spice_usb_device_manager_add_dev: use type SpiceUsbDevice for "device"
    
    Currently SpiceUsbDevice is a BOX for libusb_device.
    In preparation for a different SpiceUsbDevice.
    
    Renamed the libusb_device variable to libdev. Needed when
    asking usbredir to check the filter.

diff --git a/gtk/usb-device-manager.c b/gtk/usb-device-manager.c
index a81db57..d4ad1f9 100644
--- a/gtk/usb-device-manager.c
+++ b/gtk/usb-device-manager.c
@@ -540,7 +540,8 @@ static void spice_usb_device_manager_add_dev(SpiceUsbDeviceManager  *self,
                                              GUdevDevice            *udev)
 {
     SpiceUsbDeviceManagerPrivate *priv = self->priv;
-    libusb_device *device = NULL, **dev_list = NULL;
+    libusb_device *libdev = NULL, **dev_list = NULL;
+    SpiceUsbDevice *device = NULL;
     const gchar *devtype, *devclass;
     int i, bus, address;
     gboolean auto_ok = FALSE;
@@ -568,16 +569,19 @@ static void spice_usb_device_manager_add_dev(SpiceUsbDeviceManager  *self,
     for (i = 0; dev_list && dev_list[i]; i++) {
         if (libusb_get_bus_number(dev_list[i]) == bus &&
             libusb_get_device_address(dev_list[i]) == address) {
-            device = libusb_ref_device(dev_list[i]);
+            libdev = dev_list[i];
             break;
         }
     }
 
+    if (libdev)
+        device = (SpiceUsbDevice*)libusb_ref_device(libdev);
+
     if (device && priv->auto_connect) {
         auto_ok = usbredirhost_check_device_filter(
                             priv->auto_conn_filter_rules,
                             priv->auto_conn_filter_rules_count,
-                            device, 0) == 0;
+                            libdev, 0) == 0;
     }
 
     if (!priv->coldplug_list)
@@ -595,13 +599,13 @@ static void spice_usb_device_manager_add_dev(SpiceUsbDeviceManager  *self,
         gboolean can_redirect;
 
         can_redirect = spice_usb_device_manager_can_redirect_device(
-                                        self, (SpiceUsbDevice *)device, NULL);
+                                        self, device, NULL);
 
         if (can_redirect && auto_ok)
             spice_usb_device_manager_connect_device_async(self,
-                                   (SpiceUsbDevice *)device, NULL,
+                                   device, NULL,
                                    spice_usb_device_manager_auto_connect_cb,
-                                   libusb_ref_device(device));
+                                   libusb_ref_device(libdev));
     }
 
     SPICE_DEBUG("device added %p", device);
commit a0ec25a1706074a2db062df36ecc96828f960a2f
Author: Uri Lublin <uril at redhat.com>
Date:   Sun Jul 8 12:04:26 2012 +0300

    spice_usb_device_manager_add_dev: check auto_ok before freeing libusb device list
    
    In preparation for a different SpiceUsbDevice.

diff --git a/gtk/usb-device-manager.c b/gtk/usb-device-manager.c
index c50a7da..a81db57 100644
--- a/gtk/usb-device-manager.c
+++ b/gtk/usb-device-manager.c
@@ -543,6 +543,7 @@ static void spice_usb_device_manager_add_dev(SpiceUsbDeviceManager  *self,
     libusb_device *device = NULL, **dev_list = NULL;
     const gchar *devtype, *devclass;
     int i, bus, address;
+    gboolean auto_ok = FALSE;
 
     devtype = g_udev_device_get_property(udev, "DEVTYPE");
     /* Check if this is a usb device (and not an interface) */
@@ -572,6 +573,13 @@ static void spice_usb_device_manager_add_dev(SpiceUsbDeviceManager  *self,
         }
     }
 
+    if (device && priv->auto_connect) {
+        auto_ok = usbredirhost_check_device_filter(
+                            priv->auto_conn_filter_rules,
+                            priv->auto_conn_filter_rules_count,
+                            device, 0) == 0;
+    }
+
     if (!priv->coldplug_list)
         libusb_free_device_list(dev_list, 1);
 
@@ -584,16 +592,11 @@ static void spice_usb_device_manager_add_dev(SpiceUsbDeviceManager  *self,
     g_ptr_array_add(priv->devices, device);
 
     if (priv->auto_connect) {
-        gboolean can_redirect, auto_ok;
+        gboolean can_redirect;
 
         can_redirect = spice_usb_device_manager_can_redirect_device(
                                         self, (SpiceUsbDevice *)device, NULL);
 
-        auto_ok = usbredirhost_check_device_filter(
-                            priv->auto_conn_filter_rules,
-                            priv->auto_conn_filter_rules_count,
-                            device, 0) == 0;
-
         if (can_redirect && auto_ok)
             spice_usb_device_manager_connect_device_async(self,
                                    (SpiceUsbDevice *)device, NULL,
commit 2339c0005ce38e2fb57a748390cb12c11241f756
Author: Uri Lublin <uril at redhat.com>
Date:   Sun Jul 8 10:08:11 2012 +0300

    spice_usb_device_manager_remove_dev: use type SpiceUsbDevice for "device"
    
    Currently SpiceUsbDevice is a BOX for libusb_device.
    In preparation for a different SpiceUsbDevice.

diff --git a/gtk/usb-device-manager.c b/gtk/usb-device-manager.c
index 8b277cc..c50a7da 100644
--- a/gtk/usb-device-manager.c
+++ b/gtk/usb-device-manager.c
@@ -609,7 +609,7 @@ static void spice_usb_device_manager_remove_dev(SpiceUsbDeviceManager  *self,
                                                 GUdevDevice            *udev)
 {
     SpiceUsbDeviceManagerPrivate *priv = self->priv;
-    libusb_device *curr, *device = NULL;
+    SpiceUsbDevice *curr, *device = NULL;
     int bus, address;
     guint i;
 
@@ -618,8 +618,8 @@ static void spice_usb_device_manager_remove_dev(SpiceUsbDeviceManager  *self,
 
     for (i = 0; i < priv->devices->len; i++) {
         curr = g_ptr_array_index(priv->devices, i);
-        if (libusb_get_bus_number(curr) == bus &&
-               libusb_get_device_address(curr) == address) {
+        if (libusb_get_bus_number((libusb_device*)curr) == bus &&
+               libusb_get_device_address((libusb_device*)curr) == address) {
             device = curr;
             break;
         }
@@ -631,7 +631,7 @@ static void spice_usb_device_manager_remove_dev(SpiceUsbDeviceManager  *self,
         return;
     }
 
-    spice_usb_device_manager_disconnect_device(self, (SpiceUsbDevice *)device);
+    spice_usb_device_manager_disconnect_device(self, device);
 
     SPICE_DEBUG("device removed %p", device);
     g_signal_emit(self, signals[DEVICE_REMOVED], 0, device);
commit ad6127256316a227a5167db1f731069285323c7d
Author: Uri Lublin <uril at redhat.com>
Date:   Thu Jul 5 23:44:09 2012 +0300

    controller/test.c: mingw: fix compiler bad param warning for ReadFile
    
    It seems that ssize_t is int, while DWORD is long
    
    Compiler warning (some whitespaces where added for readability):
      ../../../gtk/controller/test.c: In function 'read_from_pipe':
      ../../../gtk/controller/test.c:108:5: warning: passing argument 4 \
           of 'ReadFile' from incompatible pointer type [enabled by default]
      In file included from /usr/i686-w64-mingw32/sys-root/mingw/include/windows.h:70:0,
                       from ../../../gtk/controller/test.c:27:
      /usr/i686-w64-mingw32/sys-root/mingw/include/winbase.h:1426:29: note: expected \
           'LPDWORD' but argument is of type 'ssize_t *'

diff --git a/gtk/controller/test.c b/gtk/controller/test.c
index 3f3eb55..f6f3975 100644
--- a/gtk/controller/test.c
+++ b/gtk/controller/test.c
@@ -105,9 +105,11 @@ ssize_t read_from_pipe (void* data, size_t size)
 {
     ssize_t read;
 #ifdef WIN32
-    if (!ReadFile (pipe, data, size, &read, NULL)) {
+    DWORD bytes;
+    if (!ReadFile (pipe, data, size, &bytes, NULL)) {
         printf ("Read from pipe failed %u\n", GetLastError());
     }
+    read = bytes;
 #else
     read = recv (sock, data, size, 0);
     if ((read == -1 || read == 0)) {
commit b22a0976f4ae89fc9cd9eb188af16ca309318bd3
Author: Uri Lublin <uril at redhat.com>
Date:   Thu Jul 5 23:43:59 2012 +0300

    usb-device-manager: warn if a device to remove was not found
    
    Also changed a bit the warning text on device-add to differentiate the two.

diff --git a/gtk/usb-device-manager.c b/gtk/usb-device-manager.c
index b39c2d4..8b277cc 100644
--- a/gtk/usb-device-manager.c
+++ b/gtk/usb-device-manager.c
@@ -576,7 +576,7 @@ static void spice_usb_device_manager_add_dev(SpiceUsbDeviceManager  *self,
         libusb_free_device_list(dev_list, 1);
 
     if (!device) {
-        g_warning("Could not find USB device at busnum %d devaddr %d",
+        g_warning("Could not find USB device to add at busnum %d devaddr %d",
                   bus, address);
         return;
     }
@@ -624,8 +624,12 @@ static void spice_usb_device_manager_remove_dev(SpiceUsbDeviceManager  *self,
             break;
         }
     }
-    if (!device)
+
+    if (!device) {
+        g_warning("Could not find USB device to remove at busnum %d devaddr %d",
+                  bus, address);
         return;
+    }
 
     spice_usb_device_manager_disconnect_device(self, (SpiceUsbDevice *)device);
 
commit 9edc7885264832d0ecbccbfb1df943ac5ef39bf2
Author: Uri Lublin <uril at redhat.com>
Date:   Thu Jul 5 23:43:53 2012 +0300

    spicy: more informative presentation of usb devices in menu
    
    Using the default format.

diff --git a/gtk/spicy.c b/gtk/spicy.c
index 98a44d5..5ffe3b7 100644
--- a/gtk/spicy.c
+++ b/gtk/spicy.c
@@ -460,7 +460,7 @@ static void menu_cb_select_usb_devices(GtkAction *action, void *data)
     area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
 
     usb_device_widget = spice_usb_device_widget_new(win->conn->session,
-                                                    "%s %s");
+                                                    NULL); /* default format */
     g_signal_connect(usb_device_widget, "connect-failed",
                      G_CALLBACK(usb_connect_failed), NULL);
     gtk_box_pack_start(GTK_BOX(area), usb_device_widget, TRUE, TRUE, 0);


More information about the Spice-commits mailing list