- Added win-usb-driver-install.[ch] - Added usbclerk.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/access it). - 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 USB devices are being rescanned by libusb (to pick up the new driver - required a libusbx patch), and the device usbredir continues. Linux operation is not changed. --- gtk/Makefile.am | 2 + gtk/usb-device-manager.c | 33 ++++++- gtk/usbclerk.h | 35 +++++++ gtk/win-usb-driver-install.c | 213 ++++++++++++++++++++++++++++++++++++++++++ gtk/win-usb-driver-install.h | 39 ++++++++ 5 files changed, 321 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 81150ab..67ab9d7 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -313,6 +313,8 @@ WIN_USB_FILES= \ win-usb-dev.h \ win-usb-dev.c \ usbclerk.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 2b6ce28..c373447 100644 --- a/gtk/usb-device-manager.c +++ b/gtk/usb-device-manager.c @@ -32,6 +32,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 @@ -593,11 +594,16 @@ 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) { +#ifdef G_OS_WIN32 + spice_win_usb_driver_install(self, device); +#else spice_usb_device_manager_connect_device_async(self, (SpiceUsbDevice *)device, NULL, spice_usb_device_manager_auto_connect_cb, libusb_ref_device(device)); +#endif + } } SPICE_DEBUG("device added %p", device); @@ -661,6 +667,31 @@ static void spice_usb_device_manager_channel_connect_cb( g_object_unref(result); } +#ifdef G_OS_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..07402c7 --- /dev/null +++ b/gtk/win-usb-driver-install.c @@ -0,0 +1,213 @@ +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + Copyright (C) 2011 Red Hat, Inc. + + Red Hat Authors: + Uri Lublin <uril@xxxxxxxxxx> + + 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 <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 usbclerk, a service for driver installtion. + * 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"); + CloseHandle(info->pipe); + + if (info->ack.hdr.magic != USB_CLERK_MAGIC) { + g_warning("usbclerk magic mismatch: mine 0x%04x while server 0x%04x", + 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", + 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", + info->ack.hdr.type); + ack = 0; /* fail */ + } + + SPICE_DEBUG("Finished reading: err=%ld bytes=%ld ack=%d", + 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", + err, bytes, expected_bytes, ack, GetLastError()); + ack = 0; /* fail */ + } + + SPICE_DEBUG("Calling usb-device-manager to continue with usb redir"); + spice_usb_drv_install_finished(manager, device, ack); + + free(info); +} + +/** + * + * 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. + */ +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", USB_CLERK_PIPE_NAME, GetLastError()); + return FALSE; + } + + SPICE_DEBUG("Sending request to install libusb driver for %04x:%04x", + vid, pid); + + info = malloc(sizeof(*info)); + if (!info) { + g_warning("FAILED to allocate memory for requesting driver installation"); + 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", GetLastError()); + goto install_failed; + } + SPICE_DEBUG("Sent request of size %ld bytes to the service", 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", GetLastError()); + goto install_failed; + } + + SPICE_DEBUG("Waiting (async) for a reply from usb service"); + + return TRUE; + + 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"); + libusb_get_device_list(ctx, &dev_list); + if (dev_list) + libusb_free_device_list(dev_list, 1); + return TRUE; +} + diff --git a/gtk/win-usb-driver-install.h b/gtk/win-usb-driver-install.h new file mode 100644 index 0000000..9652cdf --- /dev/null +++ b/gtk/win-usb-driver-install.h @@ -0,0 +1,39 @@ +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + Copyright (C) 2011 Red Hat, Inc. + + Red Hat Authors: + Uri Lublin <uril@xxxxxxxxxx> + + 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 + +/* 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); + +G_END_DECLS + +#endif /* SPICE_WIN_USB_DRIVER_INSTALLER_H */ -- 1.7.7.6 _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/spice-devel