usb 2.17.0 → 2.18.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 (94) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +14 -0
  3. package/binding.gyp +2 -9
  4. package/dist/usb/bindings.d.ts +27 -2
  5. package/dist/usb/bindings.js.map +1 -1
  6. package/dist/usb/index.d.ts +0 -29
  7. package/dist/usb/index.js +4 -18
  8. package/dist/usb/index.js.map +1 -1
  9. package/libusb/.clang-tidy +5 -3
  10. package/libusb/.private/ci-build.sh +5 -1
  11. package/libusb/AUTHORS +14 -0
  12. package/libusb/ChangeLog +15 -2
  13. package/libusb/README +8 -5
  14. package/libusb/Xcode/libusb.xcodeproj/project.pbxproj +4 -0
  15. package/libusb/configure.ac +12 -2
  16. package/libusb/examples/hotplugtest.c +19 -11
  17. package/libusb/examples/listdevs.c +41 -3
  18. package/libusb/examples/xusb.c +6 -1
  19. package/libusb/libusb/Makefile.am +4 -0
  20. package/libusb/libusb/core.c +175 -14
  21. package/libusb/libusb/descriptor.c +163 -14
  22. package/libusb/libusb/io.c +7 -3
  23. package/libusb/libusb/libusb-1.0.def +10 -0
  24. package/libusb/libusb/libusb.h +59 -9
  25. package/libusb/libusb/libusbi.h +89 -25
  26. package/libusb/libusb/os/darwin_usb.c +126 -46
  27. package/libusb/libusb/os/darwin_usb.h +10 -8
  28. package/libusb/libusb/os/emscripten_webusb.cpp +31 -10
  29. package/libusb/libusb/os/haiku_usb_raw.cpp +4 -0
  30. package/libusb/libusb/os/linux_usbfs.c +73 -25
  31. package/libusb/libusb/os/netbsd_usb.c +2 -0
  32. package/libusb/libusb/os/openbsd_usb.c +2 -0
  33. package/libusb/libusb/os/sunos_usb.c +2 -0
  34. package/libusb/libusb/os/threads_posix.c +3 -3
  35. package/libusb/libusb/os/threads_posix.h +8 -2
  36. package/libusb/libusb/os/threads_windows.h +2 -1
  37. package/libusb/libusb/os/windows_common.c +86 -1
  38. package/libusb/libusb/os/windows_common.h +20 -1
  39. package/libusb/libusb/os/windows_hotplug.c +321 -0
  40. package/libusb/libusb/os/windows_hotplug.h +28 -0
  41. package/libusb/libusb/os/windows_usbdk.c +16 -8
  42. package/libusb/libusb/os/windows_winusb.c +753 -41
  43. package/libusb/libusb/os/windows_winusb.h +11 -6
  44. package/libusb/libusb/version.h +1 -1
  45. package/libusb/libusb/version_nano.h +1 -1
  46. package/libusb/msvc/Base.props +1 -1
  47. package/libusb/msvc/Configuration.Base.props +2 -1
  48. package/libusb/msvc/Configuration.DynamicLibrary.props +12 -0
  49. package/libusb/msvc/ProjectConfigurations.Base.props +69 -16
  50. package/libusb/msvc/build_all.ps1 +2 -2
  51. package/libusb/msvc/config.h +4 -0
  52. package/libusb/msvc/getopt/bits/getopt_core.h +96 -0
  53. package/libusb/msvc/getopt/bits/getopt_ext.h +77 -0
  54. package/libusb/msvc/getopt/features.h +21 -0
  55. package/libusb/msvc/getopt/getopt.c +456 -705
  56. package/libusb/msvc/getopt/getopt.h +16 -158
  57. package/libusb/msvc/getopt/getopt1.c +40 -69
  58. package/libusb/msvc/getopt/getopt_int.h +118 -0
  59. package/libusb/msvc/getopt/gettext.h +7 -0
  60. package/libusb/msvc/getopt/unistd.h +5 -0
  61. package/libusb/msvc/getopt.vcxproj +11 -4
  62. package/libusb/msvc/libusb.sln +515 -268
  63. package/libusb/msvc/libusb_dll.vcxproj +2 -0
  64. package/libusb/msvc/libusb_static.vcxproj +2 -0
  65. package/libusb/msvc/xusb.vcxproj +1 -1
  66. package/libusb/tests/Makefile.am +10 -1
  67. package/libusb/tests/fuzz/corpus/bos/min.bos +0 -0
  68. package/libusb/tests/fuzz/corpus/descriptor_parsers/min_valid_config.bin +0 -0
  69. package/libusb/tests/fuzz/corpus/descriptor_parsers/regression_bug_a_endpoint_null.bin +0 -0
  70. package/libusb/tests/fuzz/corpus/descriptor_parsers/regression_bug_b_iad_oob.bin +0 -0
  71. package/libusb/tests/fuzz/fuzz_bos_descriptor.c +49 -0
  72. package/libusb/tests/fuzz/fuzz_descriptor_parsers.c +83 -0
  73. package/libusb/tests/stress_mt.c +2 -1
  74. package/libusb/tests/webusb-test-shim/index.js +6 -5
  75. package/libusb.gypi +5 -0
  76. package/package.json +1 -1
  77. package/prebuilds/android-arm/node.napi.armv7.node +0 -0
  78. package/prebuilds/android-arm64/node.napi.armv8.node +0 -0
  79. package/prebuilds/darwin-x64+arm64/node.napi.node +0 -0
  80. package/prebuilds/linux-arm/node.napi.armv6.node +0 -0
  81. package/prebuilds/linux-arm/node.napi.armv7.node +0 -0
  82. package/prebuilds/linux-arm64/node.napi.armv8.node +0 -0
  83. package/prebuilds/linux-ia32/node.napi.node +0 -0
  84. package/prebuilds/linux-x64/node.napi.glibc.node +0 -0
  85. package/prebuilds/linux-x64/node.napi.musl.node +0 -0
  86. package/prebuilds/win32-arm64/node.napi.node +0 -0
  87. package/prebuilds/win32-ia32/node.napi.node +0 -0
  88. package/prebuilds/win32-x64/node.napi.node +0 -0
  89. package/src/{hotplug/libusb.cc → hotplug.cc} +2 -3
  90. package/src/{hotplug/hotplug.h → hotplug.h} +2 -6
  91. package/src/node_usb.cc +3 -3
  92. package/test/usb.coffee +4 -4
  93. package/test/webusb.coffee +22 -12
  94. package/src/hotplug/windows.cc +0 -168
@@ -0,0 +1,321 @@
1
+ /*
2
+ * windows hotplug backend for libusb 1.0
3
+ * Copyright © 2024 Sylvain Fasel <sylvain@sonatique.net>
4
+ *
5
+ * This library is free software; you can redistribute it and/or
6
+ * modify it under the terms of the GNU Lesser General Public
7
+ * License as published by the Free Software Foundation; either
8
+ * version 2.1 of the License, or (at your option) any later version.
9
+ *
10
+ * This library is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
+ * Lesser General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU Lesser General Public
16
+ * License along with this library; if not, write to the Free Software
17
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
+ */
19
+
20
+ #include "libusbi.h"
21
+ #include "threads_windows.h"
22
+
23
+ #include "windows_common.h"
24
+ #include "windows_hotplug.h"
25
+
26
+ #include <stdio.h>
27
+ #include <dbt.h>
28
+
29
+ /* The Windows Hotplug system is a two steps process.
30
+ * 1. We create a hidden window and listen for DBT_DEVNODES_CHANGED, which Windows
31
+ * broadcasts to all top-level windows whenever the device tree changes (no
32
+ * registration required). Multiple rapid events (e.g. a hub with many children)
33
+ * are coalesced via a short debounce timer so we only scan once the burst settles.
34
+ * A maximum delay ceiling guarantees a scan fires within a bounded time even
35
+ * during sustained bursts, preventing unbounded latency.
36
+ * 2. Upon timer expiry, we snapshot the current device list, run a full re-enumeration
37
+ * via the Windows backend, then diff the result: newly found devices that have been
38
+ * successfully initialized generate DEVICE_ARRIVED events; devices that were not
39
+ * encountered by the re-enumeration (physically removed) generate DEVICE_LEFT events.
40
+ */
41
+
42
+ #define HOTPLUG_DEBOUNCE_TIMER_ID 1
43
+ #define HOTPLUG_DEBOUNCE_MS 10
44
+ #define HOTPLUG_DEBOUNCE_MAX_DELAY_MS 100
45
+
46
+ static ULONGLONG first_debounce_tick;
47
+ static HWND windows_event_hwnd;
48
+ static HANDLE windows_event_thread_handle;
49
+ static DWORD WINAPI windows_event_thread_main(LPVOID lpParam);
50
+ static LRESULT CALLBACK windows_proc_callback(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
51
+
52
+ #define log_error(operation) do { \
53
+ usbi_err(NULL, "%s failed with error: %s", operation, windows_error_str(0)); \
54
+ } while (0)
55
+
56
+ int windows_start_event_monitor(void)
57
+ {
58
+ windows_event_thread_handle = CreateThread(
59
+ NULL, // Default security descriptor
60
+ 0, // Default stack size
61
+ windows_event_thread_main,
62
+ NULL, // No parameters to pass to the thread
63
+ 0, // Start immediately
64
+ NULL // No need to keep track of thread ID
65
+ );
66
+
67
+ if (windows_event_thread_handle == NULL)
68
+ {
69
+ log_error("CreateThread");
70
+ return LIBUSB_ERROR_OTHER;
71
+ }
72
+
73
+ return LIBUSB_SUCCESS;
74
+ }
75
+
76
+ int windows_stop_event_monitor(void)
77
+ {
78
+ if (windows_event_hwnd == NULL)
79
+ {
80
+ return LIBUSB_SUCCESS;
81
+ }
82
+
83
+ if (!SUCCEEDED(SendMessage(windows_event_hwnd, WM_CLOSE, 0, 0)))
84
+ {
85
+ log_error("SendMessage");
86
+ return LIBUSB_ERROR_OTHER;
87
+ }
88
+
89
+ if (WaitForSingleObject(windows_event_thread_handle, INFINITE) != WAIT_OBJECT_0)
90
+ {
91
+ log_error("WaitForSingleObject");
92
+ return LIBUSB_ERROR_OTHER;
93
+ }
94
+
95
+ if (!CloseHandle(windows_event_thread_handle))
96
+ {
97
+ log_error("CloseHandle");
98
+ return LIBUSB_ERROR_OTHER;
99
+ }
100
+
101
+ return LIBUSB_SUCCESS;
102
+ }
103
+
104
+ static int windows_get_device_list(struct libusb_context *ctx)
105
+ {
106
+ // note: context device list is protected by active_contexts_lock
107
+ return ((struct windows_context_priv *)usbi_get_context_priv(ctx))->backend->get_device_list(ctx, NULL);
108
+ }
109
+
110
+ void windows_initial_scan_devices(struct libusb_context *ctx)
111
+ {
112
+ usbi_mutex_static_lock(&active_contexts_lock);
113
+
114
+ const int ret = windows_get_device_list(ctx);
115
+ if (ret != LIBUSB_SUCCESS)
116
+ {
117
+ usbi_err(ctx, "hotplug failed to retrieve initial list with error: %s", libusb_error_name(ret));
118
+ }
119
+ usbi_mutex_static_unlock(&active_contexts_lock);
120
+ }
121
+
122
+ static void windows_refresh_device_list(struct libusb_context *ctx)
123
+ {
124
+ struct libusb_device *dev, *next_dev;
125
+
126
+ // Step 1: clear seen_during_scan so the scan can mark which devices are still
127
+ // physically present, and set seen_before_scan so we can distinguish newly
128
+ // created devices (which start with seen_before_scan=false via calloc).
129
+ for_each_device_safe(ctx, dev, next_dev)
130
+ {
131
+ struct winusb_device_priv *priv = (struct winusb_device_priv *)usbi_get_device_priv(dev);
132
+ priv->seen_during_scan = false;
133
+ priv->seen_before_scan = true;
134
+ }
135
+
136
+ // Step 2: re-enumerate — winusb_get_device_list attaches newly-arrived devices
137
+ // and sets seen_during_scan=true for every device it physically encounters.
138
+ // seen_before_scan is untouched and will be left in default state (false) for newly created devices, allowing us to identify them in the next step.
139
+ const int ret = windows_get_device_list(ctx);
140
+ if (ret != LIBUSB_SUCCESS)
141
+ {
142
+ usbi_err(ctx, "hotplug failed to retrieve current list with error: %s", libusb_error_name(ret));
143
+ return;
144
+ }
145
+
146
+ // Step 3: diff old vs new.
147
+ for_each_device_safe(ctx, dev, next_dev)
148
+ {
149
+ struct winusb_device_priv *priv = (struct winusb_device_priv *)usbi_get_device_priv(dev);
150
+
151
+ if (!priv->seen_during_scan)
152
+ {
153
+ // Not encountered by the scan: device was physically removed.
154
+ if (priv->initialized)
155
+ {
156
+ usbi_disconnect_device(dev); // fires DEVICE_LEFT
157
+ }
158
+ else
159
+ {
160
+ usbi_detach_device(dev);
161
+ // No DEVICE_LEFT message is posted for uninitialized
162
+ // devices, so no message handler will drop the initial
163
+ // ref. We must drop it here to avoid a leak.
164
+ libusb_unref_device(dev);
165
+ }
166
+ }
167
+ else if (!priv->seen_before_scan)
168
+ {
169
+ if (priv->initialized)
170
+ {
171
+ usbi_hotplug_notification(ctx, dev, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED);
172
+ }
173
+ }
174
+ }
175
+ }
176
+
177
+ static void windows_refresh_device_list_for_all_ctx(void)
178
+ {
179
+ usbi_mutex_static_lock(&active_contexts_lock);
180
+
181
+ struct libusb_context *ctx;
182
+ for_each_context(ctx)
183
+ {
184
+ windows_refresh_device_list(ctx);
185
+ }
186
+
187
+ usbi_mutex_static_unlock(&active_contexts_lock);
188
+ }
189
+
190
+ #define WND_CLASS_NAME TEXT("libusb-1.0-windows-hotplug")
191
+
192
+ static bool init_wnd_class(void)
193
+ {
194
+ WNDCLASS wndClass = { 0 };
195
+ wndClass.lpfnWndProc = windows_proc_callback;
196
+ wndClass.hInstance = GetModuleHandle(NULL);
197
+ wndClass.lpszClassName = WND_CLASS_NAME;
198
+
199
+ if (!RegisterClass(&wndClass))
200
+ {
201
+ log_error("event thread: RegisterClass");
202
+ return false;
203
+ }
204
+
205
+ return true;
206
+ }
207
+
208
+ static DWORD WINAPI windows_event_thread_main(LPVOID lpParam)
209
+ {
210
+ UNUSED(lpParam);
211
+
212
+ usbi_dbg(NULL, "windows event thread entering");
213
+
214
+ if (!init_wnd_class())
215
+ {
216
+ return (DWORD)-1;
217
+ }
218
+
219
+ windows_event_hwnd = CreateWindow(
220
+ WND_CLASS_NAME,
221
+ TEXT(""),
222
+ 0,
223
+ 0, 0, 0, 0,
224
+ NULL, NULL,
225
+ GetModuleHandle(NULL),
226
+ NULL);
227
+
228
+ if (windows_event_hwnd == NULL)
229
+ {
230
+ log_error("event thread: CreateWindow");
231
+ return (DWORD)-1;
232
+ }
233
+
234
+ MSG msg;
235
+ BOOL ret_val;
236
+
237
+ while ((ret_val = GetMessage(&msg, windows_event_hwnd, 0, 0)) != 0)
238
+ {
239
+ if (ret_val == -1)
240
+ {
241
+ log_error("event thread: GetMessage");
242
+ break;
243
+ }
244
+
245
+ if (!SUCCEEDED(TranslateMessage(&msg)))
246
+ {
247
+ log_error("event thread: TranslateMessage");
248
+ }
249
+
250
+ if (!SUCCEEDED(DispatchMessage(&msg)))
251
+ {
252
+ log_error("event thread: DispatchMessage");
253
+ }
254
+ }
255
+
256
+ usbi_dbg(NULL, "windows event thread exiting");
257
+
258
+ return 0;
259
+ }
260
+
261
+ static LRESULT CALLBACK windows_proc_callback(
262
+ HWND hwnd,
263
+ UINT message,
264
+ WPARAM wParam,
265
+ LPARAM lParam)
266
+ {
267
+ switch (message)
268
+ {
269
+ case WM_DEVICECHANGE:
270
+ if (wParam == DBT_DEVNODES_CHANGED)
271
+ {
272
+ if (first_debounce_tick == 0)
273
+ {
274
+ // First event in a new burst — record the time and start the debounce timer.
275
+ first_debounce_tick = GetTickCount64();
276
+ SetTimer(hwnd, HOTPLUG_DEBOUNCE_TIMER_ID, HOTPLUG_DEBOUNCE_MS, NULL);
277
+ }
278
+ else if (GetTickCount64() - first_debounce_tick >= HOTPLUG_DEBOUNCE_MAX_DELAY_MS)
279
+ {
280
+ // Maximum delay reached — force an immediate scan so devices
281
+ // are not invisible for the entire duration of a sustained burst.
282
+ KillTimer(hwnd, HOTPLUG_DEBOUNCE_TIMER_ID);
283
+ first_debounce_tick = 0;
284
+ windows_refresh_device_list_for_all_ctx();
285
+ }
286
+ else
287
+ {
288
+ // Still within the max-delay window — reset the debounce timer.
289
+ SetTimer(hwnd, HOTPLUG_DEBOUNCE_TIMER_ID, HOTPLUG_DEBOUNCE_MS, NULL);
290
+ }
291
+ return TRUE;
292
+ }
293
+ return DefWindowProc(hwnd, message, wParam, lParam);
294
+
295
+ case WM_TIMER:
296
+ if (wParam == HOTPLUG_DEBOUNCE_TIMER_ID)
297
+ {
298
+ KillTimer(hwnd, HOTPLUG_DEBOUNCE_TIMER_ID);
299
+ first_debounce_tick = 0;
300
+ windows_refresh_device_list_for_all_ctx();
301
+ return 0;
302
+ }
303
+ return DefWindowProc(hwnd, message, wParam, lParam);
304
+
305
+ case WM_CLOSE:
306
+ KillTimer(hwnd, HOTPLUG_DEBOUNCE_TIMER_ID);
307
+ first_debounce_tick = 0;
308
+ if (!DestroyWindow(hwnd))
309
+ {
310
+ log_error("DestroyWindow");
311
+ }
312
+ return 0;
313
+
314
+ case WM_DESTROY:
315
+ PostQuitMessage(0);
316
+ return 0;
317
+
318
+ default:
319
+ return DefWindowProc(hwnd, message, wParam, lParam);
320
+ }
321
+ }
@@ -0,0 +1,28 @@
1
+ /*
2
+ * windows hotplug backend for libusb 1.0
3
+ * Copyright © 2024 Sylvain Fasel <sylvain@sonatique.net>
4
+ *
5
+ * This library is free software; you can redistribute it and/or
6
+ * modify it under the terms of the GNU Lesser General Public
7
+ * License as published by the Free Software Foundation; either
8
+ * version 2.1 of the License, or (at your option) any later version.
9
+ *
10
+ * This library is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
+ * Lesser General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU Lesser General Public
16
+ * License along with this library; if not, write to the Free Software
17
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
+ */
19
+
20
+ #ifndef WINDOWS_HOTPLUG_H
21
+ #define WINDOWS_HOTPLUG_H
22
+
23
+ int windows_start_event_monitor(void);
24
+ int windows_stop_event_monitor(void);
25
+
26
+ void windows_initial_scan_devices(struct libusb_context *ctx);
27
+
28
+ #endif
@@ -341,17 +341,21 @@ static int usbdk_get_device_list(struct libusb_context *ctx, struct discovered_d
341
341
  libusb_unref_device(dev);
342
342
  continue;
343
343
  }
344
- }
345
344
 
346
- discdevs = discovered_devs_append(*_discdevs, dev);
347
- libusb_unref_device(dev);
348
- if (!discdevs) {
349
- usbi_err(ctx, "cannot append new device to list");
350
- r = LIBUSB_ERROR_NO_MEM;
351
- goto func_exit;
345
+ usbi_connect_device(dev);
352
346
  }
353
347
 
354
- *_discdevs = discdevs;
348
+ if (_discdevs) {
349
+ discdevs = discovered_devs_append(*_discdevs, dev);
350
+ libusb_unref_device(dev);
351
+ if (!discdevs) {
352
+ usbi_err(ctx, "cannot append new device to list");
353
+ r = LIBUSB_ERROR_NO_MEM;
354
+ goto func_exit;
355
+ }
356
+
357
+ *_discdevs = discdevs;
358
+ }
355
359
  }
356
360
 
357
361
  func_exit:
@@ -704,6 +708,7 @@ const struct windows_backend usbdk_backend = {
704
708
  usbdk_init,
705
709
  usbdk_exit,
706
710
  usbdk_get_device_list,
711
+ NULL, /* usbdk_get_device_string */
707
712
  usbdk_open,
708
713
  usbdk_close,
709
714
  usbdk_get_active_config_descriptor,
@@ -721,4 +726,7 @@ const struct windows_backend usbdk_backend = {
721
726
  NULL, /* cancel_transfer */
722
727
  usbdk_clear_transfer_priv,
723
728
  usbdk_copy_transfer_data,
729
+ NULL, /* endpoint_supports_raw_io */
730
+ NULL, /* endpoint_set_raw_io */
731
+ NULL, /* get_max_raw_io_transfer_size */
724
732
  };