From: Arnon Gilboa <agilboa@xxxxxxxxxx> With this patch usb-device-manager can work with little changes on windows too. -added GUdevDevice and GUdevClient like classes -added uevent signal based on WM_DEVICECHANGE Modified-by: Uri Lublin <uril@xxxxxxxxxx> --- gtk/Makefile.am | 15 ++ gtk/usb-device-manager.c | 7 + gtk/win-usb-dev.c | 359 ++++++++++++++++++++++++++++++++++++++++++++++ gtk/win-usb-dev.h | 92 ++++++++++++ 4 files changed, 473 insertions(+), 0 deletions(-) create mode 100644 gtk/win-usb-dev.c create mode 100644 gtk/win-usb-dev.h diff --git a/gtk/Makefile.am b/gtk/Makefile.am index 69cf0ef..81150ab 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -308,6 +308,21 @@ libspice_client_glib_2_0_la_SOURCES += coroutine_gthread.c libspice_client_glib_2_0_la_LIBADD += $(GTHREAD_LIBS) endif + +WIN_USB_FILES= \ + win-usb-dev.h \ + win-usb-dev.c \ + usbclerk.h \ + $(NULL) + +if OS_WIN32 +libspice_client_glib_2_0_la_SOURCES += \ + $(WIN_USB_FILES) +else +EXTRA_DIST += $(WIN_USB_FILES) +endif + + displaysrc = \ glib-compat.h \ display/edid.h \ diff --git a/gtk/usb-device-manager.c b/gtk/usb-device-manager.c index 14b60c9..2b6ce28 100644 --- a/gtk/usb-device-manager.c +++ b/gtk/usb-device-manager.c @@ -28,7 +28,14 @@ #ifdef USE_USBREDIR #include <errno.h> #include <libusb.h> +#if defined(USE_GUDEV) #include <gudev/gudev.h> +#elif defined(G_OS_WIN32) +#include "win-usb-dev.h" +#else +#warning "Expecting one of G_OS_WIN32 and USE_GUDEV to be defined" +#endif + #include "channel-usbredir-priv.h" #include "usbredirhost.h" #include "usbutil.h" diff --git a/gtk/win-usb-dev.c b/gtk/win-usb-dev.c new file mode 100644 index 0000000..7503c41 --- /dev/null +++ b/gtk/win-usb-dev.c @@ -0,0 +1,359 @@ +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + Copyright (C) 2012 Red Hat, Inc. + + Red Hat Authors: + Arnon Gilboa <agilboa@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 <windows.h> +#include <libusb.h> +#include "win-usb-dev.h" +#include "spice-marshal.h" +#include "spice-util.h" + +#define G_UDEV_CLIENT_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE((obj), G_UDEV_TYPE_CLIENT, GUdevClientPrivate)) + +struct _GUdevClientPrivate { + libusb_device **dev_list; + ssize_t dev_list_size; + GList *udevice_list; + HWND hwnd; +}; + +#define G_UDEV_CLIENT_WINCLASS_NAME TEXT("G_UDEV_CLIENT") + +static void g_udev_client_initable_iface_init(GInitableIface *iface); + +G_DEFINE_TYPE_WITH_CODE(GUdevClient, g_udev_client, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE(G_TYPE_INITABLE, g_udev_client_initable_iface_init)); + + +typedef struct _GUdevDeviceInfo GUdevDeviceInfo; + +struct _GUdevDeviceInfo { + struct libusb_device_descriptor desc; + libusb_device *dev; + gchar sclass[4]; + gchar sbus[4]; + gchar saddr[4]; +}; + +struct _GUdevDevicePrivate +{ + GUdevDeviceInfo *usbdev; +}; + +G_DEFINE_TYPE(GUdevDevice, g_udev_device, G_TYPE_OBJECT) + +enum +{ + UEVENT_SIGNAL, + LAST_SIGNAL, +}; + +static guint signals[LAST_SIGNAL] = { 0, }; +static GUdevClient *singletone = NULL; + +static GUdevDevice *g_udev_device_new(GUdevDeviceInfo *usbdev); +static LRESULT CALLBACK wnd_proc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam); +static gboolean get_usb_dev_info(libusb_device *dev, GUdevDeviceInfo *usbdev); + +GUdevClient *g_udev_client_new(const gchar* const *subsystems) +{ + if (!singletone) { + singletone = g_initable_new(G_UDEV_TYPE_CLIENT, NULL, NULL, NULL); + return singletone; + } else { + return g_object_ref(singletone); + } +} + +static gboolean g_udev_client_initable_init(GInitable *initable, GCancellable *cancellable, + GError **err) +{ + GUdevClient *self; + GUdevClientPrivate *priv; + GUdevDeviceInfo *usbdev; + GUdevDevice *udevice; + libusb_device **dev; + WNDCLASS wcls; + int rc; + + g_return_val_if_fail(G_UDEV_IS_CLIENT(initable), FALSE); + g_return_val_if_fail(cancellable == NULL, FALSE); + + self = G_UDEV_CLIENT(initable); + priv = self->priv; + + /* FIXME: Using context != NULL results in a crash on spice_usb_device_manager_add_dev(). + * The same happens also when using SpiceUsbDeviceManagerPrivate->context and + * removing the calls to libusb_init & libusb_exit. + */ + rc = libusb_init(NULL); + g_return_val_if_fail(rc >= 0, FALSE); + + priv->dev_list_size = libusb_get_device_list(NULL, &priv->dev_list); + g_return_val_if_fail(priv->dev_list_size >= 0, FALSE); + for (dev = priv->dev_list; *dev; dev++) { + usbdev = g_new(GUdevDeviceInfo, 1); + get_usb_dev_info(*dev, usbdev); + udevice = g_udev_device_new(usbdev); + priv->udevice_list = g_list_append(priv->udevice_list, udevice); + } + + memset(&wcls, 0, sizeof(wcls)); + wcls.lpfnWndProc = wnd_proc; + wcls.lpszClassName = G_UDEV_CLIENT_WINCLASS_NAME; + g_return_val_if_fail(RegisterClass(&wcls), FALSE); + priv->hwnd = CreateWindow(G_UDEV_CLIENT_WINCLASS_NAME, + NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL); + g_return_val_if_fail(priv->hwnd, FALSE); + + return TRUE; +} + +static void g_udev_client_initable_iface_init(GInitableIface *iface) +{ + iface->init = g_udev_client_initable_init; +} + +GList *g_udev_client_query_by_subsystem(GUdevClient *self, const gchar *subsystem) +{ + GList *l = g_list_copy(self->priv->udevice_list); + g_list_foreach(l, (GFunc)g_object_ref, NULL); + return l; +} + +static void g_udev_client_init(GUdevClient *self) +{ + self->priv = G_UDEV_CLIENT_GET_PRIVATE(self); +} + +static void g_udev_client_finalize(GObject *gobject) +{ + GUdevClient *self = G_UDEV_CLIENT(gobject); + GUdevClientPrivate *priv = self->priv; + + singletone = NULL; + DestroyWindow(priv->hwnd); + g_list_free_full(priv->udevice_list, g_free); + + /* Free the device list discovered by libusb_get_device_list() and unref each of the devices */ + libusb_free_device_list(priv->dev_list, 1); + + /* Deinitialize the default context initialized by libusb_init(NULL) */ + libusb_exit(NULL); + + /* Chain up to the parent class */ + if (G_OBJECT_CLASS(g_udev_client_parent_class)->finalize) + G_OBJECT_CLASS(g_udev_client_parent_class)->finalize(gobject); +} + +static void g_udev_client_class_init(GUdevClientClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS(klass); + + gobject_class->finalize = g_udev_client_finalize; + + signals[UEVENT_SIGNAL] = + g_signal_new("uevent", + G_OBJECT_CLASS_TYPE(klass), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(GUdevClientClass, uevent), + NULL, NULL, + g_cclosure_user_marshal_VOID__BOXED_BOXED, + G_TYPE_NONE, + 2, + G_TYPE_STRING, + G_UDEV_TYPE_DEVICE); + + g_type_class_add_private(klass, sizeof(GUdevClientPrivate)); +} + +static gboolean get_usb_dev_info(libusb_device *dev, GUdevDeviceInfo *usbdev) +{ + g_return_val_if_fail(dev, FALSE); + g_return_val_if_fail(usbdev, FALSE); + + if (libusb_get_device_descriptor(dev, &usbdev->desc) < 0) { + g_warning("cannot get device descriptor %p", dev); + return FALSE; + } + usbdev->dev = libusb_ref_device(dev); + snprintf(usbdev->sclass, sizeof(usbdev->sclass), "%d", usbdev->desc.bDeviceClass); + snprintf(usbdev->sbus, sizeof(usbdev->sbus), "%d", libusb_get_bus_number(dev)); + snprintf(usbdev->saddr, sizeof(usbdev->saddr), "%d", libusb_get_device_address(dev)); + return TRUE; +} + +static void handle_dev_change(GUdevClient *self) +{ + GUdevClientPrivate *priv = self->priv; + libusb_device *changed_dev = NULL; + libusb_device **devs, **dev, **d; + GUdevDeviceInfo *usbdev; + gboolean dev_found = FALSE; + GUdevDevice *udevice; + ssize_t dev_count; + int dev_change; + + /* Assume each event stands for a single device change (at most) */ + dev_count = libusb_get_device_list(NULL, &devs); + dev_change = dev_count - priv->dev_list_size; + if (dev_change == 0) { + libusb_free_device_list(devs, 1); + return; + } + + /* Go over the longer list */ + for (dev = (dev_change > 0 ? devs : priv->dev_list); *dev && !changed_dev; dev++) { + /* Look for dev in the shorther list */ + for (d = (dev_change > 0 ? priv->dev_list : devs); *d ; d++) { + if (*d == *dev) + break; + } + if (!*d) { + /* Found a device which appears only in the longer list */ + changed_dev = *dev; + } + } + if (!changed_dev) { + g_warning("couldn't find any device change"); + goto leave; + } + + if (dev_change > 0) { + usbdev = g_new(GUdevDeviceInfo, 1); + get_usb_dev_info(changed_dev, usbdev); + udevice = g_udev_device_new(usbdev); + priv->udevice_list = g_list_append(priv->udevice_list, udevice); + SPICE_DEBUG(">>> USB device inserted: %04x:%04x (bus %s, device %s)\n", + usbdev->desc.idVendor, usbdev->desc.idProduct, usbdev->sbus, usbdev->saddr); + g_signal_emit(self, signals[UEVENT_SIGNAL], 0, "add", udevice); + } else { + dev_found = FALSE; + for (GList *it = g_list_first(priv->udevice_list); it != NULL && !dev_found; it = g_list_next(it)) { + udevice = (GUdevDevice*)it->data; + usbdev = udevice->priv->usbdev; + dev_found = (usbdev->dev == changed_dev); + } + if (!dev_found) { + goto leave; + } + SPICE_DEBUG("<<< USB device removed: %04x:%04x (bus %s, device %s)\n", + usbdev->desc.idVendor, usbdev->desc.idProduct, usbdev->sbus, usbdev->saddr); + g_signal_emit(self, signals[UEVENT_SIGNAL], 0, "remove", udevice); + priv->udevice_list = g_list_remove(priv->udevice_list, udevice); + g_object_unref(udevice); + } + +leave: + /* keep most recent info: free previous list, and keep current list */ + libusb_free_device_list(priv->dev_list, 1); + priv->dev_list = devs; + priv->dev_list_size = dev_count; +} + +static LRESULT CALLBACK wnd_proc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) +{ + /* Only DBT_DEVNODES_CHANGED recieved */ + if (message == WM_DEVICECHANGE) { + handle_dev_change(singletone); + } + return DefWindowProc(hwnd, message, wparam, lparam); +} + +/*** GUdevDevice ***/ + +static void g_udev_device_finalize(GObject *object) +{ + GUdevDevice *device = G_UDEV_DEVICE(object); + + libusb_unref_device(device->priv->usbdev->dev); + g_free(device->priv->usbdev); + if (G_OBJECT_CLASS(g_udev_device_parent_class)->finalize != NULL) + (* G_OBJECT_CLASS(g_udev_device_parent_class)->finalize)(object); +} + +static void g_udev_device_class_init(GUdevDeviceClass *klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + + gobject_class->finalize = g_udev_device_finalize; + g_type_class_add_private (klass, sizeof(GUdevDevicePrivate)); +} + +static void g_udev_device_init(GUdevDevice *device) +{ + device->priv = G_TYPE_INSTANCE_GET_PRIVATE(device, G_UDEV_TYPE_DEVICE, GUdevDevicePrivate); +} + +static GUdevDevice *g_udev_device_new(GUdevDeviceInfo *usbdev) +{ + GUdevDevice *device; + + g_return_val_if_fail(usbdev != NULL, NULL); + + device = G_UDEV_DEVICE(g_object_new(G_UDEV_TYPE_DEVICE, NULL)); + device->priv->usbdev = usbdev; + return device; +} + +const gchar *g_udev_device_get_property(GUdevDevice *udev, const gchar *property) +{ + GUdevDeviceInfo* usbdev; + + g_return_val_if_fail(udev != NULL, NULL); + g_return_val_if_fail(property != NULL, NULL); + + usbdev = udev->priv->usbdev; + g_return_val_if_fail(usbdev != NULL, NULL); + + if (strcmp(property, "BUSNUM") == 0) { + return usbdev->sbus; + } else if (strcmp(property, "DEVNUM") == 0) { + return usbdev->saddr; + } else if (strcmp(property, "DEVTYPE") == 0) { + return "usb_device"; + } + + g_warn_if_reached(); + return NULL; +} + +const gchar *g_udev_device_get_sysfs_attr(GUdevDevice *udev, const gchar *attr) +{ + GUdevDeviceInfo* usbdev; + + g_return_val_if_fail(udev != NULL, NULL); + g_return_val_if_fail(attr != NULL, NULL); + + usbdev = udev->priv->usbdev; + g_return_val_if_fail(usbdev != NULL, NULL); + + + if (strcmp(attr, "bDeviceClass") == 0) { + return usbdev->sclass; + } + g_warn_if_reached(); + return NULL; +} diff --git a/gtk/win-usb-dev.h b/gtk/win-usb-dev.h new file mode 100644 index 0000000..86fb25a --- /dev/null +++ b/gtk/win-usb-dev.h @@ -0,0 +1,92 @@ +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + Copyright (C) 2012 Red Hat, Inc. + + Red Hat Authors: + Arnon Gilboa <agilboa@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 __WIN_USB_DEV_H__ +#define __WIN_USB_DEV_H__ + +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +/* GUdevDevice */ + +#define G_UDEV_TYPE_DEVICE (g_udev_device_get_type()) +#define G_UDEV_DEVICE(o) (G_TYPE_CHECK_INSTANCE_CAST((o), G_UDEV_TYPE_DEVICE, GUdevDevice)) +#define G_UDEV_DEVICE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_UDEV_TYPE_DEVICE, GUdevDeviceClass)) +#define G_UDEV_IS_DEVICE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_UDEV_TYPE_DEVICE)) +#define G_UDEV_IS_DEVICE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE((k), G_UDEV_TYPE_DEVICE)) +#define G_UDEV_DEVICE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), G_UDEV_TYPE_DEVICE, GUdevDeviceClass)) + +typedef struct _GUdevDevice GUdevDevice; +typedef struct _GUdevDeviceClass GUdevDeviceClass; +typedef struct _GUdevDevicePrivate GUdevDevicePrivate; + +struct _GUdevDevice +{ + GObject parent; + GUdevDevicePrivate *priv; +}; + +struct _GUdevDeviceClass +{ + GObjectClass parent_class; +}; + +/* GUdevClient */ + +#define G_UDEV_TYPE_CLIENT (g_udev_client_get_type()) +#define G_UDEV_CLIENT(o) (G_TYPE_CHECK_INSTANCE_CAST((o), G_UDEV_TYPE_CLIENT, GUdevClient)) +#define G_UDEV_CLIENT_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_UDEV_TYPE_CLIENT, GUdevClientClass)) +#define G_UDEV_IS_CLIENT(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), G_UDEV_TYPE_CLIENT)) +#define G_UDEV_IS_CLIENT_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE((k), G_UDEV_TYPE_CLIENT)) +#define G_UDEV_CLIENT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), G_UDEV_TYPE_CLIENT, GUdevClientClass)) + +typedef struct _GUdevClient GUdevClient; +typedef struct _GUdevClientClass GUdevClientClass; +typedef struct _GUdevClientPrivate GUdevClientPrivate; + +struct _GUdevClient +{ + GObject parent; + + GUdevClientPrivate *priv; +}; + +struct _GUdevClientClass +{ + GObjectClass parent_class; + + /* signals */ + void (*uevent)(GUdevClient *client, const gchar *action, GUdevDevice *device); +}; + +typedef struct _SpiceUsbDeviceManager SpiceUsbDeviceManager; + +GType g_udev_client_get_type(void) G_GNUC_CONST; +GUdevClient *g_udev_client_new(const gchar* const *subsystems); +GList *g_udev_client_query_by_subsystem(GUdevClient *client, const gchar *subsystem); + +GType g_udev_device_get_type(void) G_GNUC_CONST; +const gchar *g_udev_device_get_property(GUdevDevice *udev, const gchar *property); +const gchar *g_udev_device_get_sysfs_attr(GUdevDevice *udev, const gchar *attr); + +G_END_DECLS + +#endif /* __WIN_USB_DEV_H__ */ -- 1.7.7.6 _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/spice-devel