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.
- package/CHANGELOG.md +14 -0
- package/README.md +14 -0
- package/binding.gyp +2 -9
- package/dist/usb/bindings.d.ts +27 -2
- package/dist/usb/bindings.js.map +1 -1
- package/dist/usb/endpoint.js +1 -1
- package/dist/usb/endpoint.js.map +1 -1
- package/dist/usb/index.d.ts +0 -29
- package/dist/usb/index.js +4 -18
- package/dist/usb/index.js.map +1 -1
- package/dist/webusb/index.d.ts +1 -1
- package/dist/webusb/index.js +1 -1
- package/dist/webusb/index.js.map +1 -1
- package/dist/webusb/webusb-device.d.ts +4 -4
- package/dist/webusb/webusb-device.js +5 -2
- package/dist/webusb/webusb-device.js.map +1 -1
- package/libusb/.clang-tidy +36 -0
- package/libusb/.private/ci-build.sh +5 -1
- package/libusb/AUTHORS +21 -0
- package/libusb/ChangeLog +29 -2
- package/libusb/KEYS +123 -0
- package/libusb/README +8 -9
- package/libusb/Xcode/common.xcconfig +20 -0
- package/libusb/Xcode/libusb.xcodeproj/project.pbxproj +16 -12
- package/libusb/android/examples/unrooted_android.c +1 -0
- package/libusb/configure.ac +12 -2
- package/libusb/examples/dpfp.c +1 -1
- package/libusb/examples/ezusb.c +6 -1
- package/libusb/examples/fxload.c +7 -5
- package/libusb/examples/hotplugtest.c +19 -11
- package/libusb/examples/listdevs.c +41 -3
- package/libusb/examples/testlibusb.c +1 -0
- package/libusb/examples/xusb.c +142 -77
- package/libusb/libusb/Makefile.am +4 -0
- package/libusb/libusb/core.c +183 -24
- package/libusb/libusb/descriptor.c +404 -96
- package/libusb/libusb/hotplug.c +27 -8
- package/libusb/libusb/io.c +10 -5
- package/libusb/libusb/libusb-1.0.def +14 -0
- package/libusb/libusb/libusb.h +179 -19
- package/libusb/libusb/libusbi.h +101 -25
- package/libusb/libusb/os/darwin_usb.c +216 -90
- package/libusb/libusb/os/darwin_usb.h +10 -8
- package/libusb/libusb/os/emscripten_webusb.cpp +38 -12
- package/libusb/libusb/os/events_posix.c +4 -4
- package/libusb/libusb/os/haiku_usb_raw.cpp +4 -0
- package/libusb/libusb/os/linux_usbfs.c +92 -33
- package/libusb/libusb/os/linux_usbfs.h +13 -3
- package/libusb/libusb/os/netbsd_usb.c +6 -4
- package/libusb/libusb/os/openbsd_usb.c +4 -2
- package/libusb/libusb/os/sunos_usb.c +7 -5
- package/libusb/libusb/os/threads_posix.c +20 -19
- package/libusb/libusb/os/threads_posix.h +9 -3
- package/libusb/libusb/os/threads_windows.h +4 -3
- package/libusb/libusb/os/windows_common.c +86 -1
- package/libusb/libusb/os/windows_common.h +20 -1
- package/libusb/libusb/os/windows_hotplug.c +321 -0
- package/libusb/libusb/os/windows_hotplug.h +28 -0
- package/libusb/libusb/os/windows_usbdk.c +16 -8
- package/libusb/libusb/os/windows_winusb.c +788 -56
- package/libusb/libusb/os/windows_winusb.h +11 -6
- package/libusb/libusb/sync.c +8 -5
- package/libusb/libusb/version.h +1 -1
- package/libusb/libusb/version_nano.h +1 -1
- package/libusb/msvc/Base.props +1 -1
- package/libusb/msvc/Configuration.Base.props +2 -1
- package/libusb/msvc/Configuration.DynamicLibrary.props +12 -0
- package/libusb/msvc/ProjectConfigurations.Base.props +69 -16
- package/libusb/msvc/build_all.ps1 +2 -2
- package/libusb/msvc/config.h +4 -0
- package/libusb/msvc/getopt/bits/getopt_core.h +96 -0
- package/libusb/msvc/getopt/bits/getopt_ext.h +77 -0
- package/libusb/msvc/getopt/features.h +21 -0
- package/libusb/msvc/getopt/getopt.c +456 -705
- package/libusb/msvc/getopt/getopt.h +16 -158
- package/libusb/msvc/getopt/getopt1.c +40 -69
- package/libusb/msvc/getopt/getopt_int.h +118 -0
- package/libusb/msvc/getopt/gettext.h +7 -0
- package/libusb/msvc/getopt/unistd.h +5 -0
- package/libusb/msvc/getopt.vcxproj +11 -4
- package/libusb/msvc/libusb.sln +515 -268
- package/libusb/msvc/libusb_dll.vcxproj +2 -0
- package/libusb/msvc/libusb_static.vcxproj +2 -0
- package/libusb/msvc/xusb.vcxproj +1 -1
- package/libusb/tests/Makefile.am +10 -1
- package/libusb/tests/fuzz/corpus/bos/min.bos +0 -0
- package/libusb/tests/fuzz/corpus/descriptor_parsers/min_valid_config.bin +0 -0
- package/libusb/tests/fuzz/corpus/descriptor_parsers/regression_bug_a_endpoint_null.bin +0 -0
- package/libusb/tests/fuzz/corpus/descriptor_parsers/regression_bug_b_iad_oob.bin +0 -0
- package/libusb/tests/fuzz/fuzz_bos_descriptor.c +49 -0
- package/libusb/tests/fuzz/fuzz_descriptor_parsers.c +83 -0
- package/libusb/tests/macos.c +2 -2
- package/libusb/tests/stress_mt.c +6 -3
- package/libusb/tests/webusb-test-shim/index.js +6 -5
- package/libusb.gypi +5 -0
- package/package.json +3 -3
- package/prebuilds/android-arm/node.napi.armv7.node +0 -0
- package/prebuilds/android-arm64/node.napi.armv8.node +0 -0
- package/prebuilds/darwin-x64+arm64/node.napi.node +0 -0
- package/prebuilds/linux-arm/node.napi.armv6.node +0 -0
- package/prebuilds/linux-arm/node.napi.armv7.node +0 -0
- package/prebuilds/linux-arm64/node.napi.armv8.node +0 -0
- package/prebuilds/linux-ia32/node.napi.node +0 -0
- package/prebuilds/linux-x64/node.napi.glibc.node +0 -0
- package/prebuilds/linux-x64/node.napi.musl.node +0 -0
- package/prebuilds/win32-arm64/node.napi.node +0 -0
- package/prebuilds/win32-ia32/node.napi.node +0 -0
- package/prebuilds/win32-x64/node.napi.node +0 -0
- package/src/{hotplug/libusb.cc → hotplug.cc} +2 -3
- package/src/{hotplug/hotplug.h → hotplug.h} +2 -6
- package/src/node_usb.cc +3 -3
- package/test/usb.coffee +4 -4
- package/test/webusb.coffee +22 -12
- 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
|
-
|
|
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
|
|
109
|
+
static inline unsigned long usbi_get_tid(void)
|
|
109
110
|
{
|
|
110
|
-
return (unsigned
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
};
|