1 /* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3    Copyright (C) 2012 Red Hat, Inc.
4 
5    Red Hat Authors:
6    Arnon Gilboa <agilboa@redhat.com>
7    Uri Lublin   <uril@redhat.com>
8 
9    This library is free software; you can redistribute it and/or
10    modify it under the terms of the GNU Lesser General Public
11    License as published by the Free Software Foundation; either
12    version 2.1 of the License, or (at your option) any later version.
13 
14    This library is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17    Lesser General Public License for more details.
18 
19    You should have received a copy of the GNU Lesser General Public
20    License along with this library; if not, see <http://www.gnu.org/licenses/>.
21 */
22 
23 #include "config.h"
24 
25 #include <windows.h>
26 #include <libusb.h>
27 #include "win-usb-dev.h"
28 #include "spice-marshal.h"
29 #include "spice-util.h"
30 #include "usbutil.h"
31 
32 enum {
33     PROP_0,
34     PROP_REDIRECTING
35 };
36 
37 struct _GUdevClientPrivate {
38     libusb_context *ctx;
39     GList *udev_list;
40     HWND hwnd;
41     gboolean redirecting;
42 };
43 
44 #define G_UDEV_CLIENT_WINCLASS_NAME  TEXT("G_UDEV_CLIENT")
45 
46 static void g_udev_client_initable_iface_init(GInitableIface  *iface);
47 
48 G_DEFINE_TYPE_WITH_CODE(GUdevClient, g_udev_client, G_TYPE_OBJECT,
49                         G_ADD_PRIVATE(GUdevClient)
50                         G_IMPLEMENT_INTERFACE(G_TYPE_INITABLE, g_udev_client_initable_iface_init))
51 
52 enum
53 {
54     UEVENT_SIGNAL,
55     LAST_SIGNAL,
56 };
57 
58 static guint signals[LAST_SIGNAL] = { 0, };
59 static GUdevClient *singleton = NULL;
60 
61 static LRESULT CALLBACK wnd_proc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam);
62 
63 //uncomment to debug gudev device lists.
64 //#define DEBUG_GUDEV_DEVICE_LISTS
65 
66 #ifdef DEBUG_GUDEV_DEVICE_LISTS
67 static void g_udev_device_print_list(GList *l, const gchar *msg);
68 #else
g_udev_device_print_list(GList * l,const gchar * msg)69 static void g_udev_device_print_list(GList *l, const gchar *msg) {}
70 #endif
71 static void g_udev_device_print(libusb_device *dev, const gchar *msg);
72 
73 static gboolean g_udev_skip_search(libusb_device *dev);
74 
g_udev_client_error_quark(void)75 GQuark g_udev_client_error_quark(void)
76 {
77     return g_quark_from_static_string("win-gudev-client-error-quark");
78 }
79 
g_udev_client_new(void)80 GUdevClient *g_udev_client_new(void)
81 {
82     if (singleton != NULL)
83         return g_object_ref(singleton);
84 
85     singleton = g_initable_new(G_UDEV_TYPE_CLIENT, NULL, NULL, NULL);
86     return singleton;
87 }
88 
g_udev_client_get_context(GUdevClient * client)89 libusb_context *g_udev_client_get_context(GUdevClient *client)
90 {
91     return client->priv->ctx;
92 }
93 
94 /*
95  * devs [in,out] an empty devs list in, full devs list out
96  * Returns: number-of-devices, or a negative value on error.
97  */
98 static ssize_t
g_udev_client_list_devices(GUdevClient * self,GList ** devs,GError ** err,const gchar * name)99 g_udev_client_list_devices(GUdevClient *self, GList **devs,
100                            GError **err, const gchar *name)
101 {
102     gssize rc;
103     libusb_device **lusb_list, **dev;
104     GUdevClientPrivate *priv;
105     ssize_t n;
106 
107     g_return_val_if_fail(G_UDEV_IS_CLIENT(self), -1);
108     g_return_val_if_fail(devs != NULL, -2);
109 
110     priv = self->priv;
111 
112     g_return_val_if_fail(self->priv->ctx != NULL, -3);
113 
114     rc = libusb_get_device_list(priv->ctx, &lusb_list);
115     if (rc < 0) {
116         const char *errstr = spice_usbutil_libusb_strerror(rc);
117         g_warning("%s: libusb_get_device_list failed - %s", name, errstr);
118         g_set_error(err, G_UDEV_CLIENT_ERROR, G_UDEV_CLIENT_LIBUSB_FAILED,
119                     "%s: Error getting device list from libusb: %s [%"G_GSSIZE_FORMAT"]",
120                     name, errstr, rc);
121         return -4;
122     }
123 
124     n = 0;
125     for (dev = lusb_list; *dev; dev++) {
126         if (g_udev_skip_search(*dev)) {
127             continue;
128         }
129         *devs = g_list_prepend(*devs, libusb_ref_device(*dev));
130         n++;
131     }
132     libusb_free_device_list(lusb_list, 1);
133 
134     return n;
135 }
136 
unreference_libusb_device(gpointer data)137 static void unreference_libusb_device(gpointer data)
138 {
139     libusb_unref_device((libusb_device *)data);
140 }
141 
g_udev_client_free_device_list(GList ** devs)142 static void g_udev_client_free_device_list(GList **devs)
143 {
144     g_return_if_fail(devs != NULL);
145     if (*devs) {
146         /* the unreference_libusb_device method is required as
147          * libusb_unref_device calling convention differs from glib's
148          * see 558c967ec
149          */
150         g_list_free_full(*devs, unreference_libusb_device);
151         *devs = NULL;
152     }
153 }
154 
155 
156 static gboolean
g_udev_client_initable_init(GInitable * initable,GCancellable * cancellable,GError ** err)157 g_udev_client_initable_init(GInitable *initable, GCancellable *cancellable,
158                             GError **err)
159 {
160     GUdevClient *self;
161     GUdevClientPrivate *priv;
162     WNDCLASS wcls;
163     int rc;
164 
165     g_return_val_if_fail(G_UDEV_IS_CLIENT(initable), FALSE);
166     g_return_val_if_fail(cancellable == NULL, FALSE);
167 
168     self = G_UDEV_CLIENT(initable);
169     priv = self->priv;
170 
171     rc = libusb_init(&priv->ctx);
172     if (rc < 0) {
173         const char *errstr = spice_usbutil_libusb_strerror(rc);
174         g_warning("Error initializing USB support: %s [%i]", errstr, rc);
175         g_set_error(err, G_UDEV_CLIENT_ERROR, G_UDEV_CLIENT_LIBUSB_FAILED,
176                     "Error initializing USB support: %s [%i]", errstr, rc);
177         return FALSE;
178     }
179 #ifdef G_OS_WIN32
180 #if LIBUSB_API_VERSION >= 0x01000106
181     libusb_set_option(priv->ctx, LIBUSB_OPTION_USE_USBDK);
182 #endif
183 #endif
184 
185     /* get initial device list */
186     if (g_udev_client_list_devices(self, &priv->udev_list, err, __FUNCTION__) < 0) {
187         goto g_udev_client_init_failed;
188     }
189 
190     g_udev_device_print_list(priv->udev_list, "init: first list is: ");
191 
192     /* create a hidden window */
193     memset(&wcls, 0, sizeof(wcls));
194     wcls.lpfnWndProc = wnd_proc;
195     wcls.lpszClassName = G_UDEV_CLIENT_WINCLASS_NAME;
196     if (!RegisterClass(&wcls)) {
197         DWORD e = GetLastError();
198         g_warning("RegisterClass failed , %ld", (long)e);
199         g_set_error(err, G_UDEV_CLIENT_ERROR, G_UDEV_CLIENT_WINAPI_FAILED,
200                     "RegisterClass failed: %ld", (long)e);
201         goto g_udev_client_init_failed;
202     }
203     priv->hwnd = CreateWindow(G_UDEV_CLIENT_WINCLASS_NAME,
204                               NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL);
205     if (!priv->hwnd) {
206         DWORD e = GetLastError();
207         g_warning("CreateWindow failed: %ld", (long)e);
208         g_set_error(err, G_UDEV_CLIENT_ERROR, G_UDEV_CLIENT_LIBUSB_FAILED,
209                     "CreateWindow failed: %ld", (long)e);
210         goto g_udev_client_init_failed_unreg;
211     }
212 
213     return TRUE;
214 
215  g_udev_client_init_failed_unreg:
216     UnregisterClass(G_UDEV_CLIENT_WINCLASS_NAME, NULL);
217  g_udev_client_init_failed:
218     return FALSE;
219 }
220 
g_udev_client_initable_iface_init(GInitableIface * iface)221 static void g_udev_client_initable_iface_init(GInitableIface *iface)
222 {
223     iface->init = g_udev_client_initable_init;
224 }
225 
report_one_device(gpointer data,gpointer self)226 static void report_one_device(gpointer data, gpointer self)
227 {
228     g_signal_emit(self, signals[UEVENT_SIGNAL], 0, data, TRUE);
229 }
230 
g_udev_client_report_devices(GUdevClient * self)231 void g_udev_client_report_devices(GUdevClient *self)
232 {
233     g_list_foreach(self->priv->udev_list, report_one_device, self);
234 }
235 
g_udev_client_init(GUdevClient * self)236 static void g_udev_client_init(GUdevClient *self)
237 {
238     self->priv = g_udev_client_get_instance_private(self);
239 }
240 
g_udev_client_finalize(GObject * gobject)241 static void g_udev_client_finalize(GObject *gobject)
242 {
243     GUdevClient *self = G_UDEV_CLIENT(gobject);
244     GUdevClientPrivate *priv = self->priv;
245 
246     singleton = NULL;
247     DestroyWindow(priv->hwnd);
248     UnregisterClass(G_UDEV_CLIENT_WINCLASS_NAME, NULL);
249     g_udev_client_free_device_list(&priv->udev_list);
250 
251     /* free libusb context initializing by libusb_init() */
252     g_warn_if_fail(priv->ctx != NULL);
253     libusb_exit(priv->ctx);
254 
255     /* Chain up to the parent class */
256     if (G_OBJECT_CLASS(g_udev_client_parent_class)->finalize)
257         G_OBJECT_CLASS(g_udev_client_parent_class)->finalize(gobject);
258 }
259 
g_udev_client_get_property(GObject * gobject,guint prop_id,GValue * value,GParamSpec * pspec)260 static void g_udev_client_get_property(GObject     *gobject,
261                                        guint        prop_id,
262                                        GValue      *value,
263                                        GParamSpec  *pspec)
264 {
265     GUdevClient *self = G_UDEV_CLIENT(gobject);
266     GUdevClientPrivate *priv = self->priv;
267 
268     switch (prop_id) {
269     case PROP_REDIRECTING:
270         g_value_set_boolean(value, priv->redirecting);
271         break;
272     default:
273         G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, pspec);
274         break;
275     }
276 }
277 
278 static void handle_dev_change(GUdevClient *self);
279 
g_udev_client_set_property(GObject * gobject,guint prop_id,const GValue * value,GParamSpec * pspec)280 static void g_udev_client_set_property(GObject       *gobject,
281                                        guint          prop_id,
282                                        const GValue  *value,
283                                        GParamSpec    *pspec)
284 {
285     GUdevClient *self = G_UDEV_CLIENT(gobject);
286     GUdevClientPrivate *priv = self->priv;
287     gboolean old_val;
288 
289     switch (prop_id) {
290     case PROP_REDIRECTING:
291         old_val = priv->redirecting;
292         priv->redirecting = g_value_get_boolean(value);
293         if (old_val && !priv->redirecting) {
294             /* This is a redirection completion case.
295                Inject hotplug event in case we missed device changes
296                during redirection processing. */
297             handle_dev_change(self);
298         }
299         break;
300     default:
301         G_OBJECT_WARN_INVALID_PROPERTY_ID(gobject, prop_id, pspec);
302         break;
303     }
304 }
305 
g_udev_client_class_init(GUdevClientClass * klass)306 static void g_udev_client_class_init(GUdevClientClass *klass)
307 {
308     GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
309     GParamSpec *pspec;
310 
311     gobject_class->finalize = g_udev_client_finalize;
312     gobject_class->get_property = g_udev_client_get_property;
313     gobject_class->set_property = g_udev_client_set_property;
314 
315     signals[UEVENT_SIGNAL] =
316         g_signal_new("uevent",
317                      G_OBJECT_CLASS_TYPE(klass),
318                      G_SIGNAL_RUN_FIRST,
319                      G_STRUCT_OFFSET(GUdevClientClass, uevent),
320                      NULL, NULL,
321                      g_cclosure_user_marshal_VOID__POINTER_BOOLEAN,
322                      G_TYPE_NONE,
323                      2,
324                      G_TYPE_POINTER,
325                      G_TYPE_BOOLEAN);
326 
327     /**
328     * GUdevClient::redirecting:
329     *
330     * This property indicates when a redirection operation
331     * is in progress on a device. It's set back to FALSE
332     * once the device is fully redirected to the guest.
333     */
334     pspec = g_param_spec_boolean("redirecting", "Redirecting",
335                                  "USB redirection operation is in progress",
336                                  FALSE,
337                                  G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
338 
339     g_object_class_install_property(gobject_class, PROP_REDIRECTING, pspec);
340 }
341 
342 /* comparing bus:addr and vid:pid */
compare_libusb_devices(gconstpointer a,gconstpointer b)343 static gint compare_libusb_devices(gconstpointer a, gconstpointer b)
344 {
345     libusb_device *a_dev = (libusb_device *)a;
346     libusb_device *b_dev = (libusb_device *)b;
347     struct libusb_device_descriptor a_desc, b_desc;
348     gboolean same_bus, same_addr, same_vid, same_pid;
349 
350     libusb_get_device_descriptor(a_dev, &a_desc);
351     libusb_get_device_descriptor(b_dev, &b_desc);
352 
353     same_bus = (libusb_get_bus_number(a_dev) == libusb_get_bus_number(b_dev));
354     same_addr = (libusb_get_device_address(a_dev) == libusb_get_device_address(b_dev));
355     same_vid = (a_desc.idVendor == b_desc.idVendor);
356     same_pid = (a_desc.idProduct == b_desc.idProduct);
357 
358     return (same_bus && same_addr && same_vid && same_pid) ? 0 : -1;
359 }
360 
update_device_list(GUdevClient * self,GList * new_device_list)361 static void update_device_list(GUdevClient *self, GList *new_device_list)
362 {
363     GList *dev;
364 
365     for (dev = g_list_first(new_device_list); dev != NULL; dev = g_list_next(dev)) {
366         GList *found = g_list_find_custom(self->priv->udev_list, dev->data, compare_libusb_devices);
367         if (found != NULL) {
368             /* keep old existing libusb_device in the new list, when
369                usb-dev-manager will maintain its own list of libusb_device,
370                these lists will be completely consistent */
371             libusb_device *temp = found->data;
372             found->data = dev->data;
373             dev->data = temp;
374         }
375     }
376 
377     g_udev_client_free_device_list(&self->priv->udev_list);
378     self->priv->udev_list = new_device_list;
379 }
380 
notify_dev_state_change(GUdevClient * self,GList * old_list,GList * new_list,gboolean add)381 static void notify_dev_state_change(GUdevClient *self,
382                                     GList *old_list,
383                                     GList *new_list,
384                                     gboolean add)
385 {
386     GList *dev;
387 
388     for (dev = g_list_first(old_list); dev != NULL; dev = g_list_next(dev)) {
389         GList *found = g_list_find_custom(new_list, dev->data, compare_libusb_devices);
390         if (found == NULL) {
391             g_udev_device_print(dev->data, add ? "add" : "remove");
392             g_signal_emit(self, signals[UEVENT_SIGNAL], 0, dev->data, add);
393         }
394     }
395 }
396 
handle_dev_change(GUdevClient * self)397 static void handle_dev_change(GUdevClient *self)
398 {
399     GUdevClientPrivate *priv = self->priv;
400     GError *err = NULL;
401     GList *now_devs = NULL;
402 
403     if (priv->redirecting == TRUE) {
404         /* On Windows, querying USB device list may return inconsistent results
405            if performed in parallel to redirection flow.
406            A simulated hotplug event will be injected after redirection
407            completion in order to process real device list changes that may
408            had taken place during redirection process. */
409         return;
410     }
411 
412     if(g_udev_client_list_devices(self, &now_devs, &err, __FUNCTION__) < 0) {
413         g_warning("could not retrieve device list");
414         return;
415     }
416 
417     g_udev_device_print_list(now_devs, "handle_dev_change: current list:");
418     g_udev_device_print_list(priv->udev_list, "handle_dev_change: previous list:");
419 
420     /* Unregister devices that are not present anymore */
421     notify_dev_state_change(self, priv->udev_list, now_devs, FALSE);
422 
423     /* Register newly inserted devices */
424     notify_dev_state_change(self, now_devs, priv->udev_list, TRUE);
425 
426     /* keep most recent info: free previous list, and keep current list */
427     update_device_list(self, now_devs);
428 }
429 
wnd_proc(HWND hwnd,UINT message,WPARAM wparam,LPARAM lparam)430 static LRESULT CALLBACK wnd_proc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
431 {
432     /* Only DBT_DEVNODES_CHANGED recieved */
433     if (message == WM_DEVICECHANGE) {
434         handle_dev_change(singleton);
435     }
436     return DefWindowProc(hwnd, message, wparam, lparam);
437 }
438 
439 #ifdef DEBUG_GUDEV_DEVICE_LISTS
g_udev_device_print_list(GList * l,const gchar * msg)440 static void g_udev_device_print_list(GList *l, const gchar *msg)
441 {
442     GList *it;
443 
444     for (it = g_list_first(l); it != NULL; it=g_list_next(it)) {
445         g_udev_device_print(it->data, msg);
446     }
447 }
448 #endif
449 
g_udev_device_print(libusb_device * dev,const gchar * msg)450 static void g_udev_device_print(libusb_device *dev, const gchar *msg)
451 {
452     struct libusb_device_descriptor desc;
453 
454     libusb_get_device_descriptor(dev, &desc);
455 
456     SPICE_DEBUG("%s: %d.%d 0x%04x:0x%04x class %d", msg,
457                 libusb_get_bus_number(dev),
458                 libusb_get_device_address(dev),
459                 desc.idVendor, desc.idProduct, desc.bDeviceClass);
460 }
461 
g_udev_skip_search(libusb_device * dev)462 static gboolean g_udev_skip_search(libusb_device *dev)
463 {
464     gboolean skip;
465     uint8_t addr = libusb_get_device_address(dev);
466     struct libusb_device_descriptor desc;
467 
468     libusb_get_device_descriptor(dev, &desc);
469 
470     skip = ((addr == 0xff) ||  /* root hub (HCD) */
471             (addr == 1) || /* root hub addr */
472             (desc.bDeviceClass == LIBUSB_CLASS_HUB) || /* hub*/
473             (addr == 0)); /* bad address */
474     return skip;
475 }
476