[Spice-devel,spice-gtk,Win32,v3,08/12] Windows mingw: usb: Dynamically install a libusb driver for USB devices

Submitted by Uri Lublin on June 28, 2012, 1:46 a.m.

Details

Message ID 1340848001-7791-9-git-send-email-uril@redhat.com
State New
Headers show

Not browsing as part of any series.

Commit Message

Uri Lublin June 28, 2012, 1:46 a.m.
- 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.
---
 gtk/Makefile.am              |   11 ++
 gtk/usb-device-manager.c     |  143 +++++++++++++++-
 gtk/win-usb-clerk.h          |   35 ++++
 gtk/win-usb-driver-install.c |  379 ++++++++++++++++++++++++++++++++++++++++++
 gtk/win-usb-driver-install.h |   98 +++++++++++
 5 files changed, 660 insertions(+), 6 deletions(-)
 create mode 100644 gtk/win-usb-clerk.h
 create mode 100644 gtk/win-usb-driver-install.c
 create mode 100644 gtk/win-usb-driver-install.h

Patch hide | download patch | download mbox

diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index 4df3ee5..646b614 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -313,9 +313,20 @@  libspice_client_glib_2_0_la_LIBADD += $(GTHREAD_LIBS)
 endif


+NAMED_PIPE_FILES =				\
+	controller/namedpipe.h			\
+	controller/namedpipe.c			\
+	controller/namedpipeconnection.c	\
+	controller/namedpipeconnection.h	\
+	$(NULL)
+
 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	\
+	$(NAMED_PIPE_FILES)		\
 	$(NULL)

 if OS_WIN32
diff --git a/gtk/usb-device-manager.c b/gtk/usb-device-manager.c
index e4c8c32..8340335 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
@@ -122,6 +123,14 @@  static void spice_usb_device_manager_uevent_cb(GUdevClient     *client,
                                                gpointer         user_data);
 static void spice_usb_device_manager_add_dev(SpiceUsbDeviceManager  *self,
                                              GUdevDevice            *udev);
+
+static void
+_spice_usb_device_manager_connect_device_async(SpiceUsbDeviceManager *self,
+                                               SpiceUsbDevice *device,
+                                               GCancellable *cancellable,
+                                               GAsyncReadyCallback callback,
+                                               gpointer user_data);
+
 static
 libusb_device *spice_usb_device_find_libusb_device(SpiceUsbDeviceManager *self,
                                                    SpiceUsbDevice *device);
@@ -129,6 +138,12 @@  libusb_device *spice_usb_device_find_libusb_device(SpiceUsbDeviceManager *self,

 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,
@@ -604,7 +619,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,
-                                   g_object_ref(device));
+                                   device);
         }
     }

@@ -669,6 +684,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
+ */
+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(SPICE_IS_USB_DEVICE(device));
+
+    status = spice_win_usb_driver_install_finish(installer, res, &err);
+
+    g_object_unref(installer);
+
+    if (err) {
+        g_warning("win usb driver installation failed -- %s",
+                  err->message);
+        g_error_free(err);
+        g_object_unref(device);
+        return;
+    }
+
+    if (!status) {
+        g_warning("failed to install win usb driver (status=0)");
+        g_object_unref(device);
+        return;
+    }
+
+    /* device is already ref'ed */
+    _spice_usb_device_manager_connect_device_async(self,
+                                                   device,
+                                                   cancellable,
+                                                   callback,
+                                                   user_data);
+
+}
+#endif
+
 /* ------------------------------------------------------------------ */
 /* private api                                                        */

@@ -841,11 +937,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;

@@ -893,6 +990,40 @@  done:
     g_object_unref(result);
 }

+
+void spice_usb_device_manager_connect_device_async(SpiceUsbDeviceManager *self,
+                                             SpiceUsbDevice *device,
+                                             GCancellable *cancellable,
+                                             GAsyncReadyCallback callback,
+                                             gpointer user_data)
+{
+
+    device = g_object_ref(device);
+
+#ifdef G_OS_WIN32
+    SpiceWinUsbDriver *installer;
+    UsbInstallCbInfo *cbinfo;
+
+    installer = spice_win_usb_driver_new();
+    cbinfo = g_new0(UsbInstallCbInfo, 1);
+    cbinfo->manager     = self;
+    cbinfo->device      = device;
+    cbinfo->installer   = installer;
+    cbinfo->cancellable = cancellable;
+    cbinfo->callback    = callback;
+    cbinfo->user_data   = user_data;
+    spice_win_usb_driver_install(installer, device, NULL,
+                                 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..9f4fdfc
--- /dev/null
+++ b/gtk/win-usb-driver-install.c
@@ -0,0 +1,379 @@ 
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+   Copyright (C) 2011 Red Hat, Inc.
+
+   Red Hat Authors:
+   Uri Lublin <uril@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 <gio/gio.h>
+#include "spice-util.h"
+#include "win-usb-clerk.h"
+#include "win-usb-driver-install.h"
+#include "controller/namedpipe.h"
+#include "controller/namedpipeconnection.h"
+#include "spice-usb-device.h"
+#include "spice-usb-device-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;
+    gulong                cancellable_id;
+    GIOStream             *gios;
+    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)
+{
+    SpiceWinUsbDriverPrivate *priv;
+    GError *err = NULL;
+
+    g_return_if_fail(self != NULL);
+    priv = self->priv;
+    g_return_if_fail(priv != NULL);
+
+    if (priv->gios) {
+        g_io_stream_close(priv->gios, NULL, &err);
+        if (err) { /* just warn about it */
+            g_warning("failed to close namedpipe stream %s", err->message);
+        }
+
+        g_clear_object(&priv->gios);
+    }
+}
+
+static void spice_win_usb_driver_cleanup(SpiceWinUsbDriver *self)
+{
+    SpiceWinUsbDriverPrivate *priv = self->priv;
+
+    spice_win_usb_driver_close(self);
+
+    if (priv->cancellable_id) { /* FIXME: should be in acl_helper too ? */
+        g_cancellable_disconnect(priv->cancellable, priv->cancellable_id);
+        priv->cancellable = NULL;
+        priv->cancellable_id = 0;
+    }
+
+    g_clear_object(&priv->result);
+
+}
+
+static void spice_win_usb_driver_finalize(GObject *gobject)
+{
+    spice_win_usb_driver_cleanup(SPICE_WIN_USB_DRIVER(gobject));
+}
+
+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                                                          */
+
+static void win_usb_driver_async_result_set_cancelled(GSimpleAsyncResult *result)
+{
+    g_simple_async_result_set_error(result,
+                G_IO_ERROR, G_IO_ERROR_CANCELLED,
+                "Win USB driver installation cancelled");
+}
+
+static void win_usb_driver_cancelled_cb(GCancellable *cancellable, gpointer user_data)
+{
+    SpiceWinUsbDriver *self = SPICE_WIN_USB_DRIVER(user_data);
+    SpiceWinUsbDriverPrivate *priv = self->priv;
+
+    g_message("IN %s result=%p", __FUNCTION__, priv->result);
+    if (priv->result) {
+        win_usb_driver_async_result_set_cancelled(priv->result);
+        g_simple_async_result_complete_in_idle(priv->result);
+    }
+}
+
+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_io_stream_get_input_stream(priv->gios);
+    bytes = g_input_stream_read_finish(istream, read_res, &err);
+
+    SPICE_DEBUG("Finished reading: bytes=%ld err_exist?=%d", (long)bytes, err!=NULL);
+
+    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 (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;
+    GOutputStream *ostream;
+    SpiceWinUsbDriverPrivate *priv;
+    gsize bytes;
+    gboolean b;
+
+    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;
+
+    memset(&req, 0, sizeof(req));
+    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_io_stream_get_output_stream(priv->gios);
+
+    b = g_output_stream_write_all(ostream, &req, sizeof(req), &bytes, NULL, err);
+    SPICE_DEBUG("write_all request returned %d written bytes %u expecting %u",
+                b, bytes, sizeof(req));
+    return b;
+}
+
+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_io_stream_get_input_stream(priv->gios);
+
+    g_input_stream_read_async(istream, &priv->reply, sizeof(priv->reply),
+                              G_PRIORITY_DEFAULT, NULL,
+                              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;
+    SpiceNamedPipe *np;
+    SpiceNamedPipeConnection *npc;
+    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(SPICE_IS_USB_DEVICE(device));
+
+    priv = self->priv;
+
+    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");
+    np = spice_named_pipe_new(USB_CLERK_PIPE_NAME, FALSE, &err);
+    if (!np) {
+        g_warning("failed to create a named pipe to usbclerk %s", err->message);
+        g_simple_async_result_take_error(result, err);
+        goto failed_request;
+    }
+
+    npc = g_object_new(SPICE_TYPE_NAMED_PIPE_CONNECTION, "namedpipe", np, NULL);
+    priv->gios = G_IO_STREAM(npc);
+
+    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;
+    if (cancellable) {
+        priv->cancellable = cancellable;
+        priv->cancellable_id = g_cancellable_connect(cancellable,
+                                                     G_CALLBACK(win_usb_driver_cancelled_cb),
+                                                     self, NULL);
+    }
+
+    spice_win_usb_driver_read_reply_async(self);
+
+    return;
+
+ failed_request:
+    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..eb04707
--- /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@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
+
+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);
+
+
+#include "spice-usb-device.h"
+
+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 */

Comments

on a brief review, it seems ok, but won't a worker thread simplify the 
code and cut it by half?
few minor comments below.

Uri Lublin wrote:
> - 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.
> ---
>  gtk/Makefile.am              |   11 ++
>  gtk/usb-device-manager.c     |  143 +++++++++++++++-
>  gtk/win-usb-clerk.h          |   35 ++++
>  gtk/win-usb-driver-install.c |  379 ++++++++++++++++++++++++++++++++++++++++++
>  gtk/win-usb-driver-install.h |   98 +++++++++++
>  5 files changed, 660 insertions(+), 6 deletions(-)
>  create mode 100644 gtk/win-usb-clerk.h
>  create mode 100644 gtk/win-usb-driver-install.c
>  create mode 100644 gtk/win-usb-driver-install.h
>
> diff --git a/gtk/Makefile.am b/gtk/Makefile.am
> index 4df3ee5..646b614 100644
> --- a/gtk/Makefile.am
> +++ b/gtk/Makefile.am
> @@ -313,9 +313,20 @@ libspice_client_glib_2_0_la_LIBADD += $(GTHREAD_LIBS)
>  endif
>
>
> +NAMED_PIPE_FILES =				\
> +	controller/namedpipe.h			\
> +	controller/namedpipe.c			\
> +	controller/namedpipeconnection.c	\
> +	controller/namedpipeconnection.h	\
> +	$(NULL)
> +
>  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	\
> +	$(NAMED_PIPE_FILES)		\
>  	$(NULL)
>
>  if OS_WIN32
> diff --git a/gtk/usb-device-manager.c b/gtk/usb-device-manager.c
> index e4c8c32..8340335 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
> @@ -122,6 +123,14 @@ static void spice_usb_device_manager_uevent_cb(GUdevClient     *client,
>                                                 gpointer         user_data);
>  static void spice_usb_device_manager_add_dev(SpiceUsbDeviceManager  *self,
>                                               GUdevDevice            *udev);
> +
> +static void
> +_spice_usb_device_manager_connect_device_async(SpiceUsbDeviceManager *self,
> +                                               SpiceUsbDevice *device,
> +                                               GCancellable *cancellable,
> +                                               GAsyncReadyCallback callback,
> +                                               gpointer user_data);
> +
>  static
>  libusb_device *spice_usb_device_find_libusb_device(SpiceUsbDeviceManager *self,
>                                                     SpiceUsbDevice *device);
> @@ -129,6 +138,12 @@ libusb_device *spice_usb_device_find_libusb_device(SpiceUsbDeviceManager *self,
>
>  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,
> @@ -604,7 +619,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,
> -                                   g_object_ref(device));
> +                                   device);
>          }
>      }
>
> @@ -669,6 +684,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
> + */
> +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(SPICE_IS_USB_DEVICE(device));
> +
> +    status = spice_win_usb_driver_install_finish(installer, res, &err);
> +
> +    g_object_unref(installer);
> +
> +    if (err) {
> +        g_warning("win usb driver installation failed -- %s",
> +                  err->message);
> +        g_error_free(err);
> +        g_object_unref(device);
> +        return;
> +    }
> +
> +    if (!status) {
> +        g_warning("failed to install win usb driver (status=0)");
> +        g_object_unref(device);
> +        return;
> +    }
> +
> +    /* device is already ref'ed */
> +    _spice_usb_device_manager_connect_device_async(self,
> +                                                   device,
> +                                                   cancellable,
> +                                                   callback,
> +                                                   user_data);
> +
> +}
> +#endif
> +
>  /* ------------------------------------------------------------------ */
>  /* private api                                                        */
>
> @@ -841,11 +937,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;
>
> @@ -893,6 +990,40 @@ done:
>      g_object_unref(result);
>  }
>
> +
> +void spice_usb_device_manager_connect_device_async(SpiceUsbDeviceManager *self,
> +                                             SpiceUsbDevice *device,
> +                                             GCancellable *cancellable,
> +                                             GAsyncReadyCallback callback,
> +                                             gpointer user_data)
> +{
> +
>   
you usually like param checks ;) although
> +    device = g_object_ref(device);
> +
> +#ifdef G_OS_WIN32
> +    SpiceWinUsbDriver *installer;
> +    UsbInstallCbInfo *cbinfo;
> +
> +    installer = spice_win_usb_driver_new();
> +    cbinfo = g_new0(UsbInstallCbInfo, 1);
> +    cbinfo->manager     = self;
> +    cbinfo->device      = device;
> +    cbinfo->installer   = installer;
> +    cbinfo->cancellable = cancellable;
> +    cbinfo->callback    = callback;
> +    cbinfo->user_data   = user_data;
> +    spice_win_usb_driver_install(installer, device, NULL,
> +                                 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..9f4fdfc
> --- /dev/null
> +++ b/gtk/win-usb-driver-install.c
> @@ -0,0 +1,379 @@
> +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
> +/*
> +   Copyright (C) 2011 Red Hat, Inc.
> +
> +   Red Hat Authors:
> +   Uri Lublin <uril@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 <gio/gio.h>
> +#include "spice-util.h"
> +#include "win-usb-clerk.h"
> +#include "win-usb-driver-install.h"
> +#include "controller/namedpipe.h"
> +#include "controller/namedpipeconnection.h"
> +#include "spice-usb-device.h"
> +#include "spice-usb-device-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;
> +    gulong                cancellable_id;
> +    GIOStream             *gios;
> +    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)
> +{
> +    SpiceWinUsbDriverPrivate *priv;
> +    GError *err = NULL;
> +
> +    g_return_if_fail(self != NULL);
> +    priv = self->priv;
> +    g_return_if_fail(priv != NULL);
> +
> +    if (priv->gios) {
> +        g_io_stream_close(priv->gios, NULL, &err);
> +        if (err) { /* just warn about it */
> +            g_warning("failed to close namedpipe stream %s", err->message);
> +        }
> +
> +        g_clear_object(&priv->gios);
> +    }
> +}
> +
> +static void spice_win_usb_driver_cleanup(SpiceWinUsbDriver *self)
> +{
> +    SpiceWinUsbDriverPrivate *priv = self->priv;
> +
> +    spice_win_usb_driver_close(self);
> +
> +    if (priv->cancellable_id) { /* FIXME: should be in acl_helper too ? */
> +        g_cancellable_disconnect(priv->cancellable, priv->cancellable_id);
> +        priv->cancellable = NULL;
> +        priv->cancellable_id = 0;
> +    }
> +
> +    g_clear_object(&priv->result);
> +
> +}
> +
> +static void spice_win_usb_driver_finalize(GObject *gobject)
> +{
> +    spice_win_usb_driver_cleanup(SPICE_WIN_USB_DRIVER(gobject));
> +}
> +
> +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                                                          */
> +
> +static void win_usb_driver_async_result_set_cancelled(GSimpleAsyncResult *result)
> +{
> +    g_simple_async_result_set_error(result,
> +                G_IO_ERROR, G_IO_ERROR_CANCELLED,
> +                "Win USB driver installation cancelled");
> +}
> +
> +static void win_usb_driver_cancelled_cb(GCancellable *cancellable, gpointer user_data)
> +{
> +    SpiceWinUsbDriver *self = SPICE_WIN_USB_DRIVER(user_data);
> +    SpiceWinUsbDriverPrivate *priv = self->priv;
> +
> +    g_message("IN %s result=%p", __FUNCTION__, priv->result);
> +    if (priv->result) {
> +        win_usb_driver_async_result_set_cancelled(priv->result);
> +        g_simple_async_result_complete_in_idle(priv->result);
> +    }
> +}
> +
> +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_io_stream_get_input_stream(priv->gios);
> +    bytes = g_input_stream_read_finish(istream, read_res, &err);
> +
> +    SPICE_DEBUG("Finished reading: bytes=%ld err_exist?=%d", (long)bytes, err!=NULL);
>   
still need it?
> +
> +    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 (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;
> +    }
>   
I would have simplify / generalize the error handling above.
> +
> + 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;
> +    GOutputStream *ostream;
> +    SpiceWinUsbDriverPrivate *priv;
> +    gsize bytes;
> +    gboolean b;
>   
ret is better ;)
> +
> +    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;
> +
> +    memset(&req, 0, sizeof(req));
> +    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_io_stream_get_output_stream(priv->gios);
> +
> +    b = g_output_stream_write_all(ostream, &req, sizeof(req), &bytes, NULL, err);
> +    SPICE_DEBUG("write_all request returned %d written bytes %u expecting %u",
> +                b, bytes, sizeof(req));
> +    return b;
> +}
> +
> +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_io_stream_get_input_stream(priv->gios);
> +
> +    g_input_stream_read_async(istream, &priv->reply, sizeof(priv->reply),
> +                              G_PRIORITY_DEFAULT, NULL,
> +                              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;
> +    SpiceNamedPipe *np;
> +    SpiceNamedPipeConnection *npc;
>   
looks like complexity classes. np, np-complete etc ;)
> +    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(SPICE_IS_USB_DEVICE(device));
> +
> +    priv = self->priv;
> +
> +    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");
> +    np = spice_named_pipe_new(USB_CLERK_PIPE_NAME, FALSE, &err);
> +    if (!np) {
> +        g_warning("failed to create a named pipe to usbclerk %s", err->message);
> +        g_simple_async_result_take_error(result, err);
> +        goto failed_request;
> +    }
> +
> +    npc = g_object_new(SPICE_TYPE_NAMED_PIPE_CONNECTION, "namedpipe", np, NULL);
> +    priv->gios = G_IO_STREAM(npc);
> +
> +    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;
> +    if (cancellable) {
> +        priv->cancellable = cancellable;
> +        priv->cancellable_id = g_cancellable_connect(cancellable,
> +                                                     G_CALLBACK(win_usb_driver_cancelled_cb),
> +                                                     self, NULL);
> +    }
> +
> +    spice_win_usb_driver_read_reply_async(self);
> +
> +    return;
> +
> + failed_request:
> +    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);
>   
^^^
spaces
> +    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..eb04707
> --- /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@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
> +
> +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);
> +
> +
> +#include "spice-usb-device.h"
> +
> +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 */
>