usb 2.16.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 (114) hide show
  1. package/CHANGELOG.md +14 -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/endpoint.js +1 -1
  7. package/dist/usb/endpoint.js.map +1 -1
  8. package/dist/usb/index.d.ts +0 -29
  9. package/dist/usb/index.js +4 -18
  10. package/dist/usb/index.js.map +1 -1
  11. package/dist/webusb/index.d.ts +1 -1
  12. package/dist/webusb/index.js +1 -1
  13. package/dist/webusb/index.js.map +1 -1
  14. package/dist/webusb/webusb-device.d.ts +4 -4
  15. package/dist/webusb/webusb-device.js +5 -2
  16. package/dist/webusb/webusb-device.js.map +1 -1
  17. package/libusb/.clang-tidy +36 -0
  18. package/libusb/.private/ci-build.sh +5 -1
  19. package/libusb/AUTHORS +21 -0
  20. package/libusb/ChangeLog +29 -2
  21. package/libusb/KEYS +123 -0
  22. package/libusb/README +8 -9
  23. package/libusb/Xcode/common.xcconfig +20 -0
  24. package/libusb/Xcode/libusb.xcodeproj/project.pbxproj +16 -12
  25. package/libusb/android/examples/unrooted_android.c +1 -0
  26. package/libusb/configure.ac +12 -2
  27. package/libusb/examples/dpfp.c +1 -1
  28. package/libusb/examples/ezusb.c +6 -1
  29. package/libusb/examples/fxload.c +7 -5
  30. package/libusb/examples/hotplugtest.c +19 -11
  31. package/libusb/examples/listdevs.c +41 -3
  32. package/libusb/examples/testlibusb.c +1 -0
  33. package/libusb/examples/xusb.c +142 -77
  34. package/libusb/libusb/Makefile.am +4 -0
  35. package/libusb/libusb/core.c +183 -24
  36. package/libusb/libusb/descriptor.c +404 -96
  37. package/libusb/libusb/hotplug.c +27 -8
  38. package/libusb/libusb/io.c +10 -5
  39. package/libusb/libusb/libusb-1.0.def +14 -0
  40. package/libusb/libusb/libusb.h +179 -19
  41. package/libusb/libusb/libusbi.h +101 -25
  42. package/libusb/libusb/os/darwin_usb.c +216 -90
  43. package/libusb/libusb/os/darwin_usb.h +10 -8
  44. package/libusb/libusb/os/emscripten_webusb.cpp +38 -12
  45. package/libusb/libusb/os/events_posix.c +4 -4
  46. package/libusb/libusb/os/haiku_usb_raw.cpp +4 -0
  47. package/libusb/libusb/os/linux_usbfs.c +92 -33
  48. package/libusb/libusb/os/linux_usbfs.h +13 -3
  49. package/libusb/libusb/os/netbsd_usb.c +6 -4
  50. package/libusb/libusb/os/openbsd_usb.c +4 -2
  51. package/libusb/libusb/os/sunos_usb.c +7 -5
  52. package/libusb/libusb/os/threads_posix.c +20 -19
  53. package/libusb/libusb/os/threads_posix.h +9 -3
  54. package/libusb/libusb/os/threads_windows.h +4 -3
  55. package/libusb/libusb/os/windows_common.c +86 -1
  56. package/libusb/libusb/os/windows_common.h +20 -1
  57. package/libusb/libusb/os/windows_hotplug.c +321 -0
  58. package/libusb/libusb/os/windows_hotplug.h +28 -0
  59. package/libusb/libusb/os/windows_usbdk.c +16 -8
  60. package/libusb/libusb/os/windows_winusb.c +788 -56
  61. package/libusb/libusb/os/windows_winusb.h +11 -6
  62. package/libusb/libusb/sync.c +8 -5
  63. package/libusb/libusb/version.h +1 -1
  64. package/libusb/libusb/version_nano.h +1 -1
  65. package/libusb/msvc/Base.props +1 -1
  66. package/libusb/msvc/Configuration.Base.props +2 -1
  67. package/libusb/msvc/Configuration.DynamicLibrary.props +12 -0
  68. package/libusb/msvc/ProjectConfigurations.Base.props +69 -16
  69. package/libusb/msvc/build_all.ps1 +2 -2
  70. package/libusb/msvc/config.h +4 -0
  71. package/libusb/msvc/getopt/bits/getopt_core.h +96 -0
  72. package/libusb/msvc/getopt/bits/getopt_ext.h +77 -0
  73. package/libusb/msvc/getopt/features.h +21 -0
  74. package/libusb/msvc/getopt/getopt.c +456 -705
  75. package/libusb/msvc/getopt/getopt.h +16 -158
  76. package/libusb/msvc/getopt/getopt1.c +40 -69
  77. package/libusb/msvc/getopt/getopt_int.h +118 -0
  78. package/libusb/msvc/getopt/gettext.h +7 -0
  79. package/libusb/msvc/getopt/unistd.h +5 -0
  80. package/libusb/msvc/getopt.vcxproj +11 -4
  81. package/libusb/msvc/libusb.sln +515 -268
  82. package/libusb/msvc/libusb_dll.vcxproj +2 -0
  83. package/libusb/msvc/libusb_static.vcxproj +2 -0
  84. package/libusb/msvc/xusb.vcxproj +1 -1
  85. package/libusb/tests/Makefile.am +10 -1
  86. package/libusb/tests/fuzz/corpus/bos/min.bos +0 -0
  87. package/libusb/tests/fuzz/corpus/descriptor_parsers/min_valid_config.bin +0 -0
  88. package/libusb/tests/fuzz/corpus/descriptor_parsers/regression_bug_a_endpoint_null.bin +0 -0
  89. package/libusb/tests/fuzz/corpus/descriptor_parsers/regression_bug_b_iad_oob.bin +0 -0
  90. package/libusb/tests/fuzz/fuzz_bos_descriptor.c +49 -0
  91. package/libusb/tests/fuzz/fuzz_descriptor_parsers.c +83 -0
  92. package/libusb/tests/macos.c +2 -2
  93. package/libusb/tests/stress_mt.c +6 -3
  94. package/libusb/tests/webusb-test-shim/index.js +6 -5
  95. package/libusb.gypi +5 -0
  96. package/package.json +3 -3
  97. package/prebuilds/android-arm/node.napi.armv7.node +0 -0
  98. package/prebuilds/android-arm64/node.napi.armv8.node +0 -0
  99. package/prebuilds/darwin-x64+arm64/node.napi.node +0 -0
  100. package/prebuilds/linux-arm/node.napi.armv6.node +0 -0
  101. package/prebuilds/linux-arm/node.napi.armv7.node +0 -0
  102. package/prebuilds/linux-arm64/node.napi.armv8.node +0 -0
  103. package/prebuilds/linux-ia32/node.napi.node +0 -0
  104. package/prebuilds/linux-x64/node.napi.glibc.node +0 -0
  105. package/prebuilds/linux-x64/node.napi.musl.node +0 -0
  106. package/prebuilds/win32-arm64/node.napi.node +0 -0
  107. package/prebuilds/win32-ia32/node.napi.node +0 -0
  108. package/prebuilds/win32-x64/node.napi.node +0 -0
  109. package/src/{hotplug/libusb.cc → hotplug.cc} +2 -3
  110. package/src/{hotplug/hotplug.h → hotplug.h} +2 -6
  111. package/src/node_usb.cc +3 -3
  112. package/test/usb.coffee +4 -4
  113. package/test/webusb.coffee +22 -12
  114. package/src/hotplug/windows.cc +0 -168
@@ -50,7 +50,8 @@ static inline void usbi_mutex_unlock(usbi_mutex_t *mutex)
50
50
  }
51
51
  static inline int usbi_mutex_trylock(usbi_mutex_t *mutex)
52
52
  {
53
- return TryEnterCriticalSection(mutex) != 0;
53
+ int mutexIsLocked = TryEnterCriticalSection(mutex) != 0;
54
+ return mutexIsLocked;
54
55
  }
55
56
  static inline void usbi_mutex_destroy(usbi_mutex_t *mutex)
56
57
  {
@@ -105,9 +106,9 @@ static inline void usbi_tls_key_delete(usbi_tls_key_t key)
105
106
  WINAPI_CHECK(TlsFree(key));
106
107
  }
107
108
 
108
- static inline unsigned int usbi_get_tid(void)
109
+ static inline unsigned long usbi_get_tid(void)
109
110
  {
110
- return (unsigned int)GetCurrentThreadId();
111
+ return (unsigned long)GetCurrentThreadId();
111
112
  }
112
113
 
113
114
  #endif /* LIBUSB_THREADS_WINDOWS_H */
@@ -29,6 +29,10 @@
29
29
  #include "libusbi.h"
30
30
  #include "windows_common.h"
31
31
 
32
+ #if defined(LIBUSB_WINDOWS_HOTPLUG)
33
+ #include "windows_hotplug.h"
34
+ #endif
35
+
32
36
  #define EPOCH_TIME UINT64_C(116444736000000000) // 1970.01.01 00:00:000 in MS Filetime
33
37
 
34
38
  #define STATUS_SUCCESS ((ULONG_PTR)0UL)
@@ -492,9 +496,11 @@ static unsigned __stdcall windows_iocp_thread(void *arg)
492
496
  }
493
497
 
494
498
  itransfer = TRANSFER_PRIV_TO_USBI_TRANSFER(transfer_priv);
499
+ #ifdef ENABLE_LOGGING
495
500
  struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
496
501
  usbi_dbg(ctx, "transfer %p completed, length %lu",
497
502
  transfer, ULONG_CAST(num_bytes));
503
+ #endif
498
504
  usbi_signal_transfer_completion(itransfer);
499
505
  }
500
506
 
@@ -565,6 +571,18 @@ static int windows_init(struct libusb_context *ctx)
565
571
 
566
572
  r = LIBUSB_SUCCESS;
567
573
 
574
+ #if defined(LIBUSB_WINDOWS_HOTPLUG)
575
+ if (init_count == 1) {
576
+ r = windows_start_event_monitor(); // Start-up hotplug event handler
577
+ if (r != LIBUSB_SUCCESS) {
578
+ usbi_err(ctx, "error starting hotplug event monitor");
579
+ goto init_exit;
580
+ }
581
+ }
582
+
583
+ windows_initial_scan_devices(ctx);
584
+ #endif
585
+
568
586
  init_exit: // Holds semaphore here
569
587
  if ((init_count == 1) && (r != LIBUSB_SUCCESS)) { // First init failed?
570
588
  if (usbdk_available) {
@@ -596,6 +614,9 @@ static void windows_exit(struct libusb_context *ctx)
596
614
 
597
615
  // Only works if exits and inits are balanced exactly
598
616
  if (--init_count == 0) { // Last exit
617
+ #if defined(LIBUSB_WINDOWS_HOTPLUG)
618
+ windows_stop_event_monitor();
619
+ #endif
599
620
  if (usbdk_available) {
600
621
  usbdk_backend.exit(ctx);
601
622
  usbdk_available = false;
@@ -624,11 +645,23 @@ static int windows_set_option(struct libusb_context *ctx, enum libusb_option opt
624
645
  return LIBUSB_ERROR_NOT_SUPPORTED;
625
646
  }
626
647
 
648
+ #if !defined(LIBUSB_WINDOWS_HOTPLUG)
627
649
  static int windows_get_device_list(struct libusb_context *ctx, struct discovered_devs **discdevs)
628
650
  {
629
651
  struct windows_context_priv *priv = usbi_get_context_priv(ctx);
630
652
  return priv->backend->get_device_list(ctx, discdevs);
631
653
  }
654
+ #endif
655
+
656
+ static int windows_get_device_string(libusb_device *dev,
657
+ enum libusb_device_string_type string_type, char *data, int length)
658
+ {
659
+ struct windows_context_priv* priv = usbi_get_context_priv(DEVICE_CTX(dev));
660
+ if (NULL != priv->backend->get_device_string) {
661
+ return priv->backend->get_device_string(dev, string_type, data, length);
662
+ }
663
+ return LIBUSB_ERROR_NOT_SUPPORTED;
664
+ }
632
665
 
633
666
  static int windows_open(struct libusb_device_handle *dev_handle)
634
667
  {
@@ -717,6 +750,37 @@ static void windows_destroy_device(struct libusb_device *dev)
717
750
  priv->backend->destroy_device(dev);
718
751
  }
719
752
 
753
+ static int windows_endpoint_supports_raw_io(libusb_device_handle* dev_handle,
754
+ uint8_t endpoint)
755
+ {
756
+ struct windows_context_priv *priv = usbi_get_context_priv(HANDLE_CTX(dev_handle));
757
+ if (priv->backend->endpoint_supports_raw_io)
758
+ return priv->backend->endpoint_supports_raw_io(dev_handle, endpoint);
759
+ return LIBUSB_ERROR_NOT_SUPPORTED;
760
+ }
761
+
762
+ static int windows_endpoint_set_raw_io(libusb_device_handle* dev_handle,
763
+ uint8_t endpoint, int enable)
764
+ {
765
+ struct windows_context_priv *priv = usbi_get_context_priv(HANDLE_CTX(dev_handle));
766
+
767
+ if (priv->backend->endpoint_supports_raw_io == NULL
768
+ || priv->backend->endpoint_supports_raw_io(dev_handle, endpoint) != 1
769
+ || priv->backend->endpoint_set_raw_io == NULL)
770
+ return LIBUSB_ERROR_NOT_SUPPORTED;
771
+
772
+ return priv->backend->endpoint_set_raw_io(dev_handle, endpoint, enable);
773
+ }
774
+
775
+ static int windows_get_max_raw_io_transfer_size(struct libusb_device_handle *dev_handle,
776
+ uint8_t endpoint)
777
+ {
778
+ struct windows_context_priv *priv = usbi_get_context_priv(HANDLE_CTX(dev_handle));
779
+ if (priv->backend->get_max_raw_io_transfer_size)
780
+ return priv->backend->get_max_raw_io_transfer_size(dev_handle, endpoint);
781
+ return LIBUSB_ERROR_NOT_SUPPORTED;
782
+ }
783
+
720
784
  static int windows_submit_transfer(struct usbi_transfer *itransfer)
721
785
  {
722
786
  struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
@@ -800,15 +864,26 @@ static int windows_handle_transfer_completion(struct usbi_transfer *itransfer)
800
864
  struct windows_transfer_priv *transfer_priv = usbi_get_transfer_priv(itransfer);
801
865
  enum libusb_transfer_status status, istatus;
802
866
  DWORD result, bytes_transferred;
867
+ HANDLE transfer_handle;
868
+
869
+ /*
870
+ * The submit path runs with itransfer->lock held. Grab the handle under
871
+ * the same lock so we do not race with submission/completion bookkeeping.
872
+ */
873
+ usbi_mutex_lock(&itransfer->lock);
874
+ transfer_handle = transfer_priv->handle;
875
+ usbi_mutex_unlock(&itransfer->lock);
803
876
 
804
- if (GetOverlappedResult(transfer_priv->handle, &transfer_priv->overlapped, &bytes_transferred, FALSE))
877
+ if (GetOverlappedResult(transfer_handle, &transfer_priv->overlapped, &bytes_transferred, FALSE))
805
878
  result = NO_ERROR;
806
879
  else
807
880
  result = GetLastError();
808
881
 
882
+ #ifdef ENABLE_LOGGING
809
883
  struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
810
884
  usbi_dbg(ctx, "handling transfer %p completion with errcode %lu, length %lu",
811
885
  transfer, ULONG_CAST(result), ULONG_CAST(bytes_transferred));
886
+ #endif
812
887
 
813
888
  switch (result) {
814
889
  case NO_ERROR:
@@ -843,7 +918,9 @@ static int windows_handle_transfer_completion(struct usbi_transfer *itransfer)
843
918
  break;
844
919
  }
845
920
 
921
+ usbi_mutex_lock(&itransfer->lock);
846
922
  transfer_priv->handle = NULL;
923
+ usbi_mutex_unlock(&itransfer->lock);
847
924
 
848
925
  // Backend-specific cleanup
849
926
  backend->clear_transfer_priv(itransfer);
@@ -888,7 +965,12 @@ const struct usbi_os_backend usbi_backend = {
888
965
  windows_init,
889
966
  windows_exit,
890
967
  windows_set_option,
968
+ #if defined(LIBUSB_WINDOWS_HOTPLUG)
969
+ NULL, /* get_device_list */
970
+ #else
891
971
  windows_get_device_list,
972
+ #endif
973
+ windows_get_device_string,
892
974
  NULL, /* hotplug_poll */
893
975
  NULL, /* wrap_sys_device */
894
976
  windows_open,
@@ -910,6 +992,9 @@ const struct usbi_os_backend usbi_backend = {
910
992
  NULL, /* kernel_driver_active */
911
993
  NULL, /* detach_kernel_driver */
912
994
  NULL, /* attach_kernel_driver */
995
+ windows_endpoint_supports_raw_io,
996
+ windows_endpoint_set_raw_io,
997
+ windows_get_max_raw_io_transfer_size,
913
998
  windows_destroy_device,
914
999
  windows_submit_transfer,
915
1000
  windows_cancel_transfer,
@@ -242,13 +242,23 @@ struct usbdk_device_priv {
242
242
 
243
243
  struct winusb_device_priv {
244
244
  bool initialized;
245
+ #if defined(LIBUSB_WINDOWS_HOTPLUG)
246
+ bool seen_during_scan; // set true for each device encountered during windows_get_device_list
247
+ bool seen_before_scan; // set true for each device encountered before windows_get_device_list
248
+ #endif
245
249
  bool root_hub;
246
250
  uint8_t active_config;
251
+ uint16_t langid; // cached USB language ID for string descriptor requests
247
252
  uint8_t depth; // distance to HCD
248
253
  const struct windows_usb_api_backend *apib;
249
254
  char *dev_id;
250
255
  char *path; // device interface path
251
256
  int sub_api; // for WinUSB-like APIs
257
+ usbi_mutex_t interface_lock; // protects usb_interface[] against concurrent
258
+ // claim/release/altsetting from different handles,
259
+ // and concurrent enumeration setup
260
+ // (set_composite_interface, set_hid_interface,
261
+ // and HUB_PASS/DEV_PASS in winusb_get_device_list)
252
262
  struct {
253
263
  char *path; // each interface needs a device interface path,
254
264
  const struct windows_usb_api_backend *apib; // an API backend (multiple drivers support),
@@ -275,7 +285,7 @@ struct usbdk_device_handle_priv {
275
285
  // Not currently used
276
286
  char dummy;
277
287
  };
278
-
288
+
279
289
  enum WINUSB_ZLP {
280
290
  WINUSB_ZLP_UNSET = 0,
281
291
  WINUSB_ZLP_OFF = 1,
@@ -320,6 +330,8 @@ struct windows_backend {
320
330
  void (*exit)(struct libusb_context *ctx);
321
331
  int (*get_device_list)(struct libusb_context *ctx,
322
332
  struct discovered_devs **discdevs);
333
+ int (*get_device_string)(libusb_device *dev,
334
+ enum libusb_device_string_type string_type, char *data, int length);
323
335
  int (*open)(struct libusb_device_handle *dev_handle);
324
336
  void (*close)(struct libusb_device_handle *dev_handle);
325
337
  int (*get_active_config_descriptor)(struct libusb_device *device,
@@ -342,6 +354,13 @@ struct windows_backend {
342
354
  int (*cancel_transfer)(struct usbi_transfer *itransfer);
343
355
  void (*clear_transfer_priv)(struct usbi_transfer *itransfer);
344
356
  enum libusb_transfer_status (*copy_transfer_data)(struct usbi_transfer *itransfer, DWORD length);
357
+ int (*endpoint_supports_raw_io)(struct libusb_device_handle *dev_handle,
358
+ uint8_t endpoint);
359
+ int (*endpoint_set_raw_io)(struct libusb_device_handle *dev_handle,
360
+ uint8_t endpoint, int enable);
361
+ int (*get_max_raw_io_transfer_size)(
362
+ struct libusb_device_handle *dev_handle,
363
+ uint8_t endpoint);
345
364
  };
346
365
 
347
366
  struct windows_context_priv {
@@ -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
  };