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