[Spice-devel] [spice-gtk Win32 PATCH 4/7] Windows: Install libusb driver dynamically when usbredir'ing a USB device

Uri Lublin uril at redhat.com
Mon May 7 06:15:36 PDT 2012


- Added win-usb-driver-install.[ch]
- Added usbclerk.h

Operation (on Windows):
- After some sanity checks, just before redir'ing a USB device
  a libusb driver needs to be installed (before libusb can open/access it).
- A connection (NamedPipe) is established with usb-clerk, a libusb
  driver installation service, and 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 USB devices are being rescanned
  by libusb, and the device redir continues.

Linux operation is not changed.
---
 gtk/Makefile.am              |    2 +
 gtk/usb-device-manager.c     |   31 ++++++-
 gtk/usbclerk.h               |   35 +++++++
 gtk/win-usb-driver-install.c |  221 ++++++++++++++++++++++++++++++++++++++++++
 gtk/win-usb-driver-install.h |   47 +++++++++
 5 files changed, 335 insertions(+), 1 deletions(-)
 create mode 100644 gtk/usbclerk.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 d0e4bda..7a409a2 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -136,10 +136,12 @@ endif
 if OS_WIN32
 WIN_USB_SRCS =						\
 	win-usb-dev.c					\
+	win-usb-driver-install.c			\
 	$(NULL)

 WIN_USB_HDRS =						\
 	win-usb-dev.h					\
+	win-usb-driver-install.h			\
 	$(NULL)

 WIN_USB_LIBS = $(GTK_LIBS)
diff --git a/gtk/usb-device-manager.c b/gtk/usb-device-manager.c
index 509ed8c..b60f1f0 100644
--- a/gtk/usb-device-manager.c
+++ b/gtk/usb-device-manager.c
@@ -44,6 +44,8 @@
 #include "spice-marshal.h"
 #include "usb-device-manager-priv.h"

+#include "win-usb-driver-install.h"
+
 #include <glib/gi18n.h>

 /**
@@ -591,11 +593,13 @@ static void spice_usb_device_manager_add_dev(SpiceUsbDeviceManager  *self,
                             priv->auto_conn_filter_rules_count,
                             device, 0) == 0;

-        if (can_redirect && auto_ok)
+        if (can_redirect && auto_ok
+            && spice_win_usb_driver_install(self, device)) {
             spice_usb_device_manager_connect_device_async(self,
                                    (SpiceUsbDevice *)device, NULL,
                                    spice_usb_device_manager_auto_connect_cb,
                                    libusb_ref_device(device));
+        }
     }

     SPICE_DEBUG("device added %p", device);
@@ -659,6 +663,31 @@ static void spice_usb_device_manager_channel_connect_cb(
     g_object_unref(result);
 }

+#ifdef WIN32
+/**
+ * spice_usb_drv_install_finished:
+ * @self: #SpiceUsbDeviceManager
+ * @device: the libusb device for which a libusb driver got installed
+ * @status: status of driver-installation operation
+ *
+ * Called when an Windows libusb driver installation completed.
+ *
+ * If the driver installation was successful, continue with USB
+ * device redirection
+ */
+void spice_usb_drv_install_finished(SpiceUsbDeviceManager *self,
+     libusb_device *device, int status)
+{
+    if (status) {
+        spice_win_usb_rescan_devices(self, self->priv->context);
+        spice_usb_device_manager_connect_device_async(self,
+                (SpiceUsbDevice *)device, NULL,
+                spice_usb_device_manager_auto_connect_cb,
+                libusb_ref_device(device));
+    }
+}
+#endif
+
 /* ------------------------------------------------------------------ */
 /* private api                                                        */

diff --git a/gtk/usbclerk.h b/gtk/usbclerk.h
new file mode 100644
index 0000000..5b1e3cf
--- /dev/null
+++ b/gtk/usbclerk.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..97b1f45
--- /dev/null
+++ b/gtk/win-usb-driver-install.c
@@ -0,0 +1,221 @@
+/* -*- 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/>.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef WIN32
+#include <libusb.h>
+#include "spice-util.h"
+#include "usb-device-manager.h"
+
+#include "usbclerk.h"
+
+typedef struct InstallInfo {
+    OVERLAPPED            overlapped;
+    SpiceUsbDeviceManager *manager;
+    libusb_device         *device;
+    HANDLE                pipe;
+    USBClerkReply         ack;
+} InstallInfo;
+
+
+/*
+ * The callback function to call when usb driver installation
+ * completes ( == upon a reply from usbclerk)
+ */
+extern void spice_usb_drv_install_finished(
+     SpiceUsbDeviceManager *manager,
+     libusb_device *device,
+     int ack);
+
+/**
+ * Get vid and pid of @device by reading its device descriptor
+ *
+ * Returns: TRUE upon success FALSE upon failure
+ */
+static gboolean get_vid_pid(libusb_device *device, guint16 *pvid, guint16 *ppid)
+{
+    struct libusb_device_descriptor devdesc;
+    gint r;
+
+    g_return_val_if_fail(device != NULL && pvid != NULL && ppid != NULL, FALSE);
+    memset(&devdesc, 0, sizeof(devdesc));
+    r = libusb_get_device_descriptor(device, &devdesc);
+    if (r < 0) {
+        g_warning("libusb_get_device_descriptor failed");
+        return FALSE;
+    }
+    *pvid = devdesc.idVendor;
+    *ppid = devdesc.idProduct;
+    return TRUE;
+}
+
+
+/**
+ *
+ * Handle a reply message from usb installation service (usbclerk).
+ * Calls spice_usb_drv_install_finished.
+ */
+void WINAPI handle_usb_service_reply(DWORD err, DWORD bytes,
+                                     LPOVERLAPPED ovlp)
+{
+    InstallInfo *info = (InstallInfo*)ovlp;
+    SpiceUsbDeviceManager *manager = info->manager;
+    libusb_device *device = info->device;
+    int ack = info->ack.status;
+    int expected_bytes = sizeof(info->ack);
+
+    /* first close the pipe */
+    SPICE_DEBUG("Finished reading: closing the named-pipe\n");
+    CloseHandle(info->pipe);
+
+    if (info->ack.hdr.magic != USB_CLERK_MAGIC) {
+        g_warning("usbclerk magic mismatch: mine 0x%04x while server 0x%04x\n",
+                  USB_CLERK_MAGIC, info->ack.hdr.magic);
+        ack = 0; /* fail */
+    }
+
+    if (info->ack.hdr.version != USB_CLERK_VERSION) {
+        SPICE_DEBUG("version mismatch: mine 0x%04x while server 0x%04x\n",
+                    USB_CLERK_VERSION, info->ack.hdr.version);
+        /* For now just warn, do not fail */
+    }
+
+    if (info->ack.hdr.type != USB_CLERK_REPLY) {
+        g_warning("usbclerk message with unexpected type %d\n",
+                  info->ack.hdr.type);
+        ack = 0; /* fail */
+    }
+
+    free(info);
+
+    SPICE_DEBUG("Finished reading: err=%ld bytes=%ld ack=%d\n",
+                 err, bytes, ack);
+
+    if (err  || (bytes != expected_bytes) || !ack) {
+        g_warning("failed to read a message from pipe: "
+                  "err=%ld bytes=%ld (expected=%d) ack=%d err=%ld\n",
+                  err, bytes, expected_bytes, ack, GetLastError());
+        ack = 0; /* fail */
+    }
+
+    SPICE_DEBUG("Calling usb-device-manager to continue with usb redir\n");
+    spice_usb_drv_install_finished(manager, device, ack);
+}
+
+
+
+/**
+ *
+ * Start libusb driver installation for @device
+ *
+ * Returns: TRUE if operation was synchronous, FALSE otherwise
+ *          TRUE means @manager should immediately continue with
+ *          redir operation for @device.
+ *          FALSE means wait for another event or do not proceed.
+ */
+gboolean spice_win_usb_driver_install(SpiceUsbDeviceManager *manager,
+                                      libusb_device *device)
+{
+    DWORD bytes;
+    HANDLE pipe;
+    USBClerkDriverOp req;
+    guint16 vid, pid;
+    InstallInfo  *info = NULL;
+
+    g_return_val_if_fail(manager != NULL && device != NULL, FALSE);
+
+    if (! get_vid_pid(device, &vid, &pid)) {
+        return FALSE;
+    }
+
+    pipe = CreateFile(USB_CLERK_PIPE_NAME, GENERIC_READ | GENERIC_WRITE,
+                      0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
+    if (pipe == INVALID_HANDLE_VALUE) {
+        g_warning("Cannot open pipe %s %ld\n", USB_CLERK_PIPE_NAME, GetLastError());
+        return FALSE;
+    }
+
+    SPICE_DEBUG("Sending request to install libusb driver for %04x:%04x\n",
+                vid, pid);
+
+    info = malloc(sizeof(*info));
+    if (!info) {
+        g_warning("FAILED to allocate memory for requesting driver installation\n");
+        goto install_failed;
+    }
+
+    req.hdr.magic   = USB_CLERK_MAGIC;
+    req.hdr.version = USB_CLERK_VERSION;
+    req.hdr.type    = USB_CLERK_DRIVER_INSTALL;
+    req.hdr.size    = sizeof(req);
+    req.vid = vid;
+    req.pid = pid;
+
+   /* send request to the service */
+    if (!WriteFile(pipe, &req, sizeof(req), &bytes, NULL)) {
+        g_warning("Failed to write request to pipe err=%lu\n", GetLastError());
+        goto install_failed;
+    }
+    SPICE_DEBUG("Sent request of size %ld bytes to the service\n", bytes);
+
+
+    memset(info, 0, sizeof(*info));
+    info->manager = manager;
+    info->device  = device;
+    info->pipe    = pipe;
+    if (!ReadFileEx(pipe, &info->ack, sizeof(info->ack), &info->overlapped,
+                    handle_usb_service_reply)) {
+        g_warning("Failed to read reply from pipe err=%lu\n", GetLastError());
+        goto install_failed;
+    }
+
+    SPICE_DEBUG("started async-read for the reply from usb service\n");
+
+    return FALSE;
+
+ install_failed:
+    CloseHandle(pipe);
+    free(info);
+
+    return FALSE;
+}
+
+
+gboolean spice_win_usb_rescan_devices(SpiceUsbDeviceManager *manager,
+                                      libusb_context *ctx)
+{
+    libusb_device **dev_list = NULL;
+
+    g_return_val_if_fail(manager != NULL && ctx != NULL, FALSE);
+
+    SPICE_DEBUG("rescanning libusb devices\n");
+
+    libusb_get_device_list(ctx, &dev_list);
+    if (dev_list)
+        libusb_free_device_list(dev_list, 1);
+    return TRUE;
+}
+
+
+#endif /* WIN32  */
diff --git a/gtk/win-usb-driver-install.h b/gtk/win-usb-driver-install.h
new file mode 100644
index 0000000..e742b8c
--- /dev/null
+++ b/gtk/win-usb-driver-install.h
@@ -0,0 +1,47 @@
+/* -*- 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_INSTALLER_H
+#define SPICE_WIN_USB_DRIVER_INSTALLER_H
+
+
+#include "usb-device-manager.h"
+
+G_BEGIN_DECLS
+
+#ifdef WIN32
+/* Install libusb driver for device @device, requested by @manager */
+gboolean spice_win_usb_driver_install(SpiceUsbDeviceManager *manager,
+                                      libusb_device *device);
+
+/* rescan usb-devices upon a succseeful driver installation */
+gboolean spice_win_usb_rescan_devices(SpiceUsbDeviceManager *manager,
+                                      libusb_context *ctx);
+#else /* ! WIN32 */
+
+#define spice_win_usb_driver_install(m, d)   TRUE
+#define spice_win_usb_rescan_devices(m, c)   TRUE
+
+#endif /* WIN32 */
+
+G_END_DECLS
+
+#endif /* SPICE_WIN_USB_DRIVER_INSTALLER_H */
-- 
1.7.7.6



More information about the Spice-devel mailing list