usb 2.2.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +16 -2
  3. package/dist/usb/index.js +7 -16
  4. package/dist/usb/index.js.map +1 -1
  5. package/libusb/.private/ci-container-build.sh +70 -0
  6. package/libusb/AUTHORS +10 -0
  7. package/libusb/ChangeLog +13 -0
  8. package/libusb/android/examples/unrooted_android.c +3 -4
  9. package/libusb/appveyor.yml +4 -0
  10. package/libusb/configure.ac +14 -1
  11. package/libusb/libusb/Makefile.am +1 -1
  12. package/libusb/libusb/core.c +17 -5
  13. package/libusb/libusb/hotplug.c +3 -0
  14. package/libusb/libusb/io.c +32 -8
  15. package/libusb/libusb/libusb.h +7 -0
  16. package/libusb/libusb/libusbi.h +22 -4
  17. package/libusb/libusb/os/darwin_usb.c +77 -20
  18. package/libusb/libusb/os/linux_usbfs.c +1 -1
  19. package/libusb/libusb/os/windows_common.c +14 -3
  20. package/libusb/libusb/os/windows_common.h +2 -1
  21. package/libusb/libusb/os/windows_winusb.c +30 -3
  22. package/libusb/libusb/version.h +1 -1
  23. package/libusb/libusb/version_nano.h +1 -1
  24. package/libusb/tests/Makefile.am +12 -1
  25. package/libusb/tests/umockdev.c +1175 -0
  26. package/package.json +1 -1
  27. package/prebuilds/android-arm/node.napi.armv7.node +0 -0
  28. package/prebuilds/android-arm64/node.napi.armv8.node +0 -0
  29. package/prebuilds/darwin-x64+arm64/node.napi.node +0 -0
  30. package/prebuilds/linux-arm/node.napi.armv6.node +0 -0
  31. package/prebuilds/linux-arm/node.napi.armv7.node +0 -0
  32. package/prebuilds/linux-arm64/node.napi.armv8.node +0 -0
  33. package/prebuilds/linux-ia32/node.napi.node +0 -0
  34. package/prebuilds/linux-x64/node.napi.glibc.node +0 -0
  35. package/prebuilds/linux-x64/node.napi.musl.node +0 -0
  36. package/prebuilds/win32-ia32/node.napi.node +0 -0
  37. package/prebuilds/win32-x64/node.napi.node +0 -0
  38. package/tsc/usb/index.ts +5 -8
@@ -0,0 +1,1175 @@
1
+ /*
2
+ * libusb umockdev based tests
3
+ *
4
+ * Copyright (C) 2022 Benjamin Berg <bberg@redhat.com>
5
+ *
6
+ * This program is free software; you can redistribute it and/or
7
+ * modify it under the terms of the GNU Lesser General Public
8
+ * License as published by the Free Software Foundation; either
9
+ * version 2.1 of the License, or (at your option) any later version.
10
+ *
11
+ * This program is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
+ * Lesser General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU Lesser General Public License
17
+ * along with this program; If not, see <http://www.gnu.org/licenses/>.
18
+ */
19
+
20
+ #include "config.h"
21
+ #include <glib.h>
22
+ #include <glib/gstdio.h>
23
+ #include <unistd.h>
24
+ #include <string.h>
25
+ #include <errno.h>
26
+ #include <linux/ioctl.h>
27
+ #include <linux/usbdevice_fs.h>
28
+
29
+ #include "libusb.h"
30
+
31
+ #include "umockdev.h"
32
+
33
+ #define UNUSED_DATA __attribute__ ((unused)) gconstpointer unused_data
34
+
35
+ /* avoid leak reports inside assertions; leaking stuff on assertion failures does not matter in tests */
36
+ #if !defined(__clang__)
37
+ #pragma GCC diagnostic ignored "-Wanalyzer-malloc-leak"
38
+ #pragma GCC diagnostic ignored "-Wanalyzer-file-leak"
39
+ #endif
40
+
41
+ typedef struct {
42
+ pid_t thread;
43
+ libusb_context *ctx;
44
+ enum libusb_log_level level;
45
+ char *str;
46
+ } LogMessage;
47
+
48
+ static void
49
+ log_message_free(LogMessage *msg)
50
+ {
51
+ g_free(msg->str);
52
+ g_free(msg);
53
+ }
54
+ G_DEFINE_AUTOPTR_CLEANUP_FUNC(LogMessage, log_message_free)
55
+
56
+ typedef struct _UsbChat UsbChat;
57
+
58
+ struct _UsbChat {
59
+ gboolean submit;
60
+ gboolean reap;
61
+ UsbChat *reaps;
62
+ UsbChat *next;
63
+
64
+ /* struct usbdevfs_urb */
65
+ unsigned char type;
66
+ unsigned char endpoint;
67
+ int status;
68
+ unsigned int flags;
69
+ const unsigned char *buffer;
70
+ int buffer_length;
71
+ int actual_length;
72
+
73
+ /* <submit urb> */
74
+ UMockdevIoctlData *submit_urb;
75
+ };
76
+
77
+ typedef struct {
78
+ UMockdevTestbed *testbed;
79
+ UMockdevIoctlBase *handler;
80
+ struct libusb_context *ctx;
81
+
82
+ gchar *root_dir;
83
+ gchar *sys_dir;
84
+
85
+ gboolean libusb_log_silence;
86
+ GList *libusb_log;
87
+
88
+ UsbChat *chat;
89
+ GList *flying_urbs;
90
+ GList *discarded_urbs;
91
+
92
+ /* GMutex confuses tsan unecessarily */
93
+ pthread_mutex_t mutex;
94
+ } UMockdevTestbedFixture;
95
+
96
+ /* Global for log handler */
97
+ static UMockdevTestbedFixture *cur_fixture = NULL;
98
+
99
+ static void
100
+ log_handler(libusb_context *ctx, enum libusb_log_level level, const char *str)
101
+ {
102
+ /* May be called from different threads without synchronization! */
103
+ LogMessage *msg;
104
+ pid_t tid = gettid();
105
+
106
+ g_assert (cur_fixture != NULL);
107
+ g_assert(pthread_mutex_lock(&cur_fixture->mutex) == 0);
108
+
109
+ msg = g_new0(LogMessage, 1);
110
+ msg->ctx = ctx;
111
+ msg->level = level;
112
+ msg->str = g_strchomp (g_strdup(str));
113
+ msg->thread = tid;
114
+
115
+ if (!cur_fixture->libusb_log_silence)
116
+ g_printerr("%s\n", msg->str);
117
+
118
+ cur_fixture->libusb_log = g_list_append(cur_fixture->libusb_log, msg);
119
+ pthread_mutex_unlock(&cur_fixture->mutex);
120
+ }
121
+
122
+ static void
123
+ log_handler_null(libusb_context *ctx, enum libusb_log_level level, const char *str)
124
+ {
125
+ (void) ctx;
126
+ (void) level;
127
+ (void) str;
128
+ }
129
+
130
+ static void
131
+ clear_libusb_log(UMockdevTestbedFixture * fixture, enum libusb_log_level level)
132
+ {
133
+ g_assert(pthread_mutex_lock(&fixture->mutex) == 0);
134
+
135
+ while (fixture->libusb_log) {
136
+ LogMessage *msg = fixture->libusb_log->data;
137
+
138
+ g_assert(msg->ctx == fixture->ctx);
139
+
140
+ if (msg->level < level) {
141
+ pthread_mutex_unlock(&fixture->mutex);
142
+ return;
143
+ }
144
+
145
+ fixture->libusb_log = g_list_delete_link(fixture->libusb_log, fixture->libusb_log);
146
+ log_message_free(msg);
147
+ }
148
+ pthread_mutex_unlock(&fixture->mutex);
149
+ }
150
+
151
+ static void
152
+ assert_libusb_log_msg(UMockdevTestbedFixture * fixture, enum libusb_log_level level, const char *re)
153
+ {
154
+ g_assert(pthread_mutex_lock(&fixture->mutex) == 0);
155
+
156
+ while (fixture->libusb_log) {
157
+ g_autoptr(LogMessage) msg = NULL;
158
+
159
+ if (fixture->libusb_log == NULL)
160
+ g_error ("No level %d message found searching for %s", level, re);
161
+
162
+ msg = fixture->libusb_log->data;
163
+ fixture->libusb_log = g_list_delete_link(fixture->libusb_log, fixture->libusb_log);
164
+
165
+ if (msg->ctx != fixture->ctx)
166
+ g_error ("Saw unexpected message \"%s\" from context %p while %p was expected",
167
+ msg->str, msg->ctx, fixture->ctx);
168
+
169
+ if (msg->level == level && g_regex_match_simple(re, msg->str, 0, 0)) {
170
+ pthread_mutex_unlock(&fixture->mutex);
171
+ return;
172
+ }
173
+
174
+ /* Allow skipping INFO and DEBUG messages */
175
+ if (msg->level >= LIBUSB_LOG_LEVEL_INFO)
176
+ continue;
177
+
178
+ g_error ("Searched for \"%s\" (%d) but found \"%s\" (%d)", re, level, msg->str, msg->level);
179
+ }
180
+
181
+ pthread_mutex_unlock(&fixture->mutex);
182
+ g_error ("Searched for \"%s\" (%d) but no message matched", re, level);
183
+ }
184
+
185
+ static void
186
+ assert_libusb_no_log_msg(UMockdevTestbedFixture * fixture, enum libusb_log_level level, const char *re)
187
+ {
188
+ g_assert(pthread_mutex_lock(&fixture->mutex) == 0);
189
+
190
+ while (fixture->libusb_log) {
191
+ g_autoptr(LogMessage) msg = NULL;
192
+ gboolean matching;
193
+
194
+ msg = fixture->libusb_log->data;
195
+ fixture->libusb_log = g_list_delete_link(fixture->libusb_log, fixture->libusb_log);
196
+
197
+ g_assert(msg->ctx == fixture->ctx);
198
+
199
+ matching = (msg->level == level && g_regex_match_simple(re, msg->str, 0, 0));
200
+
201
+ /* Allow skipping INFO and DEBUG messages */
202
+ if (!matching && msg->level >= LIBUSB_LOG_LEVEL_INFO)
203
+ continue;
204
+
205
+ g_error ("Asserting \"%s\" (%d) not logged and found \"%s\" (%d)", re, level, msg->str, msg->level);
206
+ }
207
+
208
+ pthread_mutex_unlock(&fixture->mutex);
209
+ }
210
+
211
+ static void
212
+ dump_buffer(const unsigned char *buffer, int len)
213
+ {
214
+ g_autoptr(GString) line = NULL;
215
+
216
+ line = g_string_new ("");
217
+ for (gint i = 0; i < len; i++) {
218
+ g_string_append_printf(line, "%02x ", buffer[i]);
219
+ if ((i + 1) % 16 == 0) {
220
+ g_printerr(" %s\n", line->str);
221
+ g_string_set_size(line, 0);
222
+ }
223
+ }
224
+
225
+ if (line->len)
226
+ g_printerr(" %s\n", line->str);
227
+ }
228
+
229
+ static gint
230
+ cmp_ioctl_data_addr(const void *data, const void *addr)
231
+ {
232
+ return ((const UMockdevIoctlData*) data)->client_addr != (gulong) addr;
233
+ }
234
+
235
+ static gboolean
236
+ handle_ioctl_cb (UMockdevIoctlBase *handler, UMockdevIoctlClient *client, UMockdevTestbedFixture *fixture)
237
+ {
238
+ UMockdevIoctlData *ioctl_arg;
239
+ long int request;
240
+ struct usbdevfs_urb *urb;
241
+
242
+ (void) handler;
243
+
244
+ request = umockdev_ioctl_client_get_request (client);
245
+ ioctl_arg = umockdev_ioctl_client_get_arg (client);
246
+
247
+ /* NOTE: We share the address space, dereferencing pointers *will* work.
248
+ * However, to make tsan work, we still stick to the API that resolves
249
+ * the data into a local copy! */
250
+
251
+ switch (request) {
252
+ case USBDEVFS_GET_CAPABILITIES: {
253
+ g_autoptr(UMockdevIoctlData) d = NULL;
254
+ d = umockdev_ioctl_data_resolve(ioctl_arg, 0, sizeof(guint32), NULL);
255
+
256
+ *(guint32*) d->data = USBDEVFS_CAP_BULK_SCATTER_GATHER |
257
+ USBDEVFS_CAP_BULK_CONTINUATION |
258
+ USBDEVFS_CAP_NO_PACKET_SIZE_LIM |
259
+ USBDEVFS_CAP_REAP_AFTER_DISCONNECT |
260
+ USBDEVFS_CAP_ZERO_PACKET;
261
+
262
+ umockdev_ioctl_client_complete(client, 0, 0);
263
+ return TRUE;
264
+ }
265
+
266
+ case USBDEVFS_CLAIMINTERFACE:
267
+ case USBDEVFS_RELEASEINTERFACE:
268
+ case USBDEVFS_CLEAR_HALT:
269
+ case USBDEVFS_RESET:
270
+ case USBDEVFS_RESETEP:
271
+ umockdev_ioctl_client_complete(client, 0, 0);
272
+ return TRUE;
273
+
274
+ case USBDEVFS_SUBMITURB: {
275
+ g_autoptr(UMockdevIoctlData) urb_buffer = NULL;
276
+ g_autoptr(UMockdevIoctlData) urb_data = NULL;
277
+ gsize buflen;
278
+
279
+ if (!fixture->chat || !fixture->chat->submit)
280
+ return FALSE;
281
+
282
+ buflen = fixture->chat->buffer_length;
283
+ if (fixture->chat->type == USBDEVFS_URB_TYPE_CONTROL)
284
+ buflen = 8;
285
+
286
+ urb_data = umockdev_ioctl_data_resolve(ioctl_arg, 0, sizeof(struct usbdevfs_urb), NULL);
287
+ urb = (struct usbdevfs_urb*) urb_data->data;
288
+ urb_buffer = umockdev_ioctl_data_resolve(urb_data, G_STRUCT_OFFSET(struct usbdevfs_urb, buffer), urb->buffer_length, NULL);
289
+
290
+ if (fixture->chat->type == urb->type &&
291
+ fixture->chat->endpoint == urb->endpoint &&
292
+ fixture->chat->buffer_length == urb->buffer_length &&
293
+ (fixture->chat->buffer == NULL || memcmp (fixture->chat->buffer, urb_buffer->data, buflen) == 0)) {
294
+ fixture->flying_urbs = g_list_append (fixture->flying_urbs, umockdev_ioctl_data_ref(urb_data));
295
+
296
+ if (fixture->chat->reaps)
297
+ fixture->chat->reaps->submit_urb = urb_data;
298
+
299
+ if (fixture->chat->status)
300
+ umockdev_ioctl_client_complete(client, -1, -fixture->chat->status);
301
+ else
302
+ umockdev_ioctl_client_complete(client, 0, 0);
303
+
304
+ if (fixture->chat->next)
305
+ fixture->chat = fixture->chat->next;
306
+ else
307
+ fixture->chat += 1;
308
+ return TRUE;
309
+ }
310
+
311
+ /* chat message didn't match, don't accept it */
312
+ g_printerr("Could not process submit urb:\n");
313
+ g_printerr(" t: %d, ep: %d, actual_length: %d, buffer_length: %d\n",
314
+ urb->type, urb->endpoint, urb->actual_length, urb->buffer_length);
315
+ if (urb->type == USBDEVFS_URB_TYPE_CONTROL || urb->endpoint & LIBUSB_ENDPOINT_IN)
316
+ dump_buffer(urb->buffer, urb->buffer_length);
317
+ g_printerr("Looking for:\n");
318
+ g_printerr(" t: %d, ep: %d, actual_length: %d, buffer_length: %d\n",
319
+ fixture->chat->type, fixture->chat->endpoint,
320
+ fixture->chat->actual_length, fixture->chat->buffer_length);
321
+ if (fixture->chat->buffer)
322
+ dump_buffer(fixture->chat->buffer, buflen);
323
+
324
+ return FALSE;
325
+ }
326
+
327
+ case USBDEVFS_REAPURB:
328
+ case USBDEVFS_REAPURBNDELAY: {
329
+ g_autoptr(UMockdevIoctlData) urb_ptr = NULL;
330
+ g_autoptr(UMockdevIoctlData) urb_data = NULL;
331
+
332
+ if (fixture->discarded_urbs) {
333
+ urb_data = fixture->discarded_urbs->data;
334
+ urb = (struct usbdevfs_urb*) urb_data->data;
335
+ fixture->discarded_urbs = g_list_delete_link(fixture->discarded_urbs, fixture->discarded_urbs);
336
+ urb->status = -ENOENT;
337
+
338
+ urb_ptr = umockdev_ioctl_data_resolve(ioctl_arg, 0, sizeof(gpointer), NULL);
339
+ umockdev_ioctl_data_set_ptr(urb_ptr, 0, urb_data);
340
+
341
+ umockdev_ioctl_client_complete(client, 0, 0);
342
+ return TRUE;
343
+ }
344
+
345
+ if (fixture->chat && fixture->chat->reap) {
346
+ GList *l = g_list_find(fixture->flying_urbs, fixture->chat->submit_urb);
347
+
348
+ if (l) {
349
+ fixture->flying_urbs = g_list_remove_link(fixture->flying_urbs, fixture->flying_urbs);
350
+
351
+ urb_data = fixture->chat->submit_urb;
352
+ urb = (struct usbdevfs_urb*) urb_data->data;
353
+ urb->actual_length = fixture->chat->actual_length;
354
+ if (urb->type == USBDEVFS_URB_TYPE_CONTROL && urb->actual_length)
355
+ urb->actual_length -= 8;
356
+ if (fixture->chat->buffer)
357
+ memcpy(urb->buffer, fixture->chat->buffer, fixture->chat->actual_length);
358
+ urb->status = fixture->chat->status;
359
+
360
+ urb_ptr = umockdev_ioctl_data_resolve(ioctl_arg, 0, sizeof(gpointer), NULL);
361
+ umockdev_ioctl_data_set_ptr(urb_ptr, 0, urb_data);
362
+ if (fixture->chat->next)
363
+ fixture->chat = fixture->chat->next;
364
+ else
365
+ fixture->chat += 1;
366
+ umockdev_ioctl_client_complete(client, 0, 0);
367
+ return TRUE;
368
+ }
369
+ }
370
+
371
+ /* Nothing to reap */
372
+ umockdev_ioctl_client_complete(client, -1, EAGAIN);
373
+ return TRUE;
374
+ }
375
+
376
+ case USBDEVFS_DISCARDURB: {
377
+ GList *l = g_list_find_custom(fixture->flying_urbs, *(void**) ioctl_arg->data, cmp_ioctl_data_addr);
378
+
379
+ if (l) {
380
+ fixture->discarded_urbs = g_list_append(fixture->discarded_urbs, l->data);
381
+ fixture->flying_urbs = g_list_delete_link(fixture->flying_urbs, l);
382
+ umockdev_ioctl_client_complete(client, 0, 0);
383
+ } else {
384
+ umockdev_ioctl_client_complete(client, -1, EINVAL);
385
+ }
386
+
387
+ return TRUE;
388
+ }
389
+
390
+ default:
391
+ return FALSE;
392
+ }
393
+ }
394
+
395
+ static void
396
+ test_fixture_add_canon(UMockdevTestbedFixture * fixture)
397
+ {
398
+ /* Setup first, so we can be sure libusb_open works when the add uevent
399
+ * happens.
400
+ */
401
+ g_assert_cmpint(umockdev_testbed_attach_ioctl(fixture->testbed, "/dev/bus/usb/001/001", fixture->handler, NULL), ==, 1);
402
+
403
+ /* NOTE: add_device would not create a file, needed for device emulation */
404
+ /* XXX: Racy, see https://github.com/martinpitt/umockdev/issues/173 */
405
+ umockdev_testbed_add_from_string(fixture->testbed,
406
+ "P: /devices/usb1\n"
407
+ "N: bus/usb/001/001\n"
408
+ "E: SUBSYSTEM=usb\n"
409
+ "E: DRIVER=usb\n"
410
+ "E: BUSNUM=001\n"
411
+ "E: DEVNUM=001\n"
412
+ "E: DEVNAME=/dev/bus/usb/001/001\n"
413
+ "E: DEVTYPE=usb_device\n"
414
+ "A: bConfigurationValue=1\\n\n"
415
+ "A: busnum=1\\n\n"
416
+ "A: devnum=1\\n\n"
417
+ "A: bConfigurationValue=1\\n\n"
418
+ "A: speed=480\\n\n"
419
+ /* descriptor from a Canon PowerShot SX200; VID 04a9 PID 31c0 */
420
+ "H: descriptors="
421
+ "1201000200000040a904c03102000102"
422
+ "030109022700010100c0010904000003"
423
+ "06010100070581020002000705020200"
424
+ "020007058303080009\n",
425
+ NULL);
426
+ }
427
+
428
+ static void
429
+ test_fixture_setup_libusb(UMockdevTestbedFixture * fixture, int devcount)
430
+ {
431
+ libusb_device **devs = NULL;
432
+
433
+ libusb_init (&fixture->ctx);
434
+
435
+ /* Supress global log messages completely
436
+ * (though, in some tests it might be interesting to check there are no real ones).
437
+ */
438
+ libusb_set_log_cb (NULL, log_handler_null, LIBUSB_LOG_CB_GLOBAL);
439
+ libusb_set_option (fixture->ctx, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_DEBUG);
440
+ g_assert_cmpint(libusb_get_device_list(fixture->ctx, &devs), ==, devcount);
441
+ libusb_free_device_list(devs, TRUE);
442
+ libusb_set_log_cb (fixture->ctx, log_handler, LIBUSB_LOG_CB_CONTEXT);
443
+ }
444
+
445
+ static void
446
+ test_fixture_setup_common(UMockdevTestbedFixture * fixture)
447
+ {
448
+ g_assert(cur_fixture == NULL);
449
+ cur_fixture = fixture;
450
+
451
+ pthread_mutex_init(&fixture->mutex, NULL);
452
+
453
+ fixture->testbed = umockdev_testbed_new();
454
+ g_assert(fixture->testbed != NULL);
455
+ fixture->root_dir = umockdev_testbed_get_root_dir(fixture->testbed);
456
+ fixture->sys_dir = umockdev_testbed_get_sys_dir(fixture->testbed);
457
+
458
+ fixture->handler = umockdev_ioctl_base_new();
459
+ g_object_connect(fixture->handler, "signal-after::handle-ioctl", handle_ioctl_cb, fixture, NULL);
460
+ }
461
+
462
+ static void
463
+ test_fixture_setup_empty(UMockdevTestbedFixture * fixture, UNUSED_DATA)
464
+ {
465
+ test_fixture_setup_common(fixture);
466
+
467
+ test_fixture_setup_libusb(fixture, 0);
468
+ }
469
+
470
+ static void
471
+ test_fixture_setup_with_canon(UMockdevTestbedFixture * fixture, UNUSED_DATA)
472
+ {
473
+ test_fixture_setup_common(fixture);
474
+
475
+ test_fixture_add_canon(fixture);
476
+
477
+ test_fixture_setup_libusb(fixture, 1);
478
+ }
479
+
480
+ static void
481
+ test_fixture_teardown(UMockdevTestbedFixture * fixture, UNUSED_DATA)
482
+ {
483
+ g_assert(cur_fixture == fixture);
484
+
485
+ /* Abort if there are any warnings/errors in the log */
486
+ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_INFO);
487
+
488
+ if (fixture->ctx) {
489
+ libusb_device **devs = NULL;
490
+ int count = libusb_get_device_list(fixture->ctx, &devs);
491
+ libusb_free_device_list(devs, TRUE);
492
+
493
+ libusb_exit (fixture->ctx);
494
+
495
+ /* libusb_exit should result in the correct number of devices being destroyed */
496
+ for (int i = 0; i < count; i++)
497
+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_DEBUG, "libusb_unref_device");
498
+
499
+ assert_libusb_no_log_msg(fixture, LIBUSB_LOG_LEVEL_DEBUG, "libusb_unref_device");
500
+ }
501
+ libusb_set_log_cb (NULL, NULL, LIBUSB_LOG_CB_GLOBAL);
502
+ cur_fixture = NULL;
503
+
504
+ /* Abort if there are any warnings/errors in the log */
505
+ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_INFO);
506
+ fixture->ctx = NULL;
507
+ g_assert_null(fixture->libusb_log);
508
+
509
+ g_clear_object(&fixture->handler);
510
+ g_clear_object(&fixture->testbed);
511
+
512
+ /* verify that temp dir gets cleaned up properly */
513
+ g_assert(!g_file_test(fixture->root_dir, G_FILE_TEST_EXISTS));
514
+ g_free(fixture->root_dir);
515
+ g_free(fixture->sys_dir);
516
+
517
+ while (fixture->flying_urbs) {
518
+ umockdev_ioctl_data_unref (fixture->flying_urbs->data);
519
+ fixture->flying_urbs = g_list_delete_link (fixture->flying_urbs, fixture->flying_urbs);
520
+ }
521
+
522
+ pthread_mutex_destroy(&fixture->mutex);
523
+ }
524
+
525
+ static void
526
+ test_open_close(UMockdevTestbedFixture * fixture, UNUSED_DATA)
527
+ {
528
+ libusb_device **devs = NULL;
529
+ struct libusb_device_descriptor desc;
530
+ libusb_device_handle *handle = NULL;
531
+
532
+ g_assert_cmpint(libusb_get_device_list(fixture->ctx, &devs), ==, 1);
533
+ /* The linux_enumerate_device may happen from a different thread */
534
+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_DEBUG, "libusb_get_device_list");
535
+ /* We have exactly one device */
536
+ g_assert_cmpint(libusb_get_bus_number(devs[0]), ==, 1);
537
+ g_assert_cmpint(libusb_get_device_address(devs[0]), ==, 1);
538
+
539
+ /* Get/Check descriptor */
540
+ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_INFO);
541
+ libusb_get_device_descriptor (devs[0], &desc);
542
+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_DEBUG, "libusb_get_device_descriptor");
543
+ g_assert_cmpint(desc.idVendor, ==, 0x04a9);
544
+ g_assert_cmpint(desc.idProduct, ==, 0x31c0);
545
+
546
+ /* Open and close */
547
+ g_assert_cmpint(libusb_open(devs[0], &handle), ==, 0);
548
+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_DEBUG, "usbi_add_event_source");
549
+ g_assert_nonnull(handle);
550
+ libusb_close(handle);
551
+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_DEBUG, "usbi_remove_event_source");
552
+
553
+ libusb_free_device_list(devs, TRUE);
554
+
555
+ /* Open and close using vid/pid */
556
+ handle = libusb_open_device_with_vid_pid(fixture->ctx, 0x04a9, 0x31c0);
557
+ g_assert_nonnull(handle);
558
+ libusb_close(handle);
559
+ }
560
+
561
+ static void
562
+ test_implicit_default(UMockdevTestbedFixture * fixture, UNUSED_DATA)
563
+ {
564
+ libusb_device **devs = NULL;
565
+
566
+ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_INFO);
567
+ g_assert_cmpint(libusb_get_device_list(NULL, &devs), ==, 1);
568
+ libusb_free_device_list(devs, TRUE);
569
+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_ERROR, "\\[usbi_get_context\\].*implicit default");
570
+
571
+ /* Only warns once */
572
+ g_assert_cmpint(libusb_get_device_list(NULL, &devs), ==, 1);
573
+ libusb_free_device_list(devs, TRUE);
574
+ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_INFO);
575
+
576
+ libusb_init(NULL);
577
+ g_assert_cmpint(libusb_get_device_list(NULL, &devs), ==, 1);
578
+ libusb_exit(NULL);
579
+
580
+ /* We free late, causing a warning from libusb_exit. However,
581
+ * we never see this warning (i.e. test success) because it is on a
582
+ * different context.
583
+ */
584
+ libusb_free_device_list(devs, TRUE);
585
+ }
586
+
587
+ static void
588
+ test_close_flying(UMockdevTestbedFixture * fixture, UNUSED_DATA)
589
+ {
590
+ UsbChat chat[] = {
591
+ {
592
+ .submit = TRUE,
593
+ .type = USBDEVFS_URB_TYPE_BULK,
594
+ .endpoint = LIBUSB_ENDPOINT_OUT,
595
+ .buffer = (unsigned char[]) { 0x01, 0x02, 0x03, 0x04 },
596
+ .buffer_length = 4,
597
+ },
598
+ { .submit = FALSE }
599
+ };
600
+ libusb_device_handle *handle = NULL;
601
+ struct libusb_transfer *transfer = NULL;
602
+
603
+ fixture->chat = chat;
604
+
605
+ /* Open */
606
+ handle = libusb_open_device_with_vid_pid(fixture->ctx, 0x04a9, 0x31c0);
607
+ g_assert_nonnull(handle);
608
+
609
+ transfer = libusb_alloc_transfer(0);
610
+ libusb_fill_bulk_transfer(transfer,
611
+ handle,
612
+ LIBUSB_ENDPOINT_OUT,
613
+ (unsigned char*) chat[0].buffer,
614
+ chat[0].buffer_length,
615
+ NULL,
616
+ NULL,
617
+ 1);
618
+
619
+ /* Submit */
620
+ libusb_submit_transfer(transfer);
621
+
622
+ /* Closing logs fat error (two lines) */
623
+ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_DEBUG);
624
+ libusb_close(handle);
625
+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_ERROR, "\\[do_close\\] .*connected as far as we know");
626
+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_ERROR, "\\[do_close\\] .*cancellation hasn't even been scheduled");
627
+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_DEBUG, "\\[do_close\\] Removed transfer");
628
+
629
+ /* Free'ing the transfer works, and logs to the right context */
630
+ libusb_free_transfer(transfer);
631
+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_DEBUG, "\\[libusb_free_transfer\\]");
632
+ }
633
+
634
+ static void
635
+ test_close_cancelled(UMockdevTestbedFixture * fixture, UNUSED_DATA)
636
+ {
637
+ UsbChat chat[] = {
638
+ {
639
+ .submit = TRUE,
640
+ .type = USBDEVFS_URB_TYPE_BULK,
641
+ .endpoint = LIBUSB_ENDPOINT_OUT,
642
+ .buffer = (unsigned char[]) { 0x01, 0x02, 0x03, 0x04 },
643
+ .buffer_length = 4,
644
+ },
645
+ { .submit = FALSE }
646
+ };
647
+ libusb_device_handle *handle = NULL;
648
+ struct libusb_transfer *transfer = NULL;
649
+
650
+ fixture->chat = chat;
651
+
652
+ /* Open */
653
+ handle = libusb_open_device_with_vid_pid(fixture->ctx, 0x04a9, 0x31c0);
654
+ g_assert_nonnull(handle);
655
+
656
+ transfer = libusb_alloc_transfer(0);
657
+ libusb_fill_bulk_transfer(transfer,
658
+ handle,
659
+ LIBUSB_ENDPOINT_OUT,
660
+ (unsigned char*) chat[0].buffer,
661
+ chat[0].buffer_length,
662
+ NULL,
663
+ NULL,
664
+ 1);
665
+
666
+ /* Submit */
667
+ libusb_submit_transfer(transfer);
668
+ libusb_cancel_transfer(transfer);
669
+
670
+ /* Closing logs fat error (two lines) */
671
+ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_DEBUG);
672
+ libusb_close(handle);
673
+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_ERROR, "\\[do_close\\] .*connected as far as we know");
674
+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_WARNING, "\\[do_close\\] .*cancellation.*hasn't completed");
675
+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_DEBUG, "\\[do_close\\] Removed transfer");
676
+
677
+ libusb_free_transfer(transfer);
678
+ }
679
+
680
+ static void
681
+ test_ctx_destroy(UMockdevTestbedFixture * fixture, UNUSED_DATA)
682
+ {
683
+ UsbChat chat[] = {
684
+ {
685
+ .submit = TRUE,
686
+ .type = USBDEVFS_URB_TYPE_BULK,
687
+ .endpoint = LIBUSB_ENDPOINT_OUT,
688
+ .buffer = (unsigned char[]) { 0x01, 0x02, 0x03, 0x04 },
689
+ .buffer_length = 4,
690
+ },
691
+ { .submit = FALSE }
692
+ };
693
+ libusb_device_handle *handle = NULL;
694
+ struct libusb_transfer *transfer = NULL;
695
+
696
+ fixture->chat = chat;
697
+
698
+ /* Open */
699
+ handle = libusb_open_device_with_vid_pid(fixture->ctx, 0x04a9, 0x31c0);
700
+ g_assert_nonnull(handle);
701
+
702
+ transfer = libusb_alloc_transfer(0);
703
+ libusb_fill_bulk_transfer(transfer,
704
+ handle,
705
+ LIBUSB_ENDPOINT_OUT,
706
+ (unsigned char*) chat[0].buffer,
707
+ chat[0].buffer_length,
708
+ NULL,
709
+ NULL,
710
+ 1);
711
+
712
+ /* Submit */
713
+ libusb_submit_transfer(transfer);
714
+
715
+ /* Now we are evil and destroy the ctx! */
716
+ libusb_exit(fixture->ctx);
717
+
718
+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_WARNING, "\\[libusb_exit\\] device.*still referenced");
719
+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_WARNING, "\\[libusb_exit\\] application left some devices open");
720
+
721
+ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_DEBUG);
722
+ fixture->ctx = NULL;
723
+
724
+ /* XXX: Closing crashes the application as it unref's the NULL pointer */
725
+ /* libusb_close(handle); */
726
+
727
+ libusb_free_transfer(transfer);
728
+ }
729
+
730
+ static void
731
+ test_get_string_descriptor(UMockdevTestbedFixture * fixture, UNUSED_DATA)
732
+ {
733
+ unsigned char data[255] = { 0, };
734
+ libusb_device_handle *handle = NULL;
735
+ UsbChat chat[] = {
736
+ {
737
+ .submit = TRUE,
738
+ .reaps = &chat[1],
739
+ .type = USBDEVFS_URB_TYPE_CONTROL,
740
+ .buffer_length = 12, /* 8 byte out*/
741
+ .buffer = (const unsigned char*) "\x80\x06\x00\x03\x00\x00\x04\x00",
742
+ }, {
743
+ /* String with content 0x0409 (en_US) */
744
+ .reap = TRUE,
745
+ .actual_length = 12,
746
+ .buffer = (const unsigned char*) "\x80\x06\x00\x03\x00\x00\x04\x00\x04\x03\x09\x04",
747
+ }, {
748
+ .submit = TRUE,
749
+ .reaps = &chat[3],
750
+ .type = USBDEVFS_URB_TYPE_CONTROL,
751
+ .buffer_length = 263, /* 8 byte out*/
752
+ .buffer = (const unsigned char*) "\x80\x06\x01\x03\x09\x04\xff\x00",
753
+ }, {
754
+ /* 4 byte string, "ab" */
755
+ .reap = TRUE,
756
+ .actual_length = 14,
757
+ .buffer = (const unsigned char*) "\x80\x06\x01\x03\x09\x04\xff\x00\x06\x03\x61\x00\x62\x00",
758
+ }, {
759
+ .submit = TRUE,
760
+ .reaps = &chat[5],
761
+ .type = USBDEVFS_URB_TYPE_CONTROL,
762
+ .buffer_length = 12, /* 8 byte out*/
763
+ .buffer = (const unsigned char*) "\x80\x06\x00\x03\x00\x00\x04\x00",
764
+ }, {
765
+ .reap = TRUE,
766
+ .status = -ENOENT,
767
+ }, {
768
+ .submit = TRUE,
769
+ .status = -ENOENT,
770
+ .type = USBDEVFS_URB_TYPE_CONTROL,
771
+ .buffer_length = 12, /* 8 byte out*/
772
+ .buffer = (const unsigned char*) "\x80\x06\x00\x03\x00\x00\x04\x00",
773
+ }, {
774
+ .submit = FALSE,
775
+ }
776
+ };
777
+
778
+ fixture->chat = chat;
779
+
780
+ handle = libusb_open_device_with_vid_pid(fixture->ctx, 0x04a9, 0x31c0);
781
+ g_assert_nonnull(handle);
782
+
783
+ /* The chat allows us to fetch the descriptor */
784
+ g_assert_cmpint(libusb_get_string_descriptor_ascii(handle, 1, data, sizeof(data)), ==, 2);
785
+ g_assert_cmpint(memcmp(data, "ab", 2), ==, 0);
786
+ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_DEBUG);
787
+
788
+ /* Again, but the URB fails with ENOENT when reaping */
789
+ g_assert_cmpint(libusb_get_string_descriptor_ascii(handle, 1, data, sizeof(data)), ==, -1);
790
+ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_DEBUG);
791
+
792
+ /* Again, but the URB fails to submit with ENOENT */
793
+ g_assert_cmpint(libusb_get_string_descriptor_ascii(handle, 1, data, sizeof(data)), ==, -1);
794
+ assert_libusb_log_msg(fixture, LIBUSB_LOG_LEVEL_ERROR, "\\[submit_control_transfer\\] submiturb failed, errno=2");
795
+ clear_libusb_log(fixture, LIBUSB_LOG_LEVEL_DEBUG);
796
+
797
+ libusb_close(handle);
798
+ }
799
+
800
+ static void
801
+ transfer_cb_inc_user_data(struct libusb_transfer *transfer)
802
+ {
803
+ *(int*)transfer->user_data += 1;
804
+ }
805
+
806
+ static void
807
+ test_timeout(UMockdevTestbedFixture * fixture, UNUSED_DATA)
808
+ {
809
+ UsbChat chat[] = {
810
+ {
811
+ .submit = TRUE,
812
+ .type = USBDEVFS_URB_TYPE_BULK,
813
+ .endpoint = LIBUSB_ENDPOINT_OUT,
814
+ .buffer = (unsigned char[]) { 0x01, 0x02, 0x03, 0x04 },
815
+ .buffer_length = 4,
816
+ },
817
+ {
818
+ .submit = FALSE,
819
+ }
820
+ };
821
+ int completed = 0;
822
+ libusb_device_handle *handle = NULL;
823
+ struct libusb_transfer *transfer = NULL;
824
+
825
+ fixture->chat = chat;
826
+
827
+ handle = libusb_open_device_with_vid_pid(fixture->ctx, 0x04a9, 0x31c0);
828
+ g_assert_nonnull(handle);
829
+
830
+ transfer = libusb_alloc_transfer(0);
831
+ libusb_fill_bulk_transfer(transfer,
832
+ handle,
833
+ LIBUSB_ENDPOINT_OUT,
834
+ (unsigned char*) chat[0].buffer,
835
+ chat[0].buffer_length,
836
+ transfer_cb_inc_user_data,
837
+ &completed,
838
+ 10);
839
+
840
+ libusb_submit_transfer(transfer);
841
+ while (!completed) {
842
+ g_assert_cmpint(libusb_handle_events_completed(fixture->ctx, &completed), ==, 0);
843
+ /* Silence after one iteration. */
844
+ fixture->libusb_log_silence = TRUE;
845
+ }
846
+ fixture->libusb_log_silence = FALSE;
847
+
848
+ g_assert_cmpint(transfer->status, ==, LIBUSB_TRANSFER_TIMED_OUT);
849
+ libusb_free_transfer(transfer);
850
+
851
+ libusb_close(handle);
852
+ }
853
+
854
+ #define THREADED_SUBMIT_URB_SETS 64
855
+ #define THREADED_SUBMIT_URB_IN_FLIGHT 64
856
+ typedef struct {
857
+ struct libusb_transfer *transfers[THREADED_SUBMIT_URB_IN_FLIGHT * THREADED_SUBMIT_URB_SETS];
858
+ int submitted;
859
+ int completed;
860
+ int done;
861
+ UMockdevTestbedFixture *fixture;
862
+ } TestThreadedSubmit;
863
+
864
+ static gpointer
865
+ transfer_submit_all_retry(TestThreadedSubmit *data)
866
+ {
867
+ for (guint i = 0; i < G_N_ELEMENTS(data->transfers); i++) {
868
+ while (libusb_submit_transfer(data->transfers[i]) < 0) {
869
+ assert_libusb_log_msg(data->fixture, LIBUSB_LOG_LEVEL_ERROR, "submit_bulk_transfer");
870
+ continue;
871
+ }
872
+
873
+ data->submitted += 1;
874
+ }
875
+
876
+ return NULL;
877
+ }
878
+
879
+ static void
880
+ test_threaded_submit_transfer_cb(struct libusb_transfer *transfer)
881
+ {
882
+ TestThreadedSubmit *data = transfer->user_data;
883
+
884
+ /* We should only be receiving packets in the main thread */
885
+ g_assert_cmpint (getpid(), ==, gettid());
886
+
887
+ /* Check that the transfer buffer has the expected value */
888
+ g_assert_cmpint (*(int*)transfer->buffer, ==, data->completed);
889
+ data->completed += 1;
890
+
891
+ if (data->completed == G_N_ELEMENTS(data->transfers))
892
+ data->done = TRUE;
893
+ }
894
+
895
+ static void
896
+ test_threaded_submit(UMockdevTestbedFixture * fixture, UNUSED_DATA)
897
+ {
898
+ GThread *thread = NULL;
899
+ TestThreadedSubmit data = { .fixture = fixture };
900
+ UsbChat out_msg = {
901
+ .submit = TRUE,
902
+ .type = USBDEVFS_URB_TYPE_BULK,
903
+ .endpoint = LIBUSB_ENDPOINT_IN,
904
+ .buffer_length = sizeof(int),
905
+ };
906
+ UsbChat in_msg = {
907
+ .reap = TRUE,
908
+ .actual_length = 4,
909
+ };
910
+ UsbChat *c;
911
+ libusb_device_handle *handle = NULL;
912
+ int urb;
913
+
914
+ handle = libusb_open_device_with_vid_pid(fixture->ctx, 0x04a9, 0x31c0);
915
+ g_assert_nonnull(handle);
916
+
917
+ fixture->libusb_log_silence = TRUE;
918
+
919
+ c = fixture->chat = g_new0(UsbChat, G_N_ELEMENTS(data.transfers) * 2 + 1);
920
+ urb = 0;
921
+ for (int i = 0; i < THREADED_SUBMIT_URB_SETS; i++) {
922
+ for (int j = 0; j < THREADED_SUBMIT_URB_IN_FLIGHT; j++) {
923
+ c[i*2*THREADED_SUBMIT_URB_IN_FLIGHT + j] = out_msg;
924
+ c[i*2*THREADED_SUBMIT_URB_IN_FLIGHT + j].reaps = &c[(i*2+1)*THREADED_SUBMIT_URB_IN_FLIGHT + j];
925
+ c[(i*2+1)*THREADED_SUBMIT_URB_IN_FLIGHT + j] = in_msg;
926
+ c[(i*2+1)*THREADED_SUBMIT_URB_IN_FLIGHT + j].buffer = (unsigned char*) g_new0(int, 1);
927
+ *(int*) c[(i*2+1)*THREADED_SUBMIT_URB_IN_FLIGHT + j].buffer = urb;
928
+
929
+ data.transfers[urb] = libusb_alloc_transfer(0);
930
+ libusb_fill_bulk_transfer(data.transfers[urb],
931
+ handle,
932
+ LIBUSB_ENDPOINT_IN,
933
+ g_malloc(out_msg.buffer_length),
934
+ out_msg.buffer_length,
935
+ test_threaded_submit_transfer_cb,
936
+ &data,
937
+ G_MAXUINT);
938
+ data.transfers[urb]->flags = LIBUSB_TRANSFER_FREE_BUFFER | LIBUSB_TRANSFER_FREE_TRANSFER;
939
+ urb++;
940
+ }
941
+ }
942
+
943
+ thread = g_thread_new("transfer all", (GThreadFunc) transfer_submit_all_retry, &data);
944
+
945
+ while (!data.done)
946
+ g_assert_cmpint(libusb_handle_events_completed(fixture->ctx, &data.done), ==, 0);
947
+
948
+ g_thread_join(thread);
949
+
950
+ fixture->libusb_log_silence = FALSE;
951
+ libusb_close(handle);
952
+
953
+ for (int i = 0; i < 2 * THREADED_SUBMIT_URB_SETS * THREADED_SUBMIT_URB_SETS; i++)
954
+ g_clear_pointer ((void**) &c->buffer, g_free);
955
+ g_free (c);
956
+ }
957
+
958
+ static int
959
+ hotplug_count_arrival_cb(libusb_context *ctx,
960
+ libusb_device *device,
961
+ libusb_hotplug_event event,
962
+ void *user_data)
963
+ {
964
+ g_assert_cmpint(event, ==, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED);
965
+
966
+ (void) ctx;
967
+ (void) device;
968
+
969
+ *(int*) user_data += 1;
970
+
971
+ return 0;
972
+ }
973
+
974
+ #ifdef UMOCKDEV_HOTPLUG
975
+ static int
976
+ hotplug_count_removal_cb(libusb_context *ctx,
977
+ libusb_device *device,
978
+ libusb_hotplug_event event,
979
+ void *user_data)
980
+ {
981
+ g_assert_cmpint(event, ==, LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT);
982
+
983
+ (void) ctx;
984
+ (void) device;
985
+
986
+ *(int*) user_data += 1;
987
+
988
+ return 0;
989
+ }
990
+ #endif
991
+
992
+ static void
993
+ test_hotplug_enumerate(UMockdevTestbedFixture * fixture, UNUSED_DATA)
994
+ {
995
+ libusb_hotplug_callback_handle handle_enumerate;
996
+ libusb_hotplug_callback_handle handle_no_enumerate;
997
+ int event_count_enumerate = 0;
998
+ int event_count_no_enumerate = 0;
999
+ struct timeval zero_tv = { 0 };
1000
+ int r;
1001
+
1002
+ r = libusb_hotplug_register_callback(fixture->ctx,
1003
+ LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT,
1004
+ LIBUSB_HOTPLUG_ENUMERATE,
1005
+ LIBUSB_HOTPLUG_MATCH_ANY,
1006
+ LIBUSB_HOTPLUG_MATCH_ANY,
1007
+ LIBUSB_HOTPLUG_MATCH_ANY,
1008
+ hotplug_count_arrival_cb,
1009
+ &event_count_enumerate,
1010
+ &handle_enumerate);
1011
+ g_assert_cmpint(r, ==, 0);
1012
+
1013
+ r = libusb_hotplug_register_callback(fixture->ctx,
1014
+ LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT,
1015
+ 0,
1016
+ LIBUSB_HOTPLUG_MATCH_ANY,
1017
+ LIBUSB_HOTPLUG_MATCH_ANY,
1018
+ LIBUSB_HOTPLUG_MATCH_ANY,
1019
+ hotplug_count_arrival_cb,
1020
+ &event_count_no_enumerate,
1021
+ &handle_no_enumerate);
1022
+ g_assert_cmpint(r, ==, 0);
1023
+
1024
+ g_assert_cmpint(event_count_enumerate, ==, 1);
1025
+ g_assert_cmpint(event_count_no_enumerate, ==, 0);
1026
+
1027
+ libusb_handle_events_timeout(fixture->ctx, &zero_tv);
1028
+
1029
+ g_assert_cmpint(event_count_enumerate, ==, 1);
1030
+ g_assert_cmpint(event_count_no_enumerate, ==, 0);
1031
+
1032
+ libusb_hotplug_deregister_callback(fixture->ctx, handle_enumerate);
1033
+ libusb_hotplug_deregister_callback(fixture->ctx, handle_no_enumerate);
1034
+
1035
+ libusb_handle_events_timeout(fixture->ctx, &zero_tv);
1036
+
1037
+ g_assert_cmpint(event_count_enumerate, ==, 1);
1038
+ g_assert_cmpint(event_count_no_enumerate, ==, 0);
1039
+ }
1040
+
1041
+ static void
1042
+ test_hotplug_add_remove(UMockdevTestbedFixture * fixture, UNUSED_DATA)
1043
+ {
1044
+ #ifdef UMOCKDEV_HOTPLUG
1045
+ libusb_device **devs = NULL;
1046
+ libusb_hotplug_callback_handle handle_add;
1047
+ libusb_hotplug_callback_handle handle_remove;
1048
+ int event_count_add = 0;
1049
+ int event_count_remove = 0;
1050
+ struct timeval zero_tv = { 0 };
1051
+ int r;
1052
+
1053
+ r = libusb_hotplug_register_callback(fixture->ctx,
1054
+ LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED,
1055
+ LIBUSB_HOTPLUG_ENUMERATE,
1056
+ LIBUSB_HOTPLUG_MATCH_ANY,
1057
+ LIBUSB_HOTPLUG_MATCH_ANY,
1058
+ LIBUSB_HOTPLUG_MATCH_ANY,
1059
+ hotplug_count_arrival_cb,
1060
+ &event_count_add,
1061
+ &handle_add);
1062
+ g_assert_cmpint(r, ==, 0);
1063
+
1064
+ r = libusb_hotplug_register_callback(fixture->ctx,
1065
+ LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT,
1066
+ LIBUSB_HOTPLUG_ENUMERATE,
1067
+ LIBUSB_HOTPLUG_MATCH_ANY,
1068
+ LIBUSB_HOTPLUG_MATCH_ANY,
1069
+ LIBUSB_HOTPLUG_MATCH_ANY,
1070
+ hotplug_count_removal_cb,
1071
+ &event_count_remove,
1072
+ &handle_remove);
1073
+ g_assert_cmpint(r, ==, 0);
1074
+
1075
+ /* No device, even going into the mainloop will not call cb. */
1076
+ libusb_handle_events_timeout(fixture->ctx, &zero_tv);
1077
+ g_assert_cmpint(event_count_add, ==, 0);
1078
+ g_assert_cmpint(event_count_remove, ==, 0);
1079
+
1080
+ /* Add a device */
1081
+ test_fixture_add_canon(fixture);
1082
+
1083
+ /* Either the thread has picked it up already, or we do so now. */
1084
+ g_assert_cmpint(libusb_get_device_list(fixture->ctx, &devs), ==, 1);
1085
+ libusb_free_device_list(devs, TRUE);
1086
+
1087
+ /* The hotplug event is pending now, but has not yet fired. */
1088
+ g_assert_cmpint(event_count_add, ==, 0);
1089
+ g_assert_cmpint(event_count_remove, ==, 0);
1090
+
1091
+ /* Fire hotplug event. */
1092
+ libusb_handle_events_timeout(fixture->ctx, &zero_tv);
1093
+ g_assert_cmpint(event_count_add, ==, 1);
1094
+ g_assert_cmpint(event_count_remove, ==, 0);
1095
+
1096
+ umockdev_testbed_uevent(fixture->testbed, "/sys/devices/usb1", "remove");
1097
+ //umockdev_testbed_remove_device(fixture->testbed, "/devices/usb1");
1098
+
1099
+ /* Either the thread has picked it up already, or we do so now. */
1100
+ g_assert_cmpint(libusb_get_device_list(fixture->ctx, &devs), ==, 0);
1101
+ libusb_free_device_list(devs, TRUE);
1102
+
1103
+ /* The hotplug event is pending now, but has not yet fired. */
1104
+ g_assert_cmpint(event_count_add, ==, 1);
1105
+ g_assert_cmpint(event_count_remove, ==, 0);
1106
+
1107
+ /* Fire hotplug event. */
1108
+ libusb_handle_events_timeout(fixture->ctx, &zero_tv);
1109
+ g_assert_cmpint(event_count_add, ==, 1);
1110
+ g_assert_cmpint(event_count_remove, ==, 1);
1111
+
1112
+ libusb_hotplug_deregister_callback(fixture->ctx, handle_add);
1113
+ libusb_hotplug_deregister_callback(fixture->ctx, handle_remove);
1114
+ #else
1115
+ (void) fixture;
1116
+ g_test_skip("UMockdev is too old to test hotplug");
1117
+ #endif
1118
+ }
1119
+
1120
+ int
1121
+ main(int argc, char **argv)
1122
+ {
1123
+ g_test_init(&argc, &argv, NULL);
1124
+
1125
+ g_test_add("/libusb/open-close", UMockdevTestbedFixture, NULL,
1126
+ test_fixture_setup_with_canon,
1127
+ test_open_close,
1128
+ test_fixture_teardown);
1129
+
1130
+ g_test_add("/libusb/implicit-default", UMockdevTestbedFixture, NULL,
1131
+ test_fixture_setup_with_canon,
1132
+ test_implicit_default,
1133
+ test_fixture_teardown);
1134
+
1135
+ g_test_add("/libusb/close-flying", UMockdevTestbedFixture, NULL,
1136
+ test_fixture_setup_with_canon,
1137
+ test_close_flying,
1138
+ test_fixture_teardown);
1139
+ g_test_add("/libusb/close-cancelled", UMockdevTestbedFixture, NULL,
1140
+ test_fixture_setup_with_canon,
1141
+ test_close_cancelled,
1142
+ test_fixture_teardown);
1143
+
1144
+ g_test_add("/libusb/ctx-destroy", UMockdevTestbedFixture, NULL,
1145
+ test_fixture_setup_with_canon,
1146
+ test_ctx_destroy,
1147
+ test_fixture_teardown);
1148
+
1149
+ g_test_add("/libusb/string-descriptor", UMockdevTestbedFixture, NULL,
1150
+ test_fixture_setup_with_canon,
1151
+ test_get_string_descriptor,
1152
+ test_fixture_teardown);
1153
+
1154
+ g_test_add("/libusb/timeout", UMockdevTestbedFixture, NULL,
1155
+ test_fixture_setup_with_canon,
1156
+ test_timeout,
1157
+ test_fixture_teardown);
1158
+
1159
+ g_test_add("/libusb/threaded-submit", UMockdevTestbedFixture, NULL,
1160
+ test_fixture_setup_with_canon,
1161
+ test_threaded_submit,
1162
+ test_fixture_teardown);
1163
+
1164
+ g_test_add("/libusb/hotplug/enumerate", UMockdevTestbedFixture, NULL,
1165
+ test_fixture_setup_with_canon,
1166
+ test_hotplug_enumerate,
1167
+ test_fixture_teardown);
1168
+
1169
+ g_test_add("/libusb/hotplug/add-remove", UMockdevTestbedFixture, NULL,
1170
+ test_fixture_setup_empty,
1171
+ test_hotplug_add_remove,
1172
+ test_fixture_teardown);
1173
+
1174
+ return g_test_run();
1175
+ }