[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