| /* | ||
| * windows hotplug backend for libusb 1.0 | ||
| * Copyright © 2024 Sylvain Fasel <sylvain@sonatique.net> | ||
| * | ||
| * This library is free software; you can redistribute it and/or | ||
| * modify it under the terms of the GNU Lesser General Public | ||
| * License as published by the Free Software Foundation; either | ||
| * version 2.1 of the License, or (at your option) any later version. | ||
| * | ||
| * This library is distributed in the hope that it will be useful, | ||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| * Lesser General Public License for more details. | ||
| * | ||
| * You should have received a copy of the GNU Lesser General Public | ||
| * License along with this library; if not, write to the Free Software | ||
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| */ | ||
| #include "libusbi.h" | ||
| #include "threads_windows.h" | ||
| #include "windows_common.h" | ||
| #include "windows_hotplug.h" | ||
| #include <stdio.h> | ||
| #include <dbt.h> | ||
| /* The Windows Hotplug system is a two steps process. | ||
| * 1. We create a hidden window and listen for DBT_DEVNODES_CHANGED, which Windows | ||
| * broadcasts to all top-level windows whenever the device tree changes (no | ||
| * registration required). Multiple rapid events (e.g. a hub with many children) | ||
| * are coalesced via a short debounce timer so we only scan once the burst settles. | ||
| * A maximum delay ceiling guarantees a scan fires within a bounded time even | ||
| * during sustained bursts, preventing unbounded latency. | ||
| * 2. Upon timer expiry, we snapshot the current device list, run a full re-enumeration | ||
| * via the Windows backend, then diff the result: newly found devices that have been | ||
| * successfully initialized generate DEVICE_ARRIVED events; devices that were not | ||
| * encountered by the re-enumeration (physically removed) generate DEVICE_LEFT events. | ||
| */ | ||
| #define HOTPLUG_DEBOUNCE_TIMER_ID 1 | ||
| #define HOTPLUG_DEBOUNCE_MS 10 | ||
| #define HOTPLUG_DEBOUNCE_MAX_DELAY_MS 100 | ||
| static ULONGLONG first_debounce_tick; | ||
| static HWND windows_event_hwnd; | ||
| static HANDLE windows_event_thread_handle; | ||
| static DWORD WINAPI windows_event_thread_main(LPVOID lpParam); | ||
| static LRESULT CALLBACK windows_proc_callback(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); | ||
| #define log_error(operation) do { \ | ||
| usbi_err(NULL, "%s failed with error: %s", operation, windows_error_str(0)); \ | ||
| } while (0) | ||
| int windows_start_event_monitor(void) | ||
| { | ||
| windows_event_thread_handle = CreateThread( | ||
| NULL, // Default security descriptor | ||
| 0, // Default stack size | ||
| windows_event_thread_main, | ||
| NULL, // No parameters to pass to the thread | ||
| 0, // Start immediately | ||
| NULL // No need to keep track of thread ID | ||
| ); | ||
| if (windows_event_thread_handle == NULL) | ||
| { | ||
| log_error("CreateThread"); | ||
| return LIBUSB_ERROR_OTHER; | ||
| } | ||
| return LIBUSB_SUCCESS; | ||
| } | ||
| int windows_stop_event_monitor(void) | ||
| { | ||
| if (windows_event_hwnd == NULL) | ||
| { | ||
| return LIBUSB_SUCCESS; | ||
| } | ||
| if (!SUCCEEDED(SendMessage(windows_event_hwnd, WM_CLOSE, 0, 0))) | ||
| { | ||
| log_error("SendMessage"); | ||
| return LIBUSB_ERROR_OTHER; | ||
| } | ||
| if (WaitForSingleObject(windows_event_thread_handle, INFINITE) != WAIT_OBJECT_0) | ||
| { | ||
| log_error("WaitForSingleObject"); | ||
| return LIBUSB_ERROR_OTHER; | ||
| } | ||
| if (!CloseHandle(windows_event_thread_handle)) | ||
| { | ||
| log_error("CloseHandle"); | ||
| return LIBUSB_ERROR_OTHER; | ||
| } | ||
| return LIBUSB_SUCCESS; | ||
| } | ||
| static int windows_get_device_list(struct libusb_context *ctx) | ||
| { | ||
| // note: context device list is protected by active_contexts_lock | ||
| return ((struct windows_context_priv *)usbi_get_context_priv(ctx))->backend->get_device_list(ctx, NULL); | ||
| } | ||
| void windows_initial_scan_devices(struct libusb_context *ctx) | ||
| { | ||
| usbi_mutex_static_lock(&active_contexts_lock); | ||
| const int ret = windows_get_device_list(ctx); | ||
| if (ret != LIBUSB_SUCCESS) | ||
| { | ||
| usbi_err(ctx, "hotplug failed to retrieve initial list with error: %s", libusb_error_name(ret)); | ||
| } | ||
| usbi_mutex_static_unlock(&active_contexts_lock); | ||
| } | ||
| static void windows_refresh_device_list(struct libusb_context *ctx) | ||
| { | ||
| struct libusb_device *dev, *next_dev; | ||
| // Step 1: clear seen_during_scan so the scan can mark which devices are still | ||
| // physically present, and set seen_before_scan so we can distinguish newly | ||
| // created devices (which start with seen_before_scan=false via calloc). | ||
| for_each_device_safe(ctx, dev, next_dev) | ||
| { | ||
| struct winusb_device_priv *priv = (struct winusb_device_priv *)usbi_get_device_priv(dev); | ||
| priv->seen_during_scan = false; | ||
| priv->seen_before_scan = true; | ||
| } | ||
| // Step 2: re-enumerate — winusb_get_device_list attaches newly-arrived devices | ||
| // and sets seen_during_scan=true for every device it physically encounters. | ||
| // 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. | ||
| const int ret = windows_get_device_list(ctx); | ||
| if (ret != LIBUSB_SUCCESS) | ||
| { | ||
| usbi_err(ctx, "hotplug failed to retrieve current list with error: %s", libusb_error_name(ret)); | ||
| return; | ||
| } | ||
| // Step 3: diff old vs new. | ||
| for_each_device_safe(ctx, dev, next_dev) | ||
| { | ||
| struct winusb_device_priv *priv = (struct winusb_device_priv *)usbi_get_device_priv(dev); | ||
| if (!priv->seen_during_scan) | ||
| { | ||
| // Not encountered by the scan: device was physically removed. | ||
| if (priv->initialized) | ||
| { | ||
| usbi_disconnect_device(dev); // fires DEVICE_LEFT | ||
| } | ||
| else | ||
| { | ||
| usbi_detach_device(dev); | ||
| // No DEVICE_LEFT message is posted for uninitialized | ||
| // devices, so no message handler will drop the initial | ||
| // ref. We must drop it here to avoid a leak. | ||
| libusb_unref_device(dev); | ||
| } | ||
| } | ||
| else if (!priv->seen_before_scan) | ||
| { | ||
| if (priv->initialized) | ||
| { | ||
| usbi_hotplug_notification(ctx, dev, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| static void windows_refresh_device_list_for_all_ctx(void) | ||
| { | ||
| usbi_mutex_static_lock(&active_contexts_lock); | ||
| struct libusb_context *ctx; | ||
| for_each_context(ctx) | ||
| { | ||
| windows_refresh_device_list(ctx); | ||
| } | ||
| usbi_mutex_static_unlock(&active_contexts_lock); | ||
| } | ||
| #define WND_CLASS_NAME TEXT("libusb-1.0-windows-hotplug") | ||
| static bool init_wnd_class(void) | ||
| { | ||
| WNDCLASS wndClass = { 0 }; | ||
| wndClass.lpfnWndProc = windows_proc_callback; | ||
| wndClass.hInstance = GetModuleHandle(NULL); | ||
| wndClass.lpszClassName = WND_CLASS_NAME; | ||
| if (!RegisterClass(&wndClass)) | ||
| { | ||
| log_error("event thread: RegisterClass"); | ||
| return false; | ||
| } | ||
| return true; | ||
| } | ||
| static DWORD WINAPI windows_event_thread_main(LPVOID lpParam) | ||
| { | ||
| UNUSED(lpParam); | ||
| usbi_dbg(NULL, "windows event thread entering"); | ||
| if (!init_wnd_class()) | ||
| { | ||
| return (DWORD)-1; | ||
| } | ||
| windows_event_hwnd = CreateWindow( | ||
| WND_CLASS_NAME, | ||
| TEXT(""), | ||
| 0, | ||
| 0, 0, 0, 0, | ||
| NULL, NULL, | ||
| GetModuleHandle(NULL), | ||
| NULL); | ||
| if (windows_event_hwnd == NULL) | ||
| { | ||
| log_error("event thread: CreateWindow"); | ||
| return (DWORD)-1; | ||
| } | ||
| MSG msg; | ||
| BOOL ret_val; | ||
| while ((ret_val = GetMessage(&msg, windows_event_hwnd, 0, 0)) != 0) | ||
| { | ||
| if (ret_val == -1) | ||
| { | ||
| log_error("event thread: GetMessage"); | ||
| break; | ||
| } | ||
| if (!SUCCEEDED(TranslateMessage(&msg))) | ||
| { | ||
| log_error("event thread: TranslateMessage"); | ||
| } | ||
| if (!SUCCEEDED(DispatchMessage(&msg))) | ||
| { | ||
| log_error("event thread: DispatchMessage"); | ||
| } | ||
| } | ||
| usbi_dbg(NULL, "windows event thread exiting"); | ||
| return 0; | ||
| } | ||
| static LRESULT CALLBACK windows_proc_callback( | ||
| HWND hwnd, | ||
| UINT message, | ||
| WPARAM wParam, | ||
| LPARAM lParam) | ||
| { | ||
| switch (message) | ||
| { | ||
| case WM_DEVICECHANGE: | ||
| if (wParam == DBT_DEVNODES_CHANGED) | ||
| { | ||
| if (first_debounce_tick == 0) | ||
| { | ||
| // First event in a new burst — record the time and start the debounce timer. | ||
| first_debounce_tick = GetTickCount64(); | ||
| SetTimer(hwnd, HOTPLUG_DEBOUNCE_TIMER_ID, HOTPLUG_DEBOUNCE_MS, NULL); | ||
| } | ||
| else if (GetTickCount64() - first_debounce_tick >= HOTPLUG_DEBOUNCE_MAX_DELAY_MS) | ||
| { | ||
| // Maximum delay reached — force an immediate scan so devices | ||
| // are not invisible for the entire duration of a sustained burst. | ||
| KillTimer(hwnd, HOTPLUG_DEBOUNCE_TIMER_ID); | ||
| first_debounce_tick = 0; | ||
| windows_refresh_device_list_for_all_ctx(); | ||
| } | ||
| else | ||
| { | ||
| // Still within the max-delay window — reset the debounce timer. | ||
| SetTimer(hwnd, HOTPLUG_DEBOUNCE_TIMER_ID, HOTPLUG_DEBOUNCE_MS, NULL); | ||
| } | ||
| return TRUE; | ||
| } | ||
| return DefWindowProc(hwnd, message, wParam, lParam); | ||
| case WM_TIMER: | ||
| if (wParam == HOTPLUG_DEBOUNCE_TIMER_ID) | ||
| { | ||
| KillTimer(hwnd, HOTPLUG_DEBOUNCE_TIMER_ID); | ||
| first_debounce_tick = 0; | ||
| windows_refresh_device_list_for_all_ctx(); | ||
| return 0; | ||
| } | ||
| return DefWindowProc(hwnd, message, wParam, lParam); | ||
| case WM_CLOSE: | ||
| KillTimer(hwnd, HOTPLUG_DEBOUNCE_TIMER_ID); | ||
| first_debounce_tick = 0; | ||
| if (!DestroyWindow(hwnd)) | ||
| { | ||
| log_error("DestroyWindow"); | ||
| } | ||
| return 0; | ||
| case WM_DESTROY: | ||
| PostQuitMessage(0); | ||
| return 0; | ||
| default: | ||
| return DefWindowProc(hwnd, message, wParam, lParam); | ||
| } | ||
| } |
| /* | ||
| * windows hotplug backend for libusb 1.0 | ||
| * Copyright © 2024 Sylvain Fasel <sylvain@sonatique.net> | ||
| * | ||
| * This library is free software; you can redistribute it and/or | ||
| * modify it under the terms of the GNU Lesser General Public | ||
| * License as published by the Free Software Foundation; either | ||
| * version 2.1 of the License, or (at your option) any later version. | ||
| * | ||
| * This library is distributed in the hope that it will be useful, | ||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| * Lesser General Public License for more details. | ||
| * | ||
| * You should have received a copy of the GNU Lesser General Public | ||
| * License along with this library; if not, write to the Free Software | ||
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
| */ | ||
| #ifndef WINDOWS_HOTPLUG_H | ||
| #define WINDOWS_HOTPLUG_H | ||
| int windows_start_event_monitor(void); | ||
| int windows_stop_event_monitor(void); | ||
| void windows_initial_scan_devices(struct libusb_context *ctx); | ||
| #endif |
| /* Declarations for getopt (basic, portable features only). | ||
| Copyright (C) 1989-2026 Free Software Foundation, Inc. | ||
| This file is part of the GNU C Library and is also part of gnulib. | ||
| Patches to this file should be submitted to both projects. | ||
| The GNU C Library is free software; you can redistribute it and/or | ||
| modify it under the terms of the GNU Lesser General Public | ||
| License as published by the Free Software Foundation; either | ||
| version 2.1 of the License, or (at your option) any later version. | ||
| The GNU C Library is distributed in the hope that it will be useful, | ||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| Lesser General Public License for more details. | ||
| You should have received a copy of the GNU Lesser General Public | ||
| License along with the GNU C Library; if not, see | ||
| <https://www.gnu.org/licenses/>. */ | ||
| #ifndef _GETOPT_CORE_H | ||
| #define _GETOPT_CORE_H 1 | ||
| /* This header should not be used directly; include getopt.h or | ||
| unistd.h instead. Unlike most bits headers, it does not have | ||
| a protective #error, because the guard macro for getopt.h in | ||
| gnulib is not fixed. */ | ||
| __BEGIN_DECLS | ||
| /* For communication from 'getopt' to the caller. | ||
| When 'getopt' finds an option that takes an argument, | ||
| the argument value is returned here. | ||
| Also, when 'ordering' is RETURN_IN_ORDER, | ||
| each non-option ARGV-element is returned here. */ | ||
| extern char *optarg; | ||
| /* Index in ARGV of the next element to be scanned. | ||
| This is used for communication to and from the caller | ||
| and for communication between successive calls to 'getopt'. | ||
| On entry to 'getopt', zero means this is the first call; initialize. | ||
| When 'getopt' returns -1, this is the index of the first of the | ||
| non-option elements that the caller should itself scan. | ||
| Otherwise, 'optind' communicates from one call to the next | ||
| how much of ARGV has been scanned so far. */ | ||
| extern int optind; | ||
| /* Callers store zero here to inhibit the error message 'getopt' prints | ||
| for unrecognized options. */ | ||
| extern int opterr; | ||
| /* Set to an option character which was unrecognized. */ | ||
| extern int optopt; | ||
| /* Get definitions and prototypes for functions to process the | ||
| arguments in ARGV (ARGC of them, minus the program name) for | ||
| options given in OPTS. | ||
| Return the option character from OPTS just read. Return -1 when | ||
| there are no more options. For unrecognized options, or options | ||
| missing arguments, 'optopt' is set to the option letter, and '?' is | ||
| returned. | ||
| The OPTS string is a list of characters which are recognized option | ||
| letters, optionally followed by colons, specifying that that letter | ||
| takes an argument, to be placed in 'optarg'. | ||
| If a letter in OPTS is followed by two colons, its argument is | ||
| optional. This behavior is specific to the GNU 'getopt'. | ||
| The argument '--' causes premature termination of argument | ||
| scanning, explicitly telling 'getopt' that there are no more | ||
| options. | ||
| If OPTS begins with '-', then non-option arguments are treated as | ||
| arguments to the option '\1'. This behavior is specific to the GNU | ||
| 'getopt'. If OPTS begins with '+', or POSIXLY_CORRECT is set in | ||
| the environment, then do not permute arguments. | ||
| For standards compliance, the 'argv' argument has the type | ||
| char *const *, but this is inaccurate; if argument permutation is | ||
| enabled, the argv array (not the strings it points to) must be | ||
| writable. */ | ||
| extern int getopt (int ___argc, char *const *___argv, const char *__shortopts) | ||
| __THROW __nonnull ((2, 3)); | ||
| __END_DECLS | ||
| #endif /* getopt_core.h */ |
| /* Declarations for getopt (GNU extensions). | ||
| Copyright (C) 1989-2026 Free Software Foundation, Inc. | ||
| This file is part of the GNU C Library and is also part of gnulib. | ||
| Patches to this file should be submitted to both projects. | ||
| The GNU C Library is free software; you can redistribute it and/or | ||
| modify it under the terms of the GNU Lesser General Public | ||
| License as published by the Free Software Foundation; either | ||
| version 2.1 of the License, or (at your option) any later version. | ||
| The GNU C Library is distributed in the hope that it will be useful, | ||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| Lesser General Public License for more details. | ||
| You should have received a copy of the GNU Lesser General Public | ||
| License along with the GNU C Library; if not, see | ||
| <https://www.gnu.org/licenses/>. */ | ||
| #ifndef _GETOPT_EXT_H | ||
| #define _GETOPT_EXT_H 1 | ||
| /* This header should not be used directly; include getopt.h instead. | ||
| Unlike most bits headers, it does not have a protective #error, | ||
| because the guard macro for getopt.h in gnulib is not fixed. */ | ||
| __BEGIN_DECLS | ||
| /* Describe the long-named options requested by the application. | ||
| The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector | ||
| of 'struct option' terminated by an element containing a name which is | ||
| zero. | ||
| The field 'has_arg' is: | ||
| no_argument (or 0) if the option does not take an argument, | ||
| required_argument (or 1) if the option requires an argument, | ||
| optional_argument (or 2) if the option takes an optional argument. | ||
| If the field 'flag' is not NULL, it points to a variable that is set | ||
| to the value given in the field 'val' when the option is found, but | ||
| left unchanged if the option is not found. | ||
| To have a long-named option do something other than set an 'int' to | ||
| a compiled-in constant, such as set a value from 'optarg', set the | ||
| option's 'flag' field to zero and its 'val' field to a nonzero | ||
| value (the equivalent single-letter option character, if there is | ||
| one). For long options that have a zero 'flag' field, 'getopt' | ||
| returns the contents of the 'val' field. */ | ||
| struct option | ||
| { | ||
| const char *name; | ||
| /* has_arg can't be an enum because some compilers complain about | ||
| type mismatches in all the code that assumes it is an int. */ | ||
| int has_arg; | ||
| int *flag; | ||
| int val; | ||
| }; | ||
| /* Names for the values of the 'has_arg' field of 'struct option'. */ | ||
| #define no_argument 0 | ||
| #define required_argument 1 | ||
| #define optional_argument 2 | ||
| extern int getopt_long (int ___argc, char *__getopt_argv_const *___argv, | ||
| const char *__shortopts, | ||
| const struct option *__longopts, int *__longind) | ||
| __THROW __nonnull ((2, 3)); | ||
| extern int getopt_long_only (int ___argc, char *__getopt_argv_const *___argv, | ||
| const char *__shortopts, | ||
| const struct option *__longopts, int *__longind) | ||
| __THROW __nonnull ((2, 3)); | ||
| __END_DECLS | ||
| #endif /* getopt_ext.h */ |
| /* Minimal features.h shim for standalone getopt build on MSVC. */ | ||
| #ifndef _FEATURES_H | ||
| #define _FEATURES_H 1 | ||
| #ifdef __cplusplus | ||
| # define __BEGIN_DECLS extern "C" { | ||
| # define __END_DECLS } | ||
| #else | ||
| # define __BEGIN_DECLS | ||
| # define __END_DECLS | ||
| #endif | ||
| #ifndef __THROW | ||
| # define __THROW | ||
| #endif | ||
| #ifndef __nonnull | ||
| # define __nonnull(params) | ||
| #endif | ||
| #endif /* _FEATURES_H */ |
| /* Internal declarations for getopt. | ||
| Copyright (C) 1989-2026 Free Software Foundation, Inc. | ||
| This file is part of the GNU C Library and is also part of gnulib. | ||
| Patches to this file should be submitted to both projects. | ||
| The GNU C Library is free software; you can redistribute it and/or | ||
| modify it under the terms of the GNU Lesser General Public | ||
| License as published by the Free Software Foundation; either | ||
| version 2.1 of the License, or (at your option) any later version. | ||
| The GNU C Library is distributed in the hope that it will be useful, | ||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| Lesser General Public License for more details. | ||
| You should have received a copy of the GNU Lesser General Public | ||
| License along with the GNU C Library; if not, see | ||
| <https://www.gnu.org/licenses/>. */ | ||
| #ifndef _GETOPT_INT_H | ||
| #define _GETOPT_INT_H 1 | ||
| #include <getopt.h> | ||
| extern int _getopt_internal (int ___argc, char **___argv, | ||
| const char *__shortopts, | ||
| const struct option *__longopts, int *__longind, | ||
| int __long_only, int __posixly_correct); | ||
| /* Reentrant versions which can handle parsing multiple argument | ||
| vectors at the same time. */ | ||
| /* Describe how to deal with options that follow non-option ARGV-elements. | ||
| REQUIRE_ORDER means don't recognize them as options; stop option | ||
| processing when the first non-option is seen. This is what POSIX | ||
| specifies should happen. | ||
| PERMUTE means permute the contents of ARGV as we scan, so that | ||
| eventually all the non-options are at the end. This allows options | ||
| to be given in any order, even with programs that were not written | ||
| to expect this. | ||
| RETURN_IN_ORDER is an option available to programs that were | ||
| written to expect options and other ARGV-elements in any order | ||
| and that care about the ordering of the two. We describe each | ||
| non-option ARGV-element as if it were the argument of an option | ||
| with character code 1. | ||
| The special argument '--' forces an end of option-scanning regardless | ||
| of the value of 'ordering'. In the case of RETURN_IN_ORDER, only | ||
| '--' can cause 'getopt' to return -1 with 'optind' != ARGC. */ | ||
| enum __ord | ||
| { | ||
| REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER | ||
| }; | ||
| /* Data type for reentrant functions. */ | ||
| struct _getopt_data | ||
| { | ||
| /* These have exactly the same meaning as the corresponding global | ||
| variables, except that they are used for the reentrant | ||
| versions of getopt. */ | ||
| int optind; | ||
| int opterr; | ||
| int optopt; | ||
| char *optarg; | ||
| /* Internal members. */ | ||
| /* True if the internal members have been initialized. */ | ||
| int __initialized; | ||
| /* The next char to be scanned in the option-element | ||
| in which the last option character we returned was found. | ||
| This allows us to pick up the scan where we left off. | ||
| If this is zero, or a null string, it means resume the scan | ||
| by advancing to the next ARGV-element. */ | ||
| char *__nextchar; | ||
| /* See __ord above. */ | ||
| enum __ord __ordering; | ||
| /* Handle permutation of arguments. */ | ||
| /* Describe the part of ARGV that contains non-options that have | ||
| been skipped. 'first_nonopt' is the index in ARGV of the first | ||
| of them; 'last_nonopt' is the index after the last of them. */ | ||
| int __first_nonopt; | ||
| int __last_nonopt; | ||
| }; | ||
| /* The initializer is necessary to set OPTIND and OPTERR to their | ||
| default values and to clear the initialization flag. */ | ||
| #define _GETOPT_DATA_INITIALIZER { 1, 1 } | ||
| extern int _getopt_internal_r (int ___argc, char **___argv, | ||
| const char *__shortopts, | ||
| const struct option *__longopts, int *__longind, | ||
| int __long_only, struct _getopt_data *__data, | ||
| int __posixly_correct); | ||
| extern int _getopt_long_r (int ___argc, char **___argv, | ||
| const char *__shortopts, | ||
| const struct option *__longopts, int *__longind, | ||
| struct _getopt_data *__data); | ||
| extern int _getopt_long_only_r (int ___argc, char **___argv, | ||
| const char *__shortopts, | ||
| const struct option *__longopts, | ||
| int *__longind, | ||
| struct _getopt_data *__data); | ||
| #endif /* getopt_int.h */ |
| /* Minimal gettext shim for standalone getopt build on MSVC. */ | ||
| #ifndef _GETTEXT_H | ||
| #define _GETTEXT_H 1 | ||
| #define gettext(msgid) (msgid) | ||
| #endif /* _GETTEXT_H */ |
| /* Minimal unistd.h shim for standalone getopt build on MSVC. */ | ||
| #ifndef _UNISTD_H | ||
| #define _UNISTD_H 1 | ||
| #endif /* _UNISTD_H */ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
| #include <stdint.h> | ||
| #include <stddef.h> | ||
| #include <stdlib.h> | ||
| #include <string.h> | ||
| #include <libusb.h> | ||
| /* Fuzz the public BOS device-capability parsers. | ||
| We construct a valid BOS dev-cap header (3 bytes) + variable payload. | ||
| No hardware needed; ctx=NULL is fine. */ | ||
| int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { | ||
| if (!data) return 0; | ||
| /* bLength is 3 (header) + payload; must fit in one byte. */ | ||
| uint8_t payload_len = (size > 252) ? 252 : (uint8_t)size; /* 255 - 3 = 252 */ | ||
| size_t total_len = 3u + (size_t)payload_len; | ||
| /* Allocate header + payload for the flexible array member. */ | ||
| struct libusb_bos_dev_capability_descriptor *devcap = | ||
| (struct libusb_bos_dev_capability_descriptor*) | ||
| malloc(sizeof(*devcap) + payload_len); | ||
| if (!devcap) return 0; | ||
| devcap->bLength = (uint8_t)total_len; | ||
| devcap->bDescriptorType = LIBUSB_DT_DEVICE_CAPABILITY; /* 0x10 */ | ||
| /* Copy fuzz bytes into the variable-length payload. */ | ||
| if (payload_len) memcpy(devcap->dev_capability_data, data, payload_len); | ||
| /* 1) USB 2.0 Extension dev-cap */ | ||
| devcap->bDevCapabilityType = LIBUSB_BT_USB_2_0_EXTENSION; | ||
| struct libusb_usb_2_0_extension_descriptor *d20 = NULL; | ||
| (void)libusb_get_usb_2_0_extension_descriptor(NULL, devcap, &d20); | ||
| libusb_free_usb_2_0_extension_descriptor(d20); | ||
| /* 2) SuperSpeed USB Device Capability dev-cap */ | ||
| devcap->bDevCapabilityType = LIBUSB_BT_SS_USB_DEVICE_CAPABILITY; | ||
| struct libusb_ss_usb_device_capability_descriptor *dss = NULL; | ||
| (void)libusb_get_ss_usb_device_capability_descriptor(NULL, devcap, &dss); | ||
| libusb_free_ss_usb_device_capability_descriptor(dss); | ||
| /* 3) Container ID dev-cap */ | ||
| devcap->bDevCapabilityType = LIBUSB_BT_CONTAINER_ID; | ||
| struct libusb_container_id_descriptor *dcid = NULL; | ||
| (void)libusb_get_container_id_descriptor(NULL, devcap, &dcid); | ||
| libusb_free_container_id_descriptor(dcid); | ||
| free(devcap); | ||
| return 0; | ||
| } |
| /* Fuzz the static descriptor parsers in libusb/descriptor.c. | ||
| * | ||
| * The relevant entry points (parse_configuration, parse_interface, | ||
| * parse_endpoint, parse_iad_array) all have file-local linkage, so we | ||
| * compile them into this fuzzer's translation unit by including the source | ||
| * file directly. The libusb_get_*() public APIs that wrap them require a | ||
| * libusb_device with a backend, which is more setup than a fuzz target | ||
| * needs. The unity-include keeps the fuzzer focused on parser logic only. | ||
| * | ||
| * Three small symbol stubs satisfy references that descriptor.c makes into | ||
| * other libusb translation units; none of those code paths are reachable | ||
| * from the parsers we exercise here. | ||
| * | ||
| * Built as part of the OSS-Fuzz integration; not exercised by the autotools | ||
| * test suite. | ||
| */ | ||
| #include <stdint.h> | ||
| #include <stddef.h> | ||
| #include <stdlib.h> | ||
| #include <string.h> | ||
| /* ENABLE_LOGGING affects only printf-like macros we don't care about. */ | ||
| #define ENABLE_LOGGING 0 | ||
| #include "config.h" | ||
| #include "libusbi.h" | ||
| #include "../../libusb/descriptor.c" | ||
| /* Stubs for symbols that descriptor.c references but the parsers we fuzz | ||
| * never reach (logging sink, backend dispatch, control-transfer wrapper). */ | ||
| const struct usbi_os_backend usbi_backend = {0}; | ||
| void usbi_log(struct libusb_context *ctx, enum libusb_log_level level, | ||
| const char *function, const char *format, ...) { | ||
| (void)ctx; (void)level; (void)function; (void)format; | ||
| } | ||
| int libusb_control_transfer(libusb_device_handle *dev_handle, | ||
| uint8_t bmRequestType, uint8_t bRequest, | ||
| uint16_t wValue, uint16_t wIndex, | ||
| unsigned char *data, uint16_t wLength, | ||
| unsigned int timeout) { | ||
| (void)dev_handle; (void)bmRequestType; (void)bRequest; (void)wValue; | ||
| (void)wIndex; (void)data; (void)wLength; (void)timeout; | ||
| return LIBUSB_ERROR_NOT_SUPPORTED; | ||
| } | ||
| int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { | ||
| /* The limit of 8192 is comfortably above practical cases and | ||
| * still keeping libFuzzer iterations fast and the corpus small */ | ||
| if (size < LIBUSB_DT_CONFIG_SIZE || size > 8192) | ||
| return 0; | ||
| /* Every call gets a fresh exact-size copy so any byte read past the | ||
| * end is caught by ASan. The fuzzer must not observe state between | ||
| * iterations. */ | ||
| uint8_t *buf; | ||
| /* (1) parse_configuration -> parse_interface -> parse_endpoint */ | ||
| buf = malloc(size); | ||
| if (!buf) return 0; | ||
| memcpy(buf, data, size); | ||
| { | ||
| struct libusb_config_descriptor cfg; | ||
| memset(&cfg, 0, sizeof(cfg)); | ||
| if (parse_configuration(NULL, &cfg, buf, (int)size) >= 0) | ||
| clear_configuration(&cfg); | ||
| } | ||
| free(buf); | ||
| /* (2) parse_iad_array */ | ||
| buf = malloc(size); | ||
| if (!buf) return 0; | ||
| memcpy(buf, data, size); | ||
| { | ||
| struct libusb_interface_association_descriptor_array iad; | ||
| memset(&iad, 0, sizeof(iad)); | ||
| if (parse_iad_array(NULL, &iad, buf, (int)size) == LIBUSB_SUCCESS) | ||
| free((void *)iad.iad); | ||
| } | ||
| free(buf); | ||
| return 0; | ||
| } |
| #include "hotplug.h" | ||
| struct HotPlug { | ||
| libusb_device* device; | ||
| libusb_hotplug_event event; | ||
| Napi::ObjectReference* hotplugThis; | ||
| }; | ||
| int LIBUSB_CALL hotplug_callback(libusb_context* ctx, libusb_device* device, libusb_hotplug_event event, void* user_data) { | ||
| libusb_ref_device(device); | ||
| ModuleData* instanceData = (ModuleData*)user_data; | ||
| instanceData->hotplugQueue.post(new HotPlug {device, event, &instanceData->hotplugThis}); | ||
| return 0; | ||
| } | ||
| class HotPlugManagerLibUsb: public HotPlugManager { | ||
| bool supportedHotplugEvents() { | ||
| int res = libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG); | ||
| return res > 0; | ||
| } | ||
| void enableHotplug(const Napi::Env& env, ModuleData* instanceData) { | ||
| libusb_context* usb_context = instanceData->usb_context; | ||
| CHECK_USB(libusb_hotplug_register_callback( | ||
| usb_context, | ||
| (libusb_hotplug_event)(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT), | ||
| (libusb_hotplug_flag)0, | ||
| LIBUSB_HOTPLUG_MATCH_ANY, | ||
| LIBUSB_HOTPLUG_MATCH_ANY, | ||
| LIBUSB_HOTPLUG_MATCH_ANY, | ||
| hotplug_callback, | ||
| instanceData, | ||
| &hotplugHandle | ||
| )); | ||
| } | ||
| void disableHotplug(const Napi::Env& env, ModuleData* instanceData) { | ||
| libusb_context* usb_context = instanceData->usb_context; | ||
| libusb_hotplug_deregister_callback(usb_context, hotplugHandle); | ||
| } | ||
| libusb_hotplug_callback_handle hotplugHandle; | ||
| }; | ||
| std::unique_ptr<HotPlugManager> HotPlugManager::create() { | ||
| return std::make_unique<HotPlugManagerLibUsb>(); | ||
| } | ||
| void handleHotplug(HotPlug* info) { | ||
| Napi::ObjectReference* hotplugThis = info->hotplugThis; | ||
| Napi::Env env = hotplugThis->Env(); | ||
| Napi::HandleScope scope(env); | ||
| libusb_device* dev = info->device; | ||
| libusb_hotplug_event event = info->event; | ||
| delete info; | ||
| DEBUG_LOG("HandleHotplug %p %i", dev, event); | ||
| Napi::Object v8dev = Device::get(env, dev); | ||
| libusb_unref_device(dev); | ||
| Napi::Object v8VidPid = Napi::Object::New(env); | ||
| auto deviceDescriptor = v8dev.Get("deviceDescriptor"); | ||
| if (deviceDescriptor.IsObject()) { | ||
| v8VidPid.Set("idVendor", deviceDescriptor.As<Napi::Object>().Get("idVendor")); | ||
| v8VidPid.Set("idProduct", deviceDescriptor.As<Napi::Object>().Get("idProduct")); | ||
| } | ||
| Napi::String eventName; | ||
| Napi::String changeEventName; | ||
| if (LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED == event) { | ||
| DEBUG_LOG("Device arrived"); | ||
| eventName = Napi::String::New(env, "attach"); | ||
| changeEventName = Napi::String::New(env, "attachIds"); | ||
| } else if (LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT == event) { | ||
| DEBUG_LOG("Device left"); | ||
| eventName = Napi::String::New(env, "detach"); | ||
| changeEventName = Napi::String::New(env, "detachIds"); | ||
| } else { | ||
| DEBUG_LOG("Unhandled hotplug event %d\n", event); | ||
| return; | ||
| } | ||
| hotplugThis->Get("emit").As<Napi::Function>().MakeCallback(hotplugThis->Value(), { eventName, v8dev }); | ||
| hotplugThis->Get("emit").As<Napi::Function>().MakeCallback(hotplugThis->Value(), { changeEventName, v8VidPid }); | ||
| } |
| #ifndef _USB_HOTPLUG_H | ||
| #define _USB_HOTPLUG_H | ||
| #include "node_usb.h" | ||
| class HotPlugManager { | ||
| public: | ||
| static std::unique_ptr<HotPlugManager> create(); | ||
| virtual bool supportedHotplugEvents() = 0; | ||
| virtual void enableHotplug(const Napi::Env& env, ModuleData* instanceData) = 0; | ||
| virtual void disableHotplug(const Napi::Env& env, ModuleData* instanceData) = 0; | ||
| }; | ||
| void handleHotplug(HotPlug* info); | ||
| #endif |
+6
-0
| # Changelog | ||
| ## [2.18.0] - 2026-06-06 | ||
| ### Changed | ||
| - Updated libusb to v1.0.30 - [`950`](https://github.com/node-usb/node-usb/pull/950) ([Rob Moran](https://github.com/thegecko)) | ||
| - Switched to using libusb hotplug support for Windows - [`953`](https://github.com/node-usb/node-usb/pull/953) ([Rob Moran](https://github.com/thegecko)) | ||
| ## [2.17.0] - 2026-01-18 | ||
@@ -4,0 +10,0 @@ |
| import type { DeviceDescriptor, ConfigDescriptor, BosDescriptor } from './descriptors'; | ||
| import type { ExtendedDevice } from './device'; | ||
| /** | ||
@@ -27,3 +28,3 @@ * Return a list of `Device` objects for the USB devices attached to the system. | ||
| export declare function useUsbDkBackend(): void; | ||
| export declare function _supportedHotplugEvents(): number; | ||
| export declare function _supportedHotplugEvents(): boolean; | ||
| export declare function _enableHotplugEvents(): void; | ||
@@ -57,3 +58,3 @@ export declare function _disableHotplugEvents(): void; | ||
| /** Represents a USB device. */ | ||
| export declare class Device { | ||
| export declare class Device extends ExtendedDevice { | ||
| /** Integer USB device number */ | ||
@@ -269,1 +270,25 @@ busNumber: number; | ||
| export declare const LIBUSB_ERROR_OTHER: number; | ||
| export declare interface EventListeners<T> { | ||
| newListener: keyof T; | ||
| removeListener: keyof T; | ||
| } | ||
| export declare interface DeviceIds { | ||
| idVendor: number; | ||
| idProduct: number; | ||
| } | ||
| export declare interface DeviceEvents extends EventListeners<DeviceEvents> { | ||
| attach: Device; | ||
| detach: Device; | ||
| attachIds: DeviceIds; | ||
| detachIds: DeviceIds; | ||
| } | ||
| export declare function addListener<K extends keyof DeviceEvents>(event: K, listener: (arg: DeviceEvents[K]) => void): void; | ||
| export declare function removeListener<K extends keyof DeviceEvents>(event: K, listener: (arg: DeviceEvents[K]) => void): void; | ||
| export declare function on<K extends keyof DeviceEvents>(event: K, listener: (arg: DeviceEvents[K]) => void): void; | ||
| export declare function off<K extends keyof DeviceEvents>(event: K, listener: (arg: DeviceEvents[K]) => void): void; | ||
| export declare function once<K extends keyof DeviceEvents>(event: K, listener: (arg: DeviceEvents[K]) => void): void; | ||
| export declare function listeners<K extends keyof DeviceEvents>(event: K): ((arg: DeviceEvents[K]) => void)[]; | ||
| export declare function rawListeners<K extends keyof DeviceEvents>(event: K): ((arg: DeviceEvents[K]) => void)[]; | ||
| export declare function removeAllListeners<K extends keyof DeviceEvents>(event?: K): void; | ||
| export declare function emit<K extends keyof DeviceEvents>(event: K, arg: DeviceEvents[K]): boolean; | ||
| export declare function listenerCount<K extends keyof DeviceEvents>(event: K): number; |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"bindings.js","sourceRoot":"","sources":["../../tsc/usb/bindings.ts"],"names":[],"mappings":";AAAA,+CAA+C;AAC/C,mDAAmD;AACnD,2CAA2C;;AAE3C,+BAA4B;AAG5B,uDAAuD;AACvD,MAAM,GAAG,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,IAAA,WAAI,EAAC,SAAS,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;AAChG,MAAM,CAAC,OAAO,GAAG,GAAG,CAAC"} | ||
| {"version":3,"file":"bindings.js","sourceRoot":"","sources":["../../tsc/usb/bindings.ts"],"names":[],"mappings":";AAAA,+CAA+C;AAC/C,mDAAmD;AACnD,2CAA2C;;AAE3C,+BAA4B;AAI5B,uDAAuD;AACvD,MAAM,GAAG,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,IAAA,WAAI,EAAC,SAAS,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;AAChG,MAAM,CAAC,OAAO,GAAG,GAAG,CAAC"} |
+0
-29
@@ -1,31 +0,2 @@ | ||
| import { ExtendedDevice } from './device'; | ||
| import * as usb from './bindings'; | ||
| interface EventListeners<T> { | ||
| newListener: keyof T; | ||
| removeListener: keyof T; | ||
| } | ||
| interface DeviceIds { | ||
| idVendor: number; | ||
| idProduct: number; | ||
| } | ||
| declare module './bindings' { | ||
| interface Device extends ExtendedDevice { | ||
| } | ||
| interface DeviceEvents extends EventListeners<DeviceEvents> { | ||
| attach: Device; | ||
| detach: Device; | ||
| attachIds: DeviceIds; | ||
| detachIds: DeviceIds; | ||
| } | ||
| function addListener<K extends keyof DeviceEvents>(event: K, listener: (arg: DeviceEvents[K]) => void): void; | ||
| function removeListener<K extends keyof DeviceEvents>(event: K, listener: (arg: DeviceEvents[K]) => void): void; | ||
| function on<K extends keyof DeviceEvents>(event: K, listener: (arg: DeviceEvents[K]) => void): void; | ||
| function off<K extends keyof DeviceEvents>(event: K, listener: (arg: DeviceEvents[K]) => void): void; | ||
| function once<K extends keyof DeviceEvents>(event: K, listener: (arg: DeviceEvents[K]) => void): void; | ||
| function listeners<K extends keyof DeviceEvents>(event: K): ((arg: DeviceEvents[K]) => void)[]; | ||
| function rawListeners<K extends keyof DeviceEvents>(event: K): ((arg: DeviceEvents[K]) => void)[]; | ||
| function removeAllListeners<K extends keyof DeviceEvents>(event?: K): void; | ||
| function emit<K extends keyof DeviceEvents>(event: K, arg: DeviceEvents[K]): boolean; | ||
| function listenerCount<K extends keyof DeviceEvents>(event: K): number; | ||
| } | ||
| export = usb; |
+4
-18
@@ -58,22 +58,13 @@ "use strict"; | ||
| }; | ||
| // Devices changed event handler | ||
| const devicesChanged = () => setTimeout(() => emitHotplugEvents(), usb.pollHotplugDelay); | ||
| // Hotplug control | ||
| let hotplugSupported = 0; | ||
| let hotplugSupported = false; | ||
| const startHotplug = () => { | ||
| hotplugSupported = usb.pollHotplug ? 0 : usb._supportedHotplugEvents(); | ||
| if (hotplugSupported !== 1) { | ||
| // Collect initial devices when not using libusb | ||
| hotPlugDevices = new Set(usb.getDeviceList()); | ||
| } | ||
| hotplugSupported = usb.pollHotplug ? false : usb._supportedHotplugEvents(); | ||
| if (hotplugSupported) { | ||
| // Use hotplug event emitters | ||
| usb._enableHotplugEvents(); | ||
| if (hotplugSupported === 2) { | ||
| // Use hotplug ID events to trigger a change check | ||
| usb.on('attachIds', devicesChanged); | ||
| usb.on('detachIds', devicesChanged); | ||
| } | ||
| } | ||
| else { | ||
| // Collect initial devices | ||
| hotPlugDevices = new Set(usb.getDeviceList()); | ||
| // Fallback to using polling to check for changes | ||
@@ -87,7 +78,2 @@ pollHotplug(true); | ||
| usb._disableHotplugEvents(); | ||
| if (hotplugSupported === 2) { | ||
| // Remove hotplug ID event listeners | ||
| usb.off('attachIds', devicesChanged); | ||
| usb.off('detachIds', devicesChanged); | ||
| } | ||
| } | ||
@@ -94,0 +80,0 @@ else { |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"index.js","sourceRoot":"","sources":["../../tsc/usb/index.ts"],"names":[],"mappings":";AAAA,mCAAsC;AACtC,qCAA0C;AAC1C,kCAAkC;AAElC,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC;IACjB,+BAA+B;IAC/B,OAAO,CAAC,IAAI,CAAC,8BAA8B,CAAC,CAAC;AACjD,CAAC;AAED,MAAM,CAAC,cAAc,CAAC,GAAG,EAAE,qBAAY,CAAC,SAAS,CAAC,CAAC;AACnD,MAAM,CAAC,cAAc,CAAC,GAAG,EAAE,aAAa,EAAE;IACtC,KAAK,EAAE,KAAK;IACZ,QAAQ,EAAE,IAAI;CACjB,CAAC,CAAC;AAEH,MAAM,CAAC,cAAc,CAAC,GAAG,EAAE,kBAAkB,EAAE;IAC3C,KAAK,EAAE,GAAG;IACV,QAAQ,EAAE,IAAI;CACjB,CAAC,CAAC;AAEH,4DAA4D;AAC5D,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC;IACb,MAAM,CAAC,mBAAmB,CAAC,uBAAc,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE;QAChE,MAAM,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,EAAE,MAAM,CAAC,wBAAwB,CAAC,uBAAc,CAAC,SAAS,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;IAC9I,CAAC,CAAC,CAAC;AACP,CAAC;AAoCD,sDAAsD;AACtD,IAAI,cAAc,GAAG,IAAI,GAAG,EAAc,CAAC;AAE3C,oIAAoI;AACpI,MAAM,iBAAiB,GAAG,GAAG,EAAE;IAC3B,0BAA0B;IAC1B,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC,CAAC;IAE7C,wBAAwB;IACxB,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC3B,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YAC9B,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAC/B,CAAC;IACL,CAAC;IAED,wBAAwB;IACxB,KAAK,MAAM,MAAM,IAAI,cAAc,EAAE,CAAC;QAClC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YACvB,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAC/B,CAAC;IACL,CAAC;IAED,cAAc,GAAG,OAAO,CAAC;AAC7B,CAAC,CAAC;AAEF,yFAAyF;AACzF,IAAI,cAAc,GAAG,KAAK,CAAC;AAC3B,MAAM,WAAW,GAAG,CAAC,KAAK,GAAG,KAAK,EAAE,EAAE;IAClC,IAAI,KAAK,EAAE,CAAC;QACR,cAAc,GAAG,IAAI,CAAC;IAC1B,CAAC;SAAM,IAAI,CAAC,cAAc,EAAE,CAAC;QACzB,OAAO;IACX,CAAC;SAAM,CAAC;QACJ,iBAAiB,EAAE,CAAC;IACxB,CAAC;IAED,UAAU,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,EAAE,GAAG,CAAC,gBAAgB,CAAC,CAAC;AAC1D,CAAC,CAAC;AAEF,gCAAgC;AAChC,MAAM,cAAc,GAAG,GAAG,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,CAAC,iBAAiB,EAAE,EAAE,GAAG,CAAC,gBAAgB,CAAC,CAAC;AAEzF,kBAAkB;AAClB,IAAI,gBAAgB,GAAG,CAAC,CAAC;AACzB,MAAM,YAAY,GAAG,GAAG,EAAE;IACtB,gBAAgB,GAAG,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,uBAAuB,EAAE,CAAC;IAEvE,IAAI,gBAAgB,KAAK,CAAC,EAAE,CAAC;QACzB,gDAAgD;QAChD,cAAc,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC,CAAC;IAClD,CAAC;IAED,IAAI,gBAAgB,EAAE,CAAC;QACnB,6BAA6B;QAC7B,GAAG,CAAC,oBAAoB,EAAE,CAAC;QAE3B,IAAI,gBAAgB,KAAK,CAAC,EAAE,CAAC;YACzB,kDAAkD;YAClD,GAAG,CAAC,EAAE,CAAC,WAAW,EAAE,cAAc,CAAC,CAAC;YACpC,GAAG,CAAC,EAAE,CAAC,WAAW,EAAE,cAAc,CAAC,CAAC;QACxC,CAAC;IACL,CAAC;SAAM,CAAC;QACJ,iDAAiD;QACjD,WAAW,CAAC,IAAI,CAAC,CAAC;IACtB,CAAC;AACL,CAAC,CAAC;AAEF,MAAM,WAAW,GAAG,GAAG,EAAE;IACrB,IAAI,gBAAgB,EAAE,CAAC;QACnB,yBAAyB;QACzB,GAAG,CAAC,qBAAqB,EAAE,CAAC;QAE5B,IAAI,gBAAgB,KAAK,CAAC,EAAE,CAAC;YACzB,oCAAoC;YACpC,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,cAAc,CAAC,CAAC;YACrC,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,cAAc,CAAC,CAAC;QACzC,CAAC;IACL,CAAC;SAAM,CAAC;QACJ,eAAe;QACf,cAAc,GAAG,KAAK,CAAC;IAC3B,CAAC;AACL,CAAC,CAAC;AAEF,GAAG,CAAC,EAAE,CAAC,aAAa,EAAE,KAAK,CAAC,EAAE;IAC1B,IAAI,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC3C,OAAO;IACX,CAAC;IACD,MAAM,aAAa,GAAG,GAAG,CAAC,aAAa,CAAC,QAAQ,CAAC,GAAG,GAAG,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;IAChF,IAAI,aAAa,KAAK,CAAC,EAAE,CAAC;QACtB,YAAY,EAAE,CAAC;IACnB,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,GAAG,CAAC,EAAE,CAAC,gBAAgB,EAAE,KAAK,CAAC,EAAE;IAC7B,IAAI,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC3C,OAAO;IACX,CAAC;IACD,MAAM,aAAa,GAAG,GAAG,CAAC,aAAa,CAAC,QAAQ,CAAC,GAAG,GAAG,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;IAChF,IAAI,aAAa,KAAK,CAAC,EAAE,CAAC;QACtB,WAAW,EAAE,CAAC;IAClB,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,iBAAS,GAAG,CAAC"} | ||
| {"version":3,"file":"index.js","sourceRoot":"","sources":["../../tsc/usb/index.ts"],"names":[],"mappings":";AAAA,mCAAsC;AACtC,qCAA0C;AAC1C,kCAAkC;AAElC,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC;IACjB,+BAA+B;IAC/B,OAAO,CAAC,IAAI,CAAC,8BAA8B,CAAC,CAAC;AACjD,CAAC;AAED,MAAM,CAAC,cAAc,CAAC,GAAG,EAAE,qBAAY,CAAC,SAAS,CAAC,CAAC;AACnD,MAAM,CAAC,cAAc,CAAC,GAAG,EAAE,aAAa,EAAE;IACtC,KAAK,EAAE,KAAK;IACZ,QAAQ,EAAE,IAAI;CACjB,CAAC,CAAC;AAEH,MAAM,CAAC,cAAc,CAAC,GAAG,EAAE,kBAAkB,EAAE;IAC3C,KAAK,EAAE,GAAG;IACV,QAAQ,EAAE,IAAI;CACjB,CAAC,CAAC;AAEH,4DAA4D;AAC5D,IAAI,GAAG,CAAC,MAAM,EAAE,CAAC;IACb,MAAM,CAAC,mBAAmB,CAAC,uBAAc,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE;QAChE,MAAM,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,EAAE,MAAM,CAAC,wBAAwB,CAAC,uBAAc,CAAC,SAAS,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;IAC9I,CAAC,CAAC,CAAC;AACP,CAAC;AAED,sDAAsD;AACtD,IAAI,cAAc,GAAG,IAAI,GAAG,EAAc,CAAC;AAE3C,oIAAoI;AACpI,MAAM,iBAAiB,GAAG,GAAG,EAAE;IAC3B,0BAA0B;IAC1B,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC,CAAC;IAE7C,wBAAwB;IACxB,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC3B,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YAC9B,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAC/B,CAAC;IACL,CAAC;IAED,wBAAwB;IACxB,KAAK,MAAM,MAAM,IAAI,cAAc,EAAE,CAAC;QAClC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YACvB,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAC/B,CAAC;IACL,CAAC;IAED,cAAc,GAAG,OAAO,CAAC;AAC7B,CAAC,CAAC;AAEF,yFAAyF;AACzF,IAAI,cAAc,GAAG,KAAK,CAAC;AAC3B,MAAM,WAAW,GAAG,CAAC,KAAK,GAAG,KAAK,EAAE,EAAE;IAClC,IAAI,KAAK,EAAE,CAAC;QACR,cAAc,GAAG,IAAI,CAAC;IAC1B,CAAC;SAAM,IAAI,CAAC,cAAc,EAAE,CAAC;QACzB,OAAO;IACX,CAAC;SAAM,CAAC;QACJ,iBAAiB,EAAE,CAAC;IACxB,CAAC;IAED,UAAU,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,EAAE,GAAG,CAAC,gBAAgB,CAAC,CAAC;AAC1D,CAAC,CAAC;AAEF,kBAAkB;AAClB,IAAI,gBAAgB,GAAG,KAAK,CAAC;AAC7B,MAAM,YAAY,GAAG,GAAG,EAAE;IACtB,gBAAgB,GAAG,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,uBAAuB,EAAE,CAAC;IAE3E,IAAI,gBAAgB,EAAE,CAAC;QACnB,6BAA6B;QAC7B,GAAG,CAAC,oBAAoB,EAAE,CAAC;IAC/B,CAAC;SAAM,CAAC;QACJ,0BAA0B;QAC1B,cAAc,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC,CAAC;QAC9C,iDAAiD;QACjD,WAAW,CAAC,IAAI,CAAC,CAAC;IACtB,CAAC;AACL,CAAC,CAAC;AAEF,MAAM,WAAW,GAAG,GAAG,EAAE;IACrB,IAAI,gBAAgB,EAAE,CAAC;QACnB,yBAAyB;QACzB,GAAG,CAAC,qBAAqB,EAAE,CAAC;IAChC,CAAC;SAAM,CAAC;QACJ,eAAe;QACf,cAAc,GAAG,KAAK,CAAC;IAC3B,CAAC;AACL,CAAC,CAAC;AAEF,GAAG,CAAC,EAAE,CAAC,aAAa,EAAE,KAAK,CAAC,EAAE;IAC1B,IAAI,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC3C,OAAO;IACX,CAAC;IACD,MAAM,aAAa,GAAG,GAAG,CAAC,aAAa,CAAC,QAAQ,CAAC,GAAG,GAAG,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;IAChF,IAAI,aAAa,KAAK,CAAC,EAAE,CAAC;QACtB,YAAY,EAAE,CAAC;IACnB,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,GAAG,CAAC,EAAE,CAAC,gBAAgB,EAAE,KAAK,CAAC,EAAE;IAC7B,IAAI,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC3C,OAAO;IACX,CAAC;IACD,MAAM,aAAa,GAAG,GAAG,CAAC,aAAa,CAAC,QAAQ,CAAC,GAAG,GAAG,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;IAChF,IAAI,aAAa,KAAK,CAAC,EAAE,CAAC;QACtB,WAAW,EAAE,CAAC;IAClB,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,iBAAS,GAAG,CAAC"} |
@@ -33,2 +33,6 @@ #!/bin/bash | ||
| ;; | ||
| --werror) | ||
| cflags+=" -Werror" | ||
| shift | ||
| ;; | ||
| --) | ||
@@ -57,3 +61,3 @@ shift | ||
| cflags="-O2" | ||
| cflags+=" -O2" | ||
@@ -60,0 +64,0 @@ # enable extra warnings |
+14
-0
@@ -18,2 +18,3 @@ Copyright © 2001 Johannes Erdfelt <johannes@erdfelt.com> | ||
| Adrian Bunk | ||
| Adrien Bertrand | ||
| Adrien Destugues | ||
@@ -28,2 +29,3 @@ Akshay Jaggi | ||
| Alexander Stein | ||
| Alex Birkett | ||
| Alex Feinman | ||
@@ -40,2 +42,3 @@ Alex Vatchenko | ||
| Anthony Clay | ||
| Antoine Moreau | ||
| Antonio Ospite | ||
@@ -90,4 +93,6 @@ Artem Egorkine | ||
| Haidong Zheng | ||
| Hannes Franke | ||
| Hans Ulrich Niedermann | ||
| Harry Mallon | ||
| Hartwig Wiesmann | ||
| Hector Martin | ||
@@ -131,5 +136,7 @@ Hoi-Ho Chan | ||
| Liang Yunwang | ||
| Logan Gallois | ||
| Lonnie Abelbeck | ||
| Luca Longinotti | ||
| Luz Paz | ||
| Łukasz Bartosik | ||
| Mac Wang | ||
@@ -140,2 +147,3 @@ Marco Trevisan (Treviño) | ||
| Mark Kuo | ||
| Mark Lee | ||
| Markus Heidelberg | ||
@@ -147,2 +155,3 @@ Martin Ettl | ||
| Mathias Hjärtström | ||
| Matt Liberty | ||
| Matthew Stapleton | ||
@@ -161,3 +170,5 @@ Matthias Bolte | ||
| Nicholas Corgan | ||
| Nicolas Bats | ||
| Niklas Gürtler | ||
| Ole André Vadla Ravnås | ||
| Oleksand Radovenchyk | ||
@@ -202,2 +213,3 @@ Omri Iluz | ||
| Theo Buehler | ||
| Theodor Engøy | ||
| Thomas Röfer | ||
@@ -212,2 +224,3 @@ Tim Hutt | ||
| Uwe Bonnes | ||
| Vadim Mikhailov | ||
| Vasily Khoruzhick | ||
@@ -221,2 +234,3 @@ Vegard Storheil Eriksen | ||
| Vladimir Beloborodov | ||
| Vladimir Gladkov | ||
| William Orr | ||
@@ -223,0 +237,0 @@ William Skellenger |
@@ -26,3 +26,4 @@ /* -*- Mode: C; indent-tabs-mode:t ; c-basic-offset:8 -*- */ | ||
| int done = 0; | ||
| int done_attach = 0; | ||
| int done_detach = 0; | ||
| libusb_device_handle *handle = NULL; | ||
@@ -33,2 +34,3 @@ | ||
| struct libusb_device_descriptor desc; | ||
| libusb_device_handle *new_handle; | ||
| int rc; | ||
@@ -50,9 +52,14 @@ | ||
| if (handle) { | ||
| libusb_close (handle); | ||
| handle = NULL; | ||
| } | ||
| rc = libusb_open (dev, &handle); | ||
| if (LIBUSB_SUCCESS != rc) { | ||
| rc = libusb_open (dev, &new_handle); | ||
| if (LIBUSB_SUCCESS == rc) { | ||
| if (handle) { | ||
| libusb_close (handle); | ||
| } | ||
| handle = new_handle; | ||
| } else if (LIBUSB_ERROR_ACCESS != rc | ||
| #if defined(PLATFORM_WINDOWS) | ||
| && LIBUSB_ERROR_NOT_SUPPORTED != rc | ||
| && LIBUSB_ERROR_NOT_FOUND != rc | ||
| #endif | ||
| ) { | ||
| fprintf (stderr, "No access to device: %s\n", | ||
@@ -62,3 +69,3 @@ libusb_strerror((enum libusb_error)rc)); | ||
| done++; | ||
| done_attach++; | ||
@@ -92,3 +99,3 @@ return 0; | ||
| done++; | ||
| done_detach++; | ||
@@ -138,3 +145,3 @@ return 0; | ||
| while (done < 2) { | ||
| while (done_detach < done_attach || done_attach == 0) { | ||
| rc = libusb_handle_events (NULL); | ||
@@ -147,2 +154,3 @@ if (LIBUSB_SUCCESS != rc) | ||
| if (handle) { | ||
| printf ("Warning: Closing left-over open handle\n"); | ||
| libusb_close (handle); | ||
@@ -149,0 +157,0 @@ } |
@@ -21,6 +21,7 @@ /* | ||
| #include <stdio.h> | ||
| #include <string.h> | ||
| #include "libusb.h" | ||
| static void print_devs(libusb_device **devs) | ||
| static void print_devs(libusb_device **devs, int verbose) | ||
| { | ||
@@ -30,2 +31,3 @@ libusb_device *dev; | ||
| uint8_t path[8]; | ||
| char string_buffer[LIBUSB_DEVICE_STRING_BYTES_MAX]; | ||
@@ -50,2 +52,22 @@ while ((dev = devs[i++]) != NULL) { | ||
| } | ||
| if (verbose) { | ||
| r = libusb_get_device_string(dev, LIBUSB_DEVICE_STRING_MANUFACTURER, | ||
| string_buffer, sizeof(string_buffer)); | ||
| if (r >= 0) { | ||
| printf("\n manufacturer = %s", string_buffer); | ||
| } | ||
| r = libusb_get_device_string(dev, LIBUSB_DEVICE_STRING_PRODUCT, | ||
| string_buffer, sizeof(string_buffer)); | ||
| if (r >= 0) { | ||
| printf("\n product = %s", string_buffer); | ||
| } | ||
| r = libusb_get_device_string(dev, LIBUSB_DEVICE_STRING_SERIAL_NUMBER, | ||
| string_buffer, sizeof(string_buffer)); | ||
| if (r >= 0) { | ||
| printf("\n serial_number = %s", string_buffer); | ||
| } | ||
| } | ||
| printf("\n"); | ||
@@ -55,4 +77,10 @@ } | ||
| int main(void) | ||
| static int usage(void) { | ||
| printf("usage: listdevs [--verbose]\n"); | ||
| return 1; | ||
| } | ||
| int main(int argc, char *argv[]) | ||
| { | ||
| int verbose = 0; | ||
| libusb_device **devs; | ||
@@ -62,2 +90,12 @@ int r; | ||
| --argc; ++argv; /* consume argument */ | ||
| while (argc) { | ||
| if ((0 == strcmp("-v", argv[0])) || (0 == strcmp("--verbose", argv[0]))) { | ||
| ++verbose; | ||
| --argc; ++argv; /* consume argument */ | ||
| } else { | ||
| return usage(); | ||
| } | ||
| } | ||
| r = libusb_init_context(/*ctx=*/NULL, /*options=*/NULL, /*num_options=*/0); | ||
@@ -73,3 +111,3 @@ if (r < 0) | ||
| print_devs(devs); | ||
| print_devs(devs, verbose); | ||
| libusb_free_device_list(devs, 1); | ||
@@ -76,0 +114,0 @@ |
@@ -34,3 +34,8 @@ /* | ||
| #define putenv _putenv | ||
| /* Disable: warning C5287: operands are different enum types */ | ||
| #if (_MSC_VER > 1800) | ||
| /* Disable: warning C5287: operands are different enum types, supported after Visual Studio 2013 */ | ||
| #pragma warning(disable:5287) | ||
| #endif | ||
| #endif | ||
@@ -502,3 +507,3 @@ // Future versions of libusb will use usb_interface instead of interface | ||
| pid[i] = buffer[16+i]; | ||
| rev[i/2] = buffer[32+i/2]; // instead of another loop | ||
| rev[i/2] = buffer[32+(i/2)]; // instead of another loop | ||
| } | ||
@@ -505,0 +510,0 @@ vid[8] = 0; |
+163
-14
@@ -41,6 +41,6 @@ /* -*- Mode: C; indent-tabs-mode:t ; c-basic-offset:8 -*- */ | ||
| { | ||
| return (uint32_t)((uint32_t)p[3] << 24 | | ||
| (uint32_t)p[2] << 16 | | ||
| (uint32_t)p[1] << 8 | | ||
| (uint32_t)p[0]); | ||
| return ((uint32_t)p[3] << 24 | | ||
| (uint32_t)p[2] << 16 | | ||
| (uint32_t)p[1] << 8 | | ||
| (uint32_t)p[0]); | ||
| } | ||
@@ -152,2 +152,4 @@ | ||
| free((void *)ifp->extra); | ||
| ifp->extra = NULL; | ||
| ifp->extra_length = 0; | ||
| if (ifp->endpoint) { | ||
@@ -161,2 +163,4 @@ uint8_t j; | ||
| free((void *)ifp->endpoint); | ||
| ifp->endpoint = NULL; | ||
| ifp->bNumEndpoints = 0; | ||
| } | ||
@@ -166,2 +170,3 @@ } | ||
| usb_interface->altsetting = NULL; | ||
| usb_interface->num_altsetting = 0; | ||
| } | ||
@@ -248,2 +253,6 @@ | ||
| size, header->bLength); | ||
| /* Keep the invariant: bNumEndpoints > 0 implies | ||
| * endpoint != NULL. The endpoint array isn't | ||
| * allocated yet on this early return. */ | ||
| ifp->bNumEndpoints = 0; | ||
| return parsed; | ||
@@ -330,3 +339,7 @@ } | ||
| free((void *)config->interface); | ||
| config->interface = NULL; | ||
| config->bNumInterfaces = 0; | ||
| free((void *)config->extra); | ||
| config->extra = NULL; | ||
| config->extra_length = 0; | ||
| } | ||
@@ -432,3 +445,3 @@ | ||
| r = parse_interface(ctx, usb_interface + i, buffer, (int)size); | ||
| r = parse_interface(ctx, usb_interface + i, buffer, size); | ||
| if (r < 0) | ||
@@ -787,3 +800,3 @@ goto err; | ||
| _bos = calloc(1, sizeof(*_bos) + bos_desc->bNumDeviceCaps * sizeof(void *)); | ||
| _bos = calloc(1, sizeof(*_bos) + (bos_desc->bNumDeviceCaps * sizeof(void *))); | ||
| if (!_bos) | ||
@@ -1093,3 +1106,3 @@ return LIBUSB_ERROR_NO_MEM; | ||
| uint8_t numSublikSpeedAttributes = (parsedDescriptor.bmAttributes & 0xF) + 1; | ||
| _ssplus_cap = malloc(sizeof(struct libusb_ssplus_usb_device_capability_descriptor) + numSublikSpeedAttributes * sizeof(struct libusb_ssplus_sublink_attribute)); | ||
| _ssplus_cap = malloc(sizeof(struct libusb_ssplus_usb_device_capability_descriptor) + (numSublikSpeedAttributes * sizeof(struct libusb_ssplus_sublink_attribute))); | ||
| if (!_ssplus_cap) | ||
@@ -1108,3 +1121,4 @@ return LIBUSB_ERROR_NO_MEM; | ||
| /* Check that we have enough to read all the sublink attributes */ | ||
| if (dev_cap->bLength < LIBUSB_BT_SSPLUS_USB_DEVICE_CAPABILITY_SIZE + _ssplus_cap->numSublinkSpeedAttributes * sizeof(uint32_t)) { | ||
| if (dev_cap->bLength < LIBUSB_BT_SSPLUS_USB_DEVICE_CAPABILITY_SIZE + (_ssplus_cap->numSublinkSpeedAttributes * sizeof(uint32_t))) { | ||
| free(_ssplus_cap); | ||
| usbi_err(ctx, "short ssplus capability descriptor, unable to read sublinks: Not enough data"); | ||
@@ -1117,3 +1131,3 @@ return LIBUSB_ERROR_IO; | ||
| for(uint8_t i = 0 ; i < _ssplus_cap->numSublinkSpeedAttributes ; i++) { | ||
| uint32_t attr = ReadLittleEndian32(base + i * sizeof(uint32_t)); | ||
| uint32_t attr = ReadLittleEndian32(base + (i * sizeof(uint32_t))); | ||
| _ssplus_cap->sublinkSpeedAttributes[i].ssid = attr & 0x0f; | ||
@@ -1325,5 +1339,6 @@ _ssplus_cap->sublinkSpeedAttributes[i].mantissa = attr >> 16; | ||
| return r; | ||
| else if (r != 4 || str.desc.bLength < 4 || str.desc.bDescriptorType != LIBUSB_DT_STRING) | ||
| else if (r != 4 || str.desc.bLength < 4 || str.desc.bDescriptorType != LIBUSB_DT_STRING) { | ||
| usbi_warn(HANDLE_CTX(dev_handle), "invalid language ID string descriptor"); | ||
| return LIBUSB_ERROR_IO; | ||
| else if (str.desc.bLength & 1) | ||
| } else if (str.desc.bLength & 1) | ||
| usbi_warn(HANDLE_CTX(dev_handle), "suspicious bLength %u for language ID string descriptor", str.desc.bLength); | ||
@@ -1380,3 +1395,3 @@ | ||
| iad_array->length = 0; | ||
| while (consumed < size) { | ||
| while (size - consumed >= DESC_HEADER_LENGTH) { | ||
| header.bLength = buf[0]; | ||
@@ -1389,5 +1404,5 @@ header.bDescriptorType = buf[1]; | ||
| } | ||
| else if (header.bLength > size) { | ||
| else if (header.bLength > size - consumed) { | ||
| usbi_warn(ctx, "short config descriptor read %d/%u", | ||
| size, header.bLength); | ||
| size - consumed, header.bLength); | ||
| return LIBUSB_ERROR_IO; | ||
@@ -1573,1 +1588,135 @@ } | ||
| } | ||
| /* | ||
| * \brief Copy a UTF-8 string with proper truncation if needed. | ||
| * | ||
| * \param tgt The target utf-8 string. If NULL, then tgt is ignored, | ||
| * tgt_size is forced to 0, and this function returns the | ||
| * required tgt_size for a subsequent call to this function. | ||
| * \param src The source utf-8 string. If NULL, then | ||
| * the source string is empty, the return length is 1, | ||
| * and, if tgt is not NULL, this function | ||
| * sets tgt[0] to the null terminator. | ||
| * \param tgt_size The size of target in bytes. | ||
| * \return the length of src in bytes, including the null terminator. | ||
| * | ||
| * utf8_copy(NULL, src, 0) is equivalent to strlen(src) + 1. | ||
| */ | ||
| static int usbi_utf8_copy(char *tgt, char const *src, int tgt_size) { | ||
| uint8_t* t = (uint8_t*)tgt; | ||
| uint8_t const* s = (uint8_t const*)src; | ||
| if (NULL == src) { | ||
| if ((NULL != tgt) && (tgt_size > 0)) { | ||
| tgt[0] = 0; | ||
| } | ||
| return 1; | ||
| } | ||
| if ((NULL == tgt) || (tgt_size <= 0)) { | ||
| return (int)(strlen(src) + 1); | ||
| } | ||
| // copy UTF-8 string and compute length | ||
| int k = 0; | ||
| for (k = 0; s[k]; ++k) { | ||
| if (k < tgt_size) { | ||
| t[k] = s[k]; | ||
| } else { | ||
| break; | ||
| } | ||
| } | ||
| if (k >= tgt_size) { | ||
| // truncate respecting UTF-8 character boundaries | ||
| int idx = tgt_size - 1; | ||
| while (idx && (0x80 == (t[idx] & 0xC0))) { // utf-8 continuation byte | ||
| --idx; | ||
| } | ||
| t[idx] = 0; | ||
| return (int)(strlen(src) + 1); | ||
| } else { | ||
| t[k++] = 0; | ||
| return k; | ||
| } | ||
| } | ||
| /** \ingroup libusb_desc | ||
| * Retrieve a device string without needing to open the device. | ||
| * | ||
| * Since version v1.0.30 \ref LIBUSB_API_VERSION >= 0x0100010C | ||
| * | ||
| * \param dev the target device | ||
| * \param string_type the string type to retrieve | ||
| * \param data the data buffer for the UTF-8 encoded string. | ||
| * \param length the size of the data buffer in bytes. | ||
| * USB string descriptors cannot be longer than | ||
| * LIBUSB_DEVICE_STRING_BYTES_MAX. | ||
| * \returns a negative error code or | ||
| * the actual string length in bytes including the null terminator. | ||
| * \see libusb_get_string_descriptor() | ||
| * \see libusb_get_string_descriptor_ascii() | ||
| * | ||
| * This function works when the device is still closed since it relies | ||
| * on the operating system to provide the string. The operating system | ||
| * normally reads and caches the common string descriptors during | ||
| * USB enumeration. | ||
| * | ||
| * Since the USB string descriptor could be processed by the OS, | ||
| * this function returns a UTF-8 encoded string. | ||
| * | ||
| * The string will be returned untranslated or in the default OS language | ||
| * when supported by the OS and USB device. | ||
| * | ||
| * One way to call this function is using a buffer on the stack: | ||
| * | ||
| * char buffer[LIBUSB_DEVICE_STRING_BYTES_MAX]; | ||
| * int ret = libusb_get_device_string(dev, LIBUSB_DEVICE_STRING_SERIAL_NUMBER, buffer, sizeof(buffer)); | ||
| * if (ret < 0) { | ||
| * // handle error | ||
| * } | ||
| * | ||
| * This function is commonly used to get the serial number to allow | ||
| * for device selection before opening the selected device. | ||
| */ | ||
| int API_EXPORTED libusb_get_device_string(libusb_device *dev, | ||
| enum libusb_device_string_type string_type, char *data, int length) { | ||
| char * s; | ||
| if (NULL == dev) { | ||
| return LIBUSB_ERROR_INVALID_PARAM; | ||
| } | ||
| if ((string_type < 0) || (string_type >= LIBUSB_DEVICE_STRING_COUNT)) { | ||
| return LIBUSB_ERROR_INVALID_PARAM; | ||
| } | ||
| if (length <= 0) { | ||
| return LIBUSB_ERROR_INVALID_PARAM; | ||
| } | ||
| if (NULL == data) { | ||
| length = 0; | ||
| data = NULL; | ||
| } else if (length > 0) { | ||
| *data = 0; // return an empty string on errors when possible | ||
| } | ||
| if (NULL == dev->device_strings_utf8[string_type]) { | ||
| if (usbi_backend.get_device_string) { | ||
| s = malloc(LIBUSB_DEVICE_STRING_BYTES_MAX); | ||
| int rv = usbi_backend.get_device_string(dev, string_type, s, LIBUSB_DEVICE_STRING_BYTES_MAX); | ||
| if (rv < 0) { | ||
| free(s); | ||
| return rv; | ||
| } else { | ||
| dev->device_strings_utf8[string_type] = s; | ||
| } | ||
| } else { | ||
| return LIBUSB_ERROR_NOT_SUPPORTED; | ||
| } | ||
| } | ||
| s = dev->device_strings_utf8[string_type]; | ||
| if (NULL == s) { | ||
| return LIBUSB_ERROR_NOT_SUPPORTED; | ||
| } | ||
| return usbi_utf8_copy(data, s, length); | ||
| } |
+89
-25
@@ -372,14 +372,14 @@ /* | ||
| usbi_mutex_t usb_devs_lock; | ||
| struct list_head usb_devs; | ||
| usbi_mutex_t usb_devs_lock; | ||
| /* A list of open handles. Backends are free to traverse this if required. | ||
| */ | ||
| usbi_mutex_t open_devs_lock; | ||
| struct list_head open_devs; | ||
| usbi_mutex_t open_devs_lock; | ||
| /* A list of registered hotplug callbacks */ | ||
| usbi_mutex_t hotplug_cbs_lock; | ||
| struct list_head hotplug_cbs; | ||
| libusb_hotplug_callback_handle next_hotplug_cb_handle; | ||
| usbi_mutex_t hotplug_cbs_lock; | ||
@@ -389,2 +389,5 @@ /* A flag to indicate that the context is ready for hotplug notifications */ | ||
| /* Note paths taking both this and usbi_transfer->lock must always | ||
| * take this lock first */ | ||
| usbi_mutex_t flying_transfers_lock; | ||
| /* this is a list of in-flight transfer handles, sorted by timeout | ||
@@ -395,5 +398,2 @@ * expiration. URBs to timeout the soonest are placed at the beginning of | ||
| struct list_head flying_transfers; | ||
| /* Note paths taking both this and usbi_transfer->lock must always | ||
| * take this lock first */ | ||
| usbi_mutex_t flying_transfers_lock; /* for flying_transfers and timeout_flags */ | ||
@@ -457,4 +457,4 @@ #if !defined(PLATFORM_WINDOWS) | ||
| extern usbi_mutex_static_t active_contexts_lock; | ||
| extern struct list_head active_contexts_list; | ||
| extern usbi_mutex_static_t active_contexts_lock; | ||
@@ -530,2 +530,4 @@ static inline struct libusb_context *usbi_get_context(struct libusb_context *ctx) | ||
| usbi_atomic_t attached; | ||
| char * device_strings_utf8[LIBUSB_DEVICE_STRING_COUNT]; | ||
| }; | ||
@@ -591,3 +593,13 @@ | ||
| struct usbi_transfer { | ||
| int num_iso_packets; | ||
| /* this lock is held during libusb_submit_transfer() and | ||
| * libusb_cancel_transfer() (allowing the OS backend to prevent duplicate | ||
| * cancellation, submission-during-cancellation, etc). the OS backend | ||
| * should also take this lock in the handle_events path, to prevent the user | ||
| * cancelling the transfer from another thread while you are processing | ||
| * its completion (presumably there would be races within your OS backend | ||
| * if this were possible). | ||
| * Note paths taking both this and the flying_transfers_lock must | ||
| * always take the flying_transfers_lock first */ | ||
| usbi_mutex_t lock; | ||
| struct list_head list; | ||
@@ -605,13 +617,2 @@ struct list_head completed_list; | ||
| /* this lock is held during libusb_submit_transfer() and | ||
| * libusb_cancel_transfer() (allowing the OS backend to prevent duplicate | ||
| * cancellation, submission-during-cancellation, etc). the OS backend | ||
| * should also take this lock in the handle_events path, to prevent the user | ||
| * cancelling the transfer from another thread while you are processing | ||
| * its completion (presumably there would be races within your OS backend | ||
| * if this were possible). | ||
| * Note paths taking both this and the flying_transfers_lock must | ||
| * always take the flying_transfers_lock first */ | ||
| usbi_mutex_t lock; | ||
| void *priv; | ||
@@ -824,3 +825,3 @@ }; | ||
| int usbi_sanitize_device(struct libusb_device *dev); | ||
| void usbi_handle_disconnect(struct libusb_device_handle *dev_handle); | ||
| void usbi_handle_disconnect(struct libusb_context *ctx, struct libusb_device_handle *dev_handle); | ||
@@ -832,10 +833,15 @@ int usbi_handle_transfer_completion(struct usbi_transfer *itransfer, | ||
| void usbi_attach_device(struct libusb_device *dev); | ||
| void usbi_detach_device(struct libusb_device *dev); | ||
| void usbi_connect_device(struct libusb_device *dev); | ||
| void usbi_disconnect_device(struct libusb_device *dev); | ||
| struct usbi_event_source_data { | ||
| usbi_os_handle_t os_handle; | ||
| short poll_events; | ||
| }; | ||
| struct usbi_event_source { | ||
| struct usbi_event_source_data { | ||
| usbi_os_handle_t os_handle; | ||
| short poll_events; | ||
| } data; | ||
| struct usbi_event_source_data data; | ||
| struct list_head list; | ||
@@ -1007,3 +1013,5 @@ }; | ||
| * usbi_sanitize_device() to perform some final sanity checks on the | ||
| * device. Assuming all of the above succeeded, we can now continue. | ||
| * device. Assuming all of the above succeeded, call | ||
| * usbi_connect_device() to add the device to the context's device list | ||
| * and make it discoverable by usbi_get_device_by_session_id(). | ||
| * If any of the above failed, remember to unreference the device that | ||
@@ -1034,2 +1042,27 @@ * was returned by usbi_alloc_device(). | ||
| /* Retrieve a device string without needing to open the device. | ||
| * | ||
| * The string should be retrieved without opening the device | ||
| * and ideally without performing USB transactions to the device. | ||
| * Most operating systems read and cache the common string | ||
| * descriptors. Use the OS-specific calls to retrieve these strings. | ||
| * | ||
| * Since the USB string descriptor could be processed by the OS, | ||
| * this function returns a UTF-8 encoded string. | ||
| * | ||
| * The string will be returned untranslated or in the default OS language | ||
| * when supported by the OS and USB device. | ||
| * | ||
| * This function must not write more than length bytes into data, | ||
| * including the null terminator. | ||
| * | ||
| * Return: | ||
| * - The actual length in bytes including the null termintor on success. | ||
| * - LIBUSB_ERROR_NO_DEVICE if device not found. | ||
| * - LIBUSB_ERROR_INVALID_PARAM if any parameter is invalid. | ||
| * - another LIBUSB_ERROR code on other failure | ||
| */ | ||
| int (*get_device_string)(libusb_device *dev, | ||
| enum libusb_device_string_type string_type, char *data, int length); | ||
| /* Apps which were written before hotplug support, may listen for | ||
@@ -1367,2 +1400,33 @@ * hotplug events on their own and call libusb_get_device_list on | ||
| /** Check if RAW_IO is supported by an endpoint. | ||
| * | ||
| * Return: | ||
| * - 1 if yes | ||
| * - 0 if no | ||
| * - a LIBUSB_ERROR code on failure | ||
| */ | ||
| int (*endpoint_supports_raw_io)(struct libusb_device_handle* dev_handle, | ||
| uint8_t endpoint); | ||
| /** Enable/disable RAW_IO for an endpoint. | ||
| * | ||
| * Return: | ||
| * - 0 on success | ||
| * - LIBUSB_ERROR_NOT_SUPPORTED if RAW_IO is not supported by the endpoint | ||
| * - another LIBUSB_ERROR code on other failure | ||
| */ | ||
| int (*endpoint_set_raw_io)(struct libusb_device_handle* dev_handle, | ||
| uint8_t endpoint, int enable); | ||
| /* Retrieve the maximum transfer size in bytes supported for WinUSB RAW_IO | ||
| * for an inbound bulk or interrupt endpoint on an open device. Optional. | ||
| * | ||
| * Return: | ||
| * - a positive maximum transfer size on success | ||
| * - a LIBUSB_ERROR code on failure | ||
| */ | ||
| int (*get_max_raw_io_transfer_size)( | ||
| struct libusb_device_handle *dev_handle, | ||
| uint8_t endpoint); | ||
| /* Destroy a device. Optional. | ||
@@ -1369,0 +1433,0 @@ * |
@@ -120,3 +120,3 @@ /* | ||
| int refcount; | ||
| bool in_reenumerate; | ||
| atomic_bool in_reenumerate; | ||
| int capture_count; | ||
@@ -129,2 +129,10 @@ }; | ||
| struct darwin_interface { | ||
| usb_interface_t interface; | ||
| uint8_t num_endpoints; | ||
| CFRunLoopSourceRef cfSource; | ||
| uint64_t frames[256]; | ||
| uint8_t endpoint_addrs[USB_MAXENDPOINTS]; | ||
| }; | ||
| struct darwin_device_handle_priv { | ||
@@ -134,9 +142,3 @@ bool is_open; | ||
| struct darwin_interface { | ||
| usb_interface_t interface; | ||
| uint8_t num_endpoints; | ||
| CFRunLoopSourceRef cfSource; | ||
| uint64_t frames[256]; | ||
| uint8_t endpoint_addrs[USB_MAXENDPOINTS]; | ||
| } interfaces[USB_MAXINTERFACES]; | ||
| struct darwin_interface interfaces[USB_MAXINTERFACES]; | ||
| }; | ||
@@ -143,0 +145,0 @@ |
@@ -541,13 +541,32 @@ /* | ||
| for (uint8_t j = 0; j < configurations_len; j++) { | ||
| // Note: requesting more than (platform-specific limit) bytes | ||
| // here will cause the transfer to fail, see | ||
| // https://crbug.com/1489414. Use the most common limit of 4096 | ||
| // bytes for now. | ||
| constexpr uint16_t MAX_CTRL_BUFFER_LENGTH = 4096; | ||
| auto result = co_await_try( | ||
| requestDescriptor(LIBUSB_DT_CONFIG, j, MAX_CTRL_BUFFER_LENGTH)); | ||
| if (auto error = getTransferStatus(result)) { | ||
| // Read descriptor header first to discover target length. | ||
| auto config_header_result = co_await_try( | ||
| requestDescriptor(LIBUSB_DT_CONFIG, j, LIBUSB_DT_CONFIG_SIZE)); | ||
| if (auto error = getTransferStatus(config_header_result)) { | ||
| co_return error; | ||
| } | ||
| auto configVal = result["data"]; | ||
| union usbi_config_desc_buf config_header = {}; | ||
| copyFromDataView(config_header.buf, config_header_result["data"]); | ||
| if (config_header.desc.bDescriptorType != LIBUSB_DT_CONFIG || | ||
| config_header.desc.bLength < LIBUSB_DT_CONFIG_SIZE) { | ||
| co_return LIBUSB_ERROR_IO; | ||
| } | ||
| auto config_total_length = | ||
| libusb_le16_to_cpu(config_header.desc.wTotalLength); | ||
| if (config_total_length < LIBUSB_DT_CONFIG_SIZE) { | ||
| co_return LIBUSB_ERROR_IO; | ||
| } | ||
| auto config_result = co_await_try( | ||
| requestDescriptor(LIBUSB_DT_CONFIG, j, config_total_length)); | ||
| if (auto error = getTransferStatus(config_result)) { | ||
| co_return error; | ||
| } | ||
| auto configVal = config_result["data"]; | ||
| if (configVal["byteLength"].as<size_t>() < config_total_length) { | ||
| co_return LIBUSB_ERROR_IO; | ||
| } | ||
| auto configLen = configVal["byteLength"].as<size_t>(); | ||
@@ -627,2 +646,4 @@ auto& config = configurations.emplace_back( | ||
| dev->device_address = dev->port_number = (uint8_t)session_id; | ||
| usbi_connect_device(dev); | ||
| } | ||
@@ -711,3 +732,3 @@ *devs = discovered_devs_append(*devs, dev); | ||
| int em_set_configuration(libusb_device_handle* dev_handle, int config) { | ||
| return WebUsbDevicePtr(dev_handle)->awaitOnMain("setConfiguration", config); | ||
| return WebUsbDevicePtr(dev_handle)->awaitOnMain("selectConfiguration", config); | ||
| } | ||
@@ -714,0 +735,0 @@ |
@@ -189,2 +189,3 @@ /* | ||
| /*.get_device_list =*/ NULL, | ||
| /*.get_device_string =*/ NULL, | ||
| /*.hotplug_poll =*/ NULL, | ||
@@ -218,2 +219,5 @@ /*.wrap_sys_device =*/ NULL, | ||
| /*.attach_kernel_driver =*/ NULL, | ||
| /*.endpoint_supports_raw_io =*/ NULL, | ||
| /*.endpoint_set_raw_io =*/ NULL, | ||
| /*.get_max_raw_io_transfer_size =*/ NULL, | ||
@@ -220,0 +224,0 @@ /*.destroy_device =*/ NULL, |
@@ -174,2 +174,4 @@ /* | ||
| goto error; | ||
| usbi_connect_device(dev); | ||
| } | ||
@@ -176,0 +178,0 @@ close(fd); |
@@ -203,2 +203,4 @@ /* | ||
| } | ||
| usbi_connect_device(dev); | ||
| } | ||
@@ -205,0 +207,0 @@ |
@@ -630,2 +630,4 @@ /* | ||
| } | ||
| usbi_connect_device(dev); | ||
| } else { | ||
@@ -632,0 +634,0 @@ devpriv = usbi_get_device_priv(dev); |
@@ -41,3 +41,3 @@ /* | ||
| void usbi_cond_init(pthread_cond_t *cond) | ||
| void usbi_cond_init(usbi_cond_t *cond) | ||
| { | ||
@@ -56,4 +56,4 @@ #ifdef HAVE_PTHREAD_CONDATTR_SETCLOCK | ||
| int usbi_cond_timedwait(pthread_cond_t *cond, | ||
| pthread_mutex_t *mutex, const struct timeval *tv) | ||
| int usbi_cond_timedwait(usbi_cond_t *cond, | ||
| usbi_mutex_t *mutex, const struct timeval *tv) | ||
| { | ||
@@ -60,0 +60,0 @@ struct timespec timeout; |
@@ -54,3 +54,4 @@ /* | ||
| { | ||
| return pthread_mutex_trylock(mutex) == 0; | ||
| int mutexIsLocked = pthread_mutex_trylock(mutex) == 0; | ||
| return mutexIsLocked; | ||
| } | ||
@@ -62,4 +63,5 @@ static inline void usbi_mutex_destroy(usbi_mutex_t *mutex) | ||
| #define USBI_COND_INITIALIZER PTHREAD_COND_INITIALIZER | ||
| typedef pthread_cond_t usbi_cond_t; | ||
| void usbi_cond_init(pthread_cond_t *cond); | ||
| void usbi_cond_init(usbi_cond_t *cond); | ||
| static inline void usbi_cond_wait(usbi_cond_t *cond, usbi_mutex_t *mutex) | ||
@@ -71,2 +73,6 @@ { | ||
| usbi_mutex_t *mutex, const struct timeval *tv); | ||
| static inline void usbi_cond_signal(usbi_cond_t *cond) | ||
| { | ||
| PTHREAD_CHECK(pthread_cond_signal(cond)); | ||
| } | ||
| static inline void usbi_cond_broadcast(usbi_cond_t *cond) | ||
@@ -73,0 +79,0 @@ { |
@@ -53,3 +53,4 @@ /* | ||
| { | ||
| return TryEnterCriticalSection(mutex) != 0; | ||
| int mutexIsLocked = TryEnterCriticalSection(mutex) != 0; | ||
| return mutexIsLocked; | ||
| } | ||
@@ -56,0 +57,0 @@ static inline void usbi_mutex_destroy(usbi_mutex_t *mutex) |
@@ -32,2 +32,6 @@ /* | ||
| #if defined(LIBUSB_WINDOWS_HOTPLUG) | ||
| #include "windows_hotplug.h" | ||
| #endif | ||
| #define EPOCH_TIME UINT64_C(116444736000000000) // 1970.01.01 00:00:000 in MS Filetime | ||
@@ -496,5 +500,7 @@ | ||
| itransfer = TRANSFER_PRIV_TO_USBI_TRANSFER(transfer_priv); | ||
| #ifdef ENABLE_LOGGING | ||
| struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); | ||
| usbi_dbg(ctx, "transfer %p completed, length %lu", | ||
| transfer, ULONG_CAST(num_bytes)); | ||
| #endif | ||
| usbi_signal_transfer_completion(itransfer); | ||
@@ -570,2 +576,14 @@ } | ||
| #if defined(LIBUSB_WINDOWS_HOTPLUG) | ||
| if (init_count == 1) { | ||
| r = windows_start_event_monitor(); // Start-up hotplug event handler | ||
| if (r != LIBUSB_SUCCESS) { | ||
| usbi_err(ctx, "error starting hotplug event monitor"); | ||
| goto init_exit; | ||
| } | ||
| } | ||
| windows_initial_scan_devices(ctx); | ||
| #endif | ||
| init_exit: // Holds semaphore here | ||
@@ -602,2 +620,5 @@ if ((init_count == 1) && (r != LIBUSB_SUCCESS)) { // First init failed? | ||
| if (--init_count == 0) { // Last exit | ||
| #if defined(LIBUSB_WINDOWS_HOTPLUG) | ||
| windows_stop_event_monitor(); | ||
| #endif | ||
| if (usbdk_available) { | ||
@@ -631,2 +652,3 @@ usbdk_backend.exit(ctx); | ||
| #if !defined(LIBUSB_WINDOWS_HOTPLUG) | ||
| static int windows_get_device_list(struct libusb_context *ctx, struct discovered_devs **discdevs) | ||
@@ -637,3 +659,14 @@ { | ||
| } | ||
| #endif | ||
| static int windows_get_device_string(libusb_device *dev, | ||
| enum libusb_device_string_type string_type, char *data, int length) | ||
| { | ||
| struct windows_context_priv* priv = usbi_get_context_priv(DEVICE_CTX(dev)); | ||
| if (NULL != priv->backend->get_device_string) { | ||
| return priv->backend->get_device_string(dev, string_type, data, length); | ||
| } | ||
| return LIBUSB_ERROR_NOT_SUPPORTED; | ||
| } | ||
| static int windows_open(struct libusb_device_handle *dev_handle) | ||
@@ -726,2 +759,33 @@ { | ||
| static int windows_endpoint_supports_raw_io(libusb_device_handle* dev_handle, | ||
| uint8_t endpoint) | ||
| { | ||
| struct windows_context_priv *priv = usbi_get_context_priv(HANDLE_CTX(dev_handle)); | ||
| if (priv->backend->endpoint_supports_raw_io) | ||
| return priv->backend->endpoint_supports_raw_io(dev_handle, endpoint); | ||
| return LIBUSB_ERROR_NOT_SUPPORTED; | ||
| } | ||
| static int windows_endpoint_set_raw_io(libusb_device_handle* dev_handle, | ||
| uint8_t endpoint, int enable) | ||
| { | ||
| struct windows_context_priv *priv = usbi_get_context_priv(HANDLE_CTX(dev_handle)); | ||
| if (priv->backend->endpoint_supports_raw_io == NULL | ||
| || priv->backend->endpoint_supports_raw_io(dev_handle, endpoint) != 1 | ||
| || priv->backend->endpoint_set_raw_io == NULL) | ||
| return LIBUSB_ERROR_NOT_SUPPORTED; | ||
| return priv->backend->endpoint_set_raw_io(dev_handle, endpoint, enable); | ||
| } | ||
| static int windows_get_max_raw_io_transfer_size(struct libusb_device_handle *dev_handle, | ||
| uint8_t endpoint) | ||
| { | ||
| struct windows_context_priv *priv = usbi_get_context_priv(HANDLE_CTX(dev_handle)); | ||
| if (priv->backend->get_max_raw_io_transfer_size) | ||
| return priv->backend->get_max_raw_io_transfer_size(dev_handle, endpoint); | ||
| return LIBUSB_ERROR_NOT_SUPPORTED; | ||
| } | ||
| static int windows_submit_transfer(struct usbi_transfer *itransfer) | ||
@@ -810,4 +874,13 @@ { | ||
| DWORD result, bytes_transferred; | ||
| HANDLE transfer_handle; | ||
| if (GetOverlappedResult(transfer_priv->handle, &transfer_priv->overlapped, &bytes_transferred, FALSE)) | ||
| /* | ||
| * The submit path runs with itransfer->lock held. Grab the handle under | ||
| * the same lock so we do not race with submission/completion bookkeeping. | ||
| */ | ||
| usbi_mutex_lock(&itransfer->lock); | ||
| transfer_handle = transfer_priv->handle; | ||
| usbi_mutex_unlock(&itransfer->lock); | ||
| if (GetOverlappedResult(transfer_handle, &transfer_priv->overlapped, &bytes_transferred, FALSE)) | ||
| result = NO_ERROR; | ||
@@ -817,5 +890,7 @@ else | ||
| #ifdef ENABLE_LOGGING | ||
| struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); | ||
| usbi_dbg(ctx, "handling transfer %p completion with errcode %lu, length %lu", | ||
| transfer, ULONG_CAST(result), ULONG_CAST(bytes_transferred)); | ||
| #endif | ||
@@ -855,3 +930,5 @@ switch (result) { | ||
| usbi_mutex_lock(&itransfer->lock); | ||
| transfer_priv->handle = NULL; | ||
| usbi_mutex_unlock(&itransfer->lock); | ||
@@ -901,3 +978,8 @@ // Backend-specific cleanup | ||
| windows_set_option, | ||
| #if defined(LIBUSB_WINDOWS_HOTPLUG) | ||
| NULL, /* get_device_list */ | ||
| #else | ||
| windows_get_device_list, | ||
| #endif | ||
| windows_get_device_string, | ||
| NULL, /* hotplug_poll */ | ||
@@ -924,2 +1006,5 @@ NULL, /* wrap_sys_device */ | ||
| NULL, /* attach_kernel_driver */ | ||
| windows_endpoint_supports_raw_io, | ||
| windows_endpoint_set_raw_io, | ||
| windows_get_max_raw_io_transfer_size, | ||
| windows_destroy_device, | ||
@@ -926,0 +1011,0 @@ windows_submit_transfer, |
@@ -245,4 +245,9 @@ /* | ||
| bool initialized; | ||
| #if defined(LIBUSB_WINDOWS_HOTPLUG) | ||
| bool seen_during_scan; // set true for each device encountered during windows_get_device_list | ||
| bool seen_before_scan; // set true for each device encountered before windows_get_device_list | ||
| #endif | ||
| bool root_hub; | ||
| uint8_t active_config; | ||
| uint16_t langid; // cached USB language ID for string descriptor requests | ||
| uint8_t depth; // distance to HCD | ||
@@ -253,2 +258,7 @@ const struct windows_usb_api_backend *apib; | ||
| int sub_api; // for WinUSB-like APIs | ||
| usbi_mutex_t interface_lock; // protects usb_interface[] against concurrent | ||
| // claim/release/altsetting from different handles, | ||
| // and concurrent enumeration setup | ||
| // (set_composite_interface, set_hid_interface, | ||
| // and HUB_PASS/DEV_PASS in winusb_get_device_list) | ||
| struct { | ||
@@ -280,3 +290,3 @@ char *path; // each interface needs a device interface path, | ||
| }; | ||
| enum WINUSB_ZLP { | ||
@@ -326,2 +336,4 @@ WINUSB_ZLP_UNSET = 0, | ||
| struct discovered_devs **discdevs); | ||
| int (*get_device_string)(libusb_device *dev, | ||
| enum libusb_device_string_type string_type, char *data, int length); | ||
| int (*open)(struct libusb_device_handle *dev_handle); | ||
@@ -349,2 +361,9 @@ void (*close)(struct libusb_device_handle *dev_handle); | ||
| enum libusb_transfer_status (*copy_transfer_data)(struct usbi_transfer *itransfer, DWORD length); | ||
| int (*endpoint_supports_raw_io)(struct libusb_device_handle *dev_handle, | ||
| uint8_t endpoint); | ||
| int (*endpoint_set_raw_io)(struct libusb_device_handle *dev_handle, | ||
| uint8_t endpoint, int enable); | ||
| int (*get_max_raw_io_transfer_size)( | ||
| struct libusb_device_handle *dev_handle, | ||
| uint8_t endpoint); | ||
| }; | ||
@@ -351,0 +370,0 @@ |
@@ -344,13 +344,17 @@ /* | ||
| } | ||
| usbi_connect_device(dev); | ||
| } | ||
| discdevs = discovered_devs_append(*_discdevs, dev); | ||
| libusb_unref_device(dev); | ||
| if (!discdevs) { | ||
| usbi_err(ctx, "cannot append new device to list"); | ||
| r = LIBUSB_ERROR_NO_MEM; | ||
| goto func_exit; | ||
| if (_discdevs) { | ||
| discdevs = discovered_devs_append(*_discdevs, dev); | ||
| libusb_unref_device(dev); | ||
| if (!discdevs) { | ||
| usbi_err(ctx, "cannot append new device to list"); | ||
| r = LIBUSB_ERROR_NO_MEM; | ||
| goto func_exit; | ||
| } | ||
| *_discdevs = discdevs; | ||
| } | ||
| *_discdevs = discdevs; | ||
| } | ||
@@ -708,2 +712,3 @@ | ||
| usbdk_get_device_list, | ||
| NULL, /* usbdk_get_device_string */ | ||
| usbdk_open, | ||
@@ -726,2 +731,5 @@ usbdk_close, | ||
| usbdk_copy_transfer_data, | ||
| NULL, /* endpoint_supports_raw_io */ | ||
| NULL, /* endpoint_set_raw_io */ | ||
| NULL, /* get_max_raw_io_transfer_size */ | ||
| }; |
@@ -90,2 +90,5 @@ /* | ||
| enum libusb_transfer_status (*copy_transfer_data)(int sub_api, struct usbi_transfer *itransfer, DWORD length); | ||
| int (*endpoint_supports_raw_io)(int sub_api, struct libusb_device_handle *dev_handle, uint8_t endpoint); | ||
| int (*endpoint_set_raw_io)(int sub_api, struct libusb_device_handle *dev_handle, uint8_t endpoint, int enable); | ||
| int (*get_max_raw_io_transfer_size)(int sub_api, struct libusb_device_handle *dev_handle, uint8_t endpoint); | ||
| }; | ||
@@ -180,2 +183,3 @@ | ||
| usbi_mutex_init(&priv->interface_lock); | ||
| priv->apib = &usb_api_backend[USB_API_UNSUPPORTED]; | ||
@@ -211,2 +215,3 @@ priv->sub_api = SUB_API_NOTSET; | ||
| } | ||
| usbi_mutex_destroy(&priv->interface_lock); | ||
| } | ||
@@ -313,3 +318,3 @@ | ||
| // Most of the structures below need to be packed | ||
| #include <pshpack1.h> | ||
| #pragma pack(push, 1) | ||
@@ -408,3 +413,3 @@ typedef struct _USB_HUB_DESCRIPTOR { | ||
| #include <poppack.h> | ||
| #pragma pack(pop) | ||
@@ -451,3 +456,3 @@ #if defined(_MSC_VER) | ||
| #include <pshpack1.h> | ||
| #pragma pack(push, 1) | ||
@@ -462,3 +467,3 @@ typedef struct _WINUSB_SETUP_PACKET { | ||
| #include <poppack.h> | ||
| #pragma pack(pop) | ||
@@ -705,3 +710,3 @@ typedef PVOID WINUSB_INTERFACE_HANDLE, *PWINUSB_INTERFACE_HANDLE; | ||
| #include <pshpack1.h> | ||
| #pragma pack(push, 1) | ||
@@ -715,3 +720,3 @@ typedef struct _HIDD_ATTIRBUTES { | ||
| #include <poppack.h> | ||
| #pragma pack(pop) | ||
@@ -718,0 +723,0 @@ typedef USHORT USAGE; |
@@ -1,1 +0,1 @@ | ||
| #define LIBUSB_NANO 11953 | ||
| #define LIBUSB_NANO 12037 |
@@ -10,3 +10,3 @@ /* This file is parsed by m4 and windres and RC.EXE so please keep it simple. */ | ||
| #ifndef LIBUSB_MICRO | ||
| #define LIBUSB_MICRO 29 | ||
| #define LIBUSB_MICRO 30 | ||
| #endif | ||
@@ -13,0 +13,0 @@ #ifndef LIBUSB_NANO |
@@ -18,3 +18,3 @@ <?xml version="1.0" encoding="utf-8"?> | ||
| <!--Treat sources as utf-8--> | ||
| <AdditionalOptions Condition="'$(PlatformToolsetVersion)'>'120'">/utf-8 %(AdditionalOptions)</AdditionalOptions> | ||
| <AdditionalOptions Condition="'$(LibusbPlatformToolsetVersion)'>'120'">/utf-8 %(AdditionalOptions)</AdditionalOptions> | ||
| <DebugInformationFormat>ProgramDatabase</DebugInformationFormat> | ||
@@ -21,0 +21,0 @@ <DiagnosticsFormat>Caret</DiagnosticsFormat> |
@@ -1,2 +0,2 @@ | ||
| $toolsets = "v120", "v140", "v141", "v142", "v143" | ||
| $toolsets = "v120", "v140", "v141", "v142", "v143", "v145" | ||
| $platforms = "Win32", "x64", "ARM", "ARM64" | ||
@@ -17,2 +17,2 @@ $configurations = "Debug", "Release" | ||
| } | ||
| } | ||
| } |
@@ -33,2 +33,6 @@ /* config.h. Manual config for MSVC. */ | ||
| #pragma warning(disable:4996) | ||
| #if (_MSC_VER > 1800) | ||
| /* Disable: warning C5287: operands are different enum types, supported after Visual Studio 2013 */ | ||
| #pragma warning(disable:5287) | ||
| #endif | ||
@@ -35,0 +39,0 @@ #if defined(_PREFAST_) |
@@ -10,2 +10,3 @@ <?xml version="1.0" encoding="utf-8"?> | ||
| <PlatformToolset Condition="$(VisualStudioVersion)=='17.0'">v143</PlatformToolset> | ||
| <PlatformToolset Condition="$(VisualStudioVersion)=='18.0'">v145</PlatformToolset> | ||
| <!--We may need the equivalent of PlatformToolsetVersion before it's ready, so create it ourself--> | ||
@@ -48,2 +49,2 @@ <LibusbPlatformToolsetVersion>$(PlatformToolset.Substring(1))</LibusbPlatformToolsetVersion> | ||
| </PropertyGroup> | ||
| </Project> | ||
| </Project> |
@@ -11,12 +11,24 @@ <?xml version="1.0" encoding="utf-8"?> | ||
| </ClCompile> | ||
| <ClCompile Condition="'$(Configuration)'=='Debug-Hotplug'"> | ||
| <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> | ||
| </ClCompile> | ||
| <ClCompile Condition="'$(Configuration)'=='Debug-MT'"> | ||
| <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> | ||
| </ClCompile> | ||
| <ClCompile Condition="'$(Configuration)'=='Debug-Hotplug-MT'"> | ||
| <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> | ||
| </ClCompile> | ||
| <ClCompile Condition="'$(Configuration)'=='Release'"> | ||
| <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> | ||
| </ClCompile> | ||
| <ClCompile Condition="'$(Configuration)'=='Release-Hotplug'"> | ||
| <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary> | ||
| </ClCompile> | ||
| <ClCompile Condition="'$(Configuration)'=='Release-MT'"> | ||
| <RuntimeLibrary>MultiThreaded</RuntimeLibrary> | ||
| </ClCompile> | ||
| <ClCompile Condition="'$(Configuration)'=='Release-Hotplug-MT'"> | ||
| <RuntimeLibrary>MultiThreaded</RuntimeLibrary> | ||
| </ClCompile> | ||
| </ItemDefinitionGroup> | ||
| </Project> |
+451
-700
| /* Getopt for GNU. | ||
| NOTE: getopt is now part of the C library, so if you don't know what | ||
| "Keep this file name-space clean" means, talk to drepper@gnu.org | ||
| before changing it! | ||
| Copyright (C) 1987,88,89,90,91,92,93,94,95,96,98,99,2000,2001 | ||
| Free Software Foundation, Inc. | ||
| This file is part of the GNU C Library. | ||
| Copyright (C) 1987-2026 Free Software Foundation, Inc. | ||
| This file is part of the GNU C Library and is also part of gnulib. | ||
| Patches to this file should be submitted to both projects. | ||
@@ -20,93 +17,72 @@ The GNU C Library is free software; you can redistribute it and/or | ||
| You should have received a copy of the GNU Lesser General Public | ||
| License along with the GNU C Library; if not, write to the Free | ||
| Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA | ||
| 02111-1307 USA. */ | ||
| /* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>. | ||
| Ditto for AIX 3.2 and <stdlib.h>. */ | ||
| #ifndef _NO_PROTO | ||
| # define _NO_PROTO | ||
| #endif | ||
| License along with the GNU C Library; if not, see | ||
| <https://www.gnu.org/licenses/>. */ | ||
| #ifdef HAVE_CONFIG_H | ||
| #ifndef _LIBC | ||
| # include <config.h> | ||
| #endif | ||
| #if !defined __STDC__ || !__STDC__ | ||
| /* This is a separate conditional since some stdc systems | ||
| reject `defined (const)'. */ | ||
| # ifndef const | ||
| # define const | ||
| # endif | ||
| #endif | ||
| #include "getopt.h" | ||
| #include <stdio.h> | ||
| #include <stdlib.h> | ||
| #include <string.h> | ||
| #include <unistd.h> | ||
| /* Comment out all this code if we are using the GNU C Library, and are not | ||
| actually compiling the library itself. This code is part of the GNU C | ||
| Library, but also included in many other GNU distributions. Compiling | ||
| and linking in this code is a waste when using the GNU C library | ||
| (especially if it is a shared library). Rather than having every GNU | ||
| program understand `configure --with-gnu-libc' and omit the object files, | ||
| it is simpler to just do this in the source for each such file. */ | ||
| #define GETOPT_INTERFACE_VERSION 2 | ||
| #if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2 | ||
| # include <gnu-versions.h> | ||
| # if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION | ||
| # define ELIDE_CODE | ||
| #ifdef _LIBC | ||
| /* When used as part of glibc, error printing must be done differently | ||
| for standards compliance. getopt is not a cancellation point, so | ||
| it must not call functions that are, and it is specified by an | ||
| older standard than stdio locking, so it must not refer to | ||
| functions in the "user namespace" related to stdio locking. | ||
| Finally, it must use glibc's internal message translation so that | ||
| the messages are looked up in the proper text domain. */ | ||
| # include <libintl.h> | ||
| # define fprintf __fxprintf_nocancel | ||
| # define flockfile(fp) _IO_flockfile (fp) | ||
| # define funlockfile(fp) _IO_funlockfile (fp) | ||
| #else | ||
| # include "gettext.h" | ||
| # define _(msgid) gettext (msgid) | ||
| /* When used standalone, flockfile and funlockfile might not be | ||
| available. */ | ||
| # if (!defined _POSIX_THREAD_SAFE_FUNCTIONS \ | ||
| || (defined _WIN32 && ! defined __CYGWIN__)) | ||
| # define flockfile(fp) /* nop */ | ||
| # define funlockfile(fp) /* nop */ | ||
| # endif | ||
| /* When used standalone, do not attempt to use alloca. */ | ||
| # define __libc_use_alloca(size) 0 | ||
| # undef alloca | ||
| # define alloca(size) (abort (), (void *)0) | ||
| #endif | ||
| #ifndef ELIDE_CODE | ||
| /* This implementation of 'getopt' has three modes for handling | ||
| options interspersed with non-option arguments. It can stop | ||
| scanning for options at the first non-option argument encountered, | ||
| as POSIX specifies. It can continue scanning for options after the | ||
| first non-option argument, but permute 'argv' as it goes so that, | ||
| after 'getopt' is done, all the options precede all the non-option | ||
| arguments and 'optind' points to the first non-option argument. | ||
| Or, it can report non-option arguments as if they were arguments to | ||
| the option character '\x01'. | ||
| The default behavior of 'getopt_long' is to permute the argument list. | ||
| When this implementation is used standalone, the default behavior of | ||
| 'getopt' is to stop at the first non-option argument, but when it is | ||
| used as part of GNU libc it also permutes the argument list. In both | ||
| cases, setting the environment variable POSIXLY_CORRECT to any value | ||
| disables permutation. | ||
| /* This needs to come after some library #include | ||
| to get __GNU_LIBRARY__ defined. */ | ||
| #ifdef __GNU_LIBRARY__ | ||
| /* Don't include stdlib.h for non-GNU C libraries because some of them | ||
| contain conflicting prototypes for getopt. */ | ||
| # include <stdlib.h> | ||
| # include <unistd.h> | ||
| #endif /* GNU C library. */ | ||
| If the first character of the OPTSTRING argument to 'getopt' or | ||
| 'getopt_long' is '+', both functions will stop at the first | ||
| non-option argument. If it is '-', both functions will report | ||
| non-option arguments as arguments to the option character '\x01'. */ | ||
| #ifdef VMS | ||
| # include <unixlib.h> | ||
| # if HAVE_STRING_H - 0 | ||
| # include <string.h> | ||
| # endif | ||
| #endif | ||
| #include "getopt_int.h" | ||
| #ifndef _ | ||
| /* This is for other GNU distributions with internationalized messages. */ | ||
| # if (HAVE_LIBINTL_H && ENABLE_NLS) || defined _LIBC | ||
| # include <libintl.h> | ||
| # ifndef _ | ||
| # define _(msgid) gettext (msgid) | ||
| # endif | ||
| # else | ||
| # define _(msgid) (msgid) | ||
| # endif | ||
| #endif | ||
| /* This version of `getopt' appears to the caller like standard Unix `getopt' | ||
| but it behaves differently for the user, since it allows the user | ||
| to intersperse the options with the other arguments. | ||
| As `getopt' works, it permutes the elements of ARGV so that, | ||
| when it is done, all the options precede everything else. Thus | ||
| all application programs are extended to handle flexible argument order. | ||
| Setting the environment variable POSIXLY_CORRECT disables permutation. | ||
| Then the behavior is completely standard. | ||
| GNU application programs can use a third alternative mode in which | ||
| they can distinguish the relative order of options and other arguments. */ | ||
| #include "getopt.h" | ||
| /* For communication from `getopt' to the caller. | ||
| When `getopt' finds an option that takes an argument, | ||
| /* For communication from 'getopt' to the caller. | ||
| When 'getopt' finds an option that takes an argument, | ||
| the argument value is returned here. | ||
| Also, when `ordering' is RETURN_IN_ORDER, | ||
| Also, when 'ordering' is RETURN_IN_ORDER, | ||
| each non-option ARGV-element is returned here. */ | ||
@@ -118,10 +94,10 @@ | ||
| This is used for communication to and from the caller | ||
| and for communication between successive calls to `getopt'. | ||
| and for communication between successive calls to 'getopt'. | ||
| On entry to `getopt', zero means this is the first call; initialize. | ||
| On entry to 'getopt', zero means this is the first call; initialize. | ||
| When `getopt' returns -1, this is the index of the first of the | ||
| When 'getopt' returns -1, this is the index of the first of the | ||
| non-option elements that the caller should itself scan. | ||
| Otherwise, `optind' communicates from one call to the next | ||
| Otherwise, 'optind' communicates from one call to the next | ||
| how much of ARGV has been scanned so far. */ | ||
@@ -132,17 +108,2 @@ | ||
| /* Formerly, initialization of getopt depended on optind==0, which | ||
| causes problems with re-calling getopt as programs generally don't | ||
| know that. */ | ||
| int __getopt_initialized; | ||
| /* The next char to be scanned in the option-element | ||
| in which the last option character we returned was found. | ||
| This allows us to pick up the scan where we left off. | ||
| If this is zero, or a null string, it means resume the scan | ||
| by advancing to the next ARGV-element. */ | ||
| static char *nextchar; | ||
| /* Callers store zero here to inhibit the error message | ||
@@ -159,136 +120,6 @@ for unrecognized options. */ | ||
| /* Describe how to deal with options that follow non-option ARGV-elements. | ||
| /* Keep a global copy of all internal members of getopt_data. */ | ||
| If the caller did not specify anything, | ||
| the default is REQUIRE_ORDER if the environment variable | ||
| POSIXLY_CORRECT is defined, PERMUTE otherwise. | ||
| static struct _getopt_data getopt_data; | ||
| REQUIRE_ORDER means don't recognize them as options; | ||
| stop option processing when the first non-option is seen. | ||
| This is what Unix does. | ||
| This mode of operation is selected by either setting the environment | ||
| variable POSIXLY_CORRECT, or using `+' as the first character | ||
| of the list of option characters. | ||
| PERMUTE is the default. We permute the contents of ARGV as we scan, | ||
| so that eventually all the non-options are at the end. This allows options | ||
| to be given in any order, even with programs that were not written to | ||
| expect this. | ||
| RETURN_IN_ORDER is an option available to programs that were written | ||
| to expect options and other ARGV-elements in any order and that care about | ||
| the ordering of the two. We describe each non-option ARGV-element | ||
| as if it were the argument of an option with character code 1. | ||
| Using `-' as the first character of the list of option characters | ||
| selects this mode of operation. | ||
| The special argument `--' forces an end of option-scanning regardless | ||
| of the value of `ordering'. In the case of RETURN_IN_ORDER, only | ||
| `--' can cause `getopt' to return -1 with `optind' != ARGC. */ | ||
| static enum | ||
| { | ||
| REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER | ||
| } ordering; | ||
| /* Value of POSIXLY_CORRECT environment variable. */ | ||
| static char *posixly_correct; | ||
| #ifdef __GNU_LIBRARY__ | ||
| /* We want to avoid inclusion of string.h with non-GNU libraries | ||
| because there are many ways it can cause trouble. | ||
| On some systems, it contains special magic macros that don't work | ||
| in GCC. */ | ||
| # include <string.h> | ||
| # define my_index strchr | ||
| #else | ||
| # if HAVE_STRING_H | ||
| # include <string.h> | ||
| # else | ||
| # include <strings.h> | ||
| # endif | ||
| /* Avoid depending on library functions or files | ||
| whose names are inconsistent. */ | ||
| #ifndef getenv | ||
| #ifdef _MSC_VER | ||
| // DDK will complain if you don't use the stdlib defined getenv | ||
| #include <stdlib.h> | ||
| #else | ||
| extern char *getenv (); | ||
| #endif | ||
| #endif | ||
| static char * | ||
| my_index (str, chr) | ||
| const char *str; | ||
| int chr; | ||
| { | ||
| while (*str) | ||
| { | ||
| if (*str == chr) | ||
| return (char *) str; | ||
| str++; | ||
| } | ||
| return 0; | ||
| } | ||
| /* If using GCC, we can safely declare strlen this way. | ||
| If not using GCC, it is ok not to declare it. */ | ||
| #ifdef __GNUC__ | ||
| /* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h. | ||
| That was relevant to code that was here before. */ | ||
| # if (!defined __STDC__ || !__STDC__) && !defined strlen | ||
| /* gcc with -traditional declares the built-in strlen to return int, | ||
| and has done so at least since version 2.4.5. -- rms. */ | ||
| extern int strlen (const char *); | ||
| # endif /* not __STDC__ */ | ||
| #endif /* __GNUC__ */ | ||
| #endif /* not __GNU_LIBRARY__ */ | ||
| /* Handle permutation of arguments. */ | ||
| /* Describe the part of ARGV that contains non-options that have | ||
| been skipped. `first_nonopt' is the index in ARGV of the first of them; | ||
| `last_nonopt' is the index after the last of them. */ | ||
| static int first_nonopt; | ||
| static int last_nonopt; | ||
| #ifdef _LIBC | ||
| /* Stored original parameters. | ||
| XXX This is no good solution. We should rather copy the args so | ||
| that we can compare them later. But we must not use malloc(3). */ | ||
| extern int __libc_argc; | ||
| extern char **__libc_argv; | ||
| /* Bash 2.0 gives us an environment variable containing flags | ||
| indicating ARGV elements that should not be considered arguments. */ | ||
| # ifdef USE_NONOPTION_FLAGS | ||
| /* Defined in getopt_init.c */ | ||
| extern char *__getopt_nonoption_flags; | ||
| static int nonoption_flags_max_len; | ||
| static int nonoption_flags_len; | ||
| # endif | ||
| # ifdef USE_NONOPTION_FLAGS | ||
| # define SWAP_FLAGS(ch1, ch2) \ | ||
| if (nonoption_flags_len > 0) \ | ||
| { \ | ||
| char __tmp = __getopt_nonoption_flags[ch1]; \ | ||
| __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \ | ||
| __getopt_nonoption_flags[ch2] = __tmp; \ | ||
| } | ||
| # else | ||
| # define SWAP_FLAGS(ch1, ch2) | ||
| # endif | ||
| #else /* !_LIBC */ | ||
| # define SWAP_FLAGS(ch1, ch2) | ||
| #endif /* _LIBC */ | ||
| /* Exchange two adjacent subsequences of ARGV. | ||
@@ -300,16 +131,11 @@ One subsequence is elements [first_nonopt,last_nonopt) | ||
| `first_nonopt' and `last_nonopt' are relocated so that they describe | ||
| 'first_nonopt' and 'last_nonopt' are relocated so that they describe | ||
| the new indices of the non-options in ARGV after they are moved. */ | ||
| #if defined __STDC__ && __STDC__ | ||
| static void exchange (char **); | ||
| #endif | ||
| static void | ||
| exchange (argv) | ||
| char **argv; | ||
| exchange (char **argv, struct _getopt_data *d) | ||
| { | ||
| int bottom = first_nonopt; | ||
| int middle = last_nonopt; | ||
| int top = optind; | ||
| int bottom = d->__first_nonopt; | ||
| int middle = d->__last_nonopt; | ||
| int top = d->optind; | ||
| char *tem; | ||
@@ -322,24 +148,2 @@ | ||
| #if defined _LIBC && defined USE_NONOPTION_FLAGS | ||
| /* First make sure the handling of the `__getopt_nonoption_flags' | ||
| string can work normally. Our top argument must be in the range | ||
| of the string. */ | ||
| if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len) | ||
| { | ||
| /* We must extend the array. The user plays games with us and | ||
| presents new arguments. */ | ||
| char *new_str = malloc (top + 1); | ||
| if (new_str == NULL) | ||
| nonoption_flags_len = nonoption_flags_max_len = 0; | ||
| else | ||
| { | ||
| memset (__mempcpy (new_str, __getopt_nonoption_flags, | ||
| nonoption_flags_max_len), | ||
| '\0', top + 1 - nonoption_flags_max_len); | ||
| nonoption_flags_max_len = top + 1; | ||
| __getopt_nonoption_flags = new_str; | ||
| } | ||
| } | ||
| #endif | ||
| while (top > middle && middle > bottom) | ||
@@ -351,3 +155,3 @@ { | ||
| int len = middle - bottom; | ||
| register int i; | ||
| int i; | ||
@@ -360,3 +164,2 @@ /* Swap it with the top part of the top segment. */ | ||
| argv[top - (middle - bottom) + i] = tem; | ||
| SWAP_FLAGS (bottom + i, top - (middle - bottom) + i); | ||
| } | ||
@@ -370,3 +173,3 @@ /* Exclude the moved bottom segment from further swapping. */ | ||
| int len = top - middle; | ||
| register int i; | ||
| int i; | ||
@@ -379,3 +182,2 @@ /* Swap it with the bottom part of the bottom segment. */ | ||
| argv[middle + i] = tem; | ||
| SWAP_FLAGS (bottom + i, middle + i); | ||
| } | ||
@@ -389,16 +191,205 @@ /* Exclude the moved top segment from further swapping. */ | ||
| first_nonopt += (optind - last_nonopt); | ||
| last_nonopt = optind; | ||
| d->__first_nonopt += (d->optind - d->__last_nonopt); | ||
| d->__last_nonopt = d->optind; | ||
| } | ||
| /* Initialize the internal data when the first call is made. */ | ||
| /* Process the argument starting with d->__nextchar as a long option. | ||
| d->optind should *not* have been advanced over this argument. | ||
| #if defined __STDC__ && __STDC__ | ||
| static const char *_getopt_initialize (int, char *const *, const char *); | ||
| #endif | ||
| If the value returned is -1, it was not actually a long option, the | ||
| state is unchanged, and the argument should be processed as a set | ||
| of short options (this can only happen when long_only is true). | ||
| Otherwise, the option (and its argument, if any) have been consumed | ||
| and the return value is the value to return from _getopt_internal_r. */ | ||
| static int | ||
| process_long_option (int argc, char **argv, const char *optstring, | ||
| const struct option *longopts, int *longind, | ||
| int long_only, struct _getopt_data *d, | ||
| int print_errors, const char *prefix) | ||
| { | ||
| char *nameend; | ||
| size_t namelen; | ||
| const struct option *p; | ||
| const struct option *pfound = NULL; | ||
| int n_options; | ||
| int option_index = 0; | ||
| for (nameend = d->__nextchar; *nameend && *nameend != '='; nameend++) | ||
| /* Do nothing. */ ; | ||
| namelen = nameend - d->__nextchar; | ||
| /* First look for an exact match, counting the options as a side | ||
| effect. */ | ||
| for (p = longopts, n_options = 0; p->name; p++, n_options++) | ||
| if (!strncmp (p->name, d->__nextchar, namelen) | ||
| && namelen == strlen (p->name)) | ||
| { | ||
| /* Exact match found. */ | ||
| pfound = p; | ||
| option_index = n_options; | ||
| break; | ||
| } | ||
| if (pfound == NULL) | ||
| { | ||
| /* Didn't find an exact match, so look for abbreviations. */ | ||
| unsigned char *ambig_set = NULL; | ||
| int ambig_malloced = 0; | ||
| int ambig_fallback = 0; | ||
| int indfound = -1; | ||
| for (p = longopts, option_index = 0; p->name; p++, option_index++) | ||
| if (!strncmp (p->name, d->__nextchar, namelen)) | ||
| { | ||
| if (pfound == NULL) | ||
| { | ||
| /* First nonexact match found. */ | ||
| pfound = p; | ||
| indfound = option_index; | ||
| } | ||
| else if (long_only | ||
| || pfound->has_arg != p->has_arg | ||
| || pfound->flag != p->flag | ||
| || pfound->val != p->val) | ||
| { | ||
| /* Second or later nonexact match found. */ | ||
| if (!ambig_fallback) | ||
| { | ||
| if (!print_errors) | ||
| /* Don't waste effort tracking the ambig set if | ||
| we're not going to print it anyway. */ | ||
| ambig_fallback = 1; | ||
| else if (!ambig_set) | ||
| { | ||
| if (__libc_use_alloca (n_options)) | ||
| ambig_set = alloca (n_options); | ||
| else if ((ambig_set = malloc (n_options)) == NULL) | ||
| /* Fall back to simpler error message. */ | ||
| ambig_fallback = 1; | ||
| else | ||
| ambig_malloced = 1; | ||
| if (ambig_set) | ||
| { | ||
| memset (ambig_set, 0, n_options); | ||
| ambig_set[indfound] = 1; | ||
| } | ||
| } | ||
| if (ambig_set) | ||
| ambig_set[option_index] = 1; | ||
| } | ||
| } | ||
| } | ||
| if (ambig_set || ambig_fallback) | ||
| { | ||
| if (print_errors) | ||
| { | ||
| if (ambig_fallback) | ||
| fprintf (stderr, _("%s: option '%s%s' is ambiguous\n"), | ||
| argv[0], prefix, d->__nextchar); | ||
| else | ||
| { | ||
| flockfile (stderr); | ||
| fprintf (stderr, | ||
| _("%s: option '%s%s' is ambiguous; possibilities:"), | ||
| argv[0], prefix, d->__nextchar); | ||
| for (option_index = 0; option_index < n_options; option_index++) | ||
| if (ambig_set[option_index]) | ||
| fprintf (stderr, " '%s%s'", | ||
| prefix, longopts[option_index].name); | ||
| /* This must use 'fprintf' even though it's only | ||
| printing a single character, so that it goes through | ||
| __fxprintf_nocancel when compiled as part of glibc. */ | ||
| fprintf (stderr, "\n"); | ||
| funlockfile (stderr); | ||
| } | ||
| } | ||
| if (ambig_malloced) | ||
| free (ambig_set); | ||
| d->__nextchar += strlen (d->__nextchar); | ||
| d->optind++; | ||
| d->optopt = 0; | ||
| return '?'; | ||
| } | ||
| option_index = indfound; | ||
| } | ||
| if (pfound == NULL) | ||
| { | ||
| /* Can't find it as a long option. If this is not getopt_long_only, | ||
| or the option starts with '--' or is not a valid short option, | ||
| then it's an error. */ | ||
| if (!long_only || argv[d->optind][1] == '-' | ||
| || strchr (optstring, *d->__nextchar) == NULL) | ||
| { | ||
| if (print_errors) | ||
| fprintf (stderr, _("%s: unrecognized option '%s%s'\n"), | ||
| argv[0], prefix, d->__nextchar); | ||
| d->__nextchar = NULL; | ||
| d->optind++; | ||
| d->optopt = 0; | ||
| return '?'; | ||
| } | ||
| /* Otherwise interpret it as a short option. */ | ||
| return -1; | ||
| } | ||
| /* We have found a matching long option. Consume it. */ | ||
| d->optind++; | ||
| d->__nextchar = NULL; | ||
| if (*nameend) | ||
| { | ||
| /* Don't test has_arg with >, because some C compilers don't | ||
| allow it to be used on enums. */ | ||
| if (pfound->has_arg) | ||
| d->optarg = nameend + 1; | ||
| else | ||
| { | ||
| if (print_errors) | ||
| fprintf (stderr, | ||
| _("%s: option '%s%s' doesn't allow an argument\n"), | ||
| argv[0], prefix, pfound->name); | ||
| d->optopt = pfound->val; | ||
| return '?'; | ||
| } | ||
| } | ||
| else if (pfound->has_arg == 1) | ||
| { | ||
| if (d->optind < argc) | ||
| d->optarg = argv[d->optind++]; | ||
| else | ||
| { | ||
| if (print_errors) | ||
| fprintf (stderr, | ||
| _("%s: option '%s%s' requires an argument\n"), | ||
| argv[0], prefix, pfound->name); | ||
| d->optopt = pfound->val; | ||
| return optstring[0] == ':' ? ':' : '?'; | ||
| } | ||
| } | ||
| if (longind != NULL) | ||
| *longind = option_index; | ||
| if (pfound->flag) | ||
| { | ||
| *(pfound->flag) = pfound->val; | ||
| return 0; | ||
| } | ||
| return pfound->val; | ||
| } | ||
| /* Initialize internal data upon the first call to getopt. */ | ||
| static const char * | ||
| _getopt_initialize (argc, argv, optstring) | ||
| int argc; | ||
| char *const *argv; | ||
| const char *optstring; | ||
| _getopt_initialize (_GL_UNUSED int argc, | ||
| _GL_UNUSED char **argv, const char *optstring, | ||
| struct _getopt_data *d, int posixly_correct) | ||
| { | ||
@@ -408,14 +399,12 @@ /* Start processing options with ARGV-element 1 (since ARGV-element 0 | ||
| non-option ARGV-elements is empty. */ | ||
| if (d->optind == 0) | ||
| d->optind = 1; | ||
| first_nonopt = last_nonopt = optind; | ||
| d->__first_nonopt = d->__last_nonopt = d->optind; | ||
| d->__nextchar = NULL; | ||
| nextchar = NULL; | ||
| posixly_correct = getenv ("POSIXLY_CORRECT"); | ||
| /* Determine how to handle the ordering of options and nonoptions. */ | ||
| if (optstring[0] == '-') | ||
| { | ||
| ordering = RETURN_IN_ORDER; | ||
| d->__ordering = RETURN_IN_ORDER; | ||
| ++optstring; | ||
@@ -425,43 +414,14 @@ } | ||
| { | ||
| ordering = REQUIRE_ORDER; | ||
| d->__ordering = REQUIRE_ORDER; | ||
| ++optstring; | ||
| } | ||
| else if (posixly_correct != NULL) | ||
| ordering = REQUIRE_ORDER; | ||
| else if (posixly_correct || !!getenv ("POSIXLY_CORRECT")) | ||
| d->__ordering = REQUIRE_ORDER; | ||
| else | ||
| ordering = PERMUTE; | ||
| d->__ordering = PERMUTE; | ||
| #if defined _LIBC && defined USE_NONOPTION_FLAGS | ||
| if (posixly_correct == NULL | ||
| && argc == __libc_argc && argv == __libc_argv) | ||
| { | ||
| if (nonoption_flags_max_len == 0) | ||
| { | ||
| if (__getopt_nonoption_flags == NULL | ||
| || __getopt_nonoption_flags[0] == '\0') | ||
| nonoption_flags_max_len = -1; | ||
| else | ||
| { | ||
| const char *orig_str = __getopt_nonoption_flags; | ||
| int len = nonoption_flags_max_len = strlen (orig_str); | ||
| if (nonoption_flags_max_len < argc) | ||
| nonoption_flags_max_len = argc; | ||
| __getopt_nonoption_flags = | ||
| (char *) malloc (nonoption_flags_max_len); | ||
| if (__getopt_nonoption_flags == NULL) | ||
| nonoption_flags_max_len = -1; | ||
| else | ||
| memset (__mempcpy (__getopt_nonoption_flags, orig_str, len), | ||
| '\0', nonoption_flags_max_len - len); | ||
| } | ||
| } | ||
| nonoption_flags_len = nonoption_flags_max_len; | ||
| } | ||
| else | ||
| nonoption_flags_len = 0; | ||
| #endif | ||
| d->__initialized = 1; | ||
| return optstring; | ||
| } | ||
| /* Scan elements of ARGV (whose length is ARGC) for option characters | ||
@@ -472,12 +432,12 @@ given in OPTSTRING. | ||
| then it is an option element. The characters of this element | ||
| (aside from the initial '-') are option characters. If `getopt' | ||
| (aside from the initial '-') are option characters. If 'getopt' | ||
| is called repeatedly, it returns successively each of the option characters | ||
| from each of the option elements. | ||
| If `getopt' finds another option character, it returns that character, | ||
| updating `optind' and `nextchar' so that the next call to `getopt' can | ||
| If 'getopt' finds another option character, it returns that character, | ||
| updating 'optind' and 'nextchar' so that the next call to 'getopt' can | ||
| resume the scan with the following option character or ARGV-element. | ||
| If there are no more option characters, `getopt' returns -1. | ||
| Then `optind' is the index in ARGV of the first ARGV-element | ||
| If there are no more option characters, 'getopt' returns -1. | ||
| Then 'optind' is the index in ARGV of the first ARGV-element | ||
| that is not an option. (The ARGV-elements have been permuted | ||
@@ -488,3 +448,3 @@ so that those that are not options now come last.) | ||
| If an option character is seen that is not listed in OPTSTRING, | ||
| return '?' after printing an error message. If you set `opterr' to | ||
| return '?' after printing an error message. If you set 'opterr' to | ||
| zero, the error message is suppressed but we still return '?'. | ||
@@ -494,18 +454,18 @@ | ||
| so the following text in the same ARGV-element, or the text of the following | ||
| ARGV-element, is returned in `optarg'. Two colons mean an option that | ||
| ARGV-element, is returned in 'optarg'. Two colons mean an option that | ||
| wants an optional arg; if there is text in the current ARGV-element, | ||
| it is returned in `optarg', otherwise `optarg' is set to zero. | ||
| it is returned in 'optarg', otherwise 'optarg' is set to zero. | ||
| If OPTSTRING starts with `-' or `+', it requests different methods of | ||
| If OPTSTRING starts with '-' or '+', it requests different methods of | ||
| handling the non-option ARGV-elements. | ||
| See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. | ||
| Long-named options begin with `--' instead of `-'. | ||
| Long-named options begin with '--' instead of '-'. | ||
| Their names may be abbreviated as long as the abbreviation is unique | ||
| or is an exact match for some defined option. If they have an | ||
| argument, it follows the option name in the same ARGV-element, separated | ||
| from the option name by a `=', or else the in next ARGV-element. | ||
| When `getopt' finds a long-named option, it returns 0 if that option's | ||
| `flag' field is nonzero, the value of the option's `val' field | ||
| if the `flag' field is zero. | ||
| from the option name by a '=', or else the in next ARGV-element. | ||
| When 'getopt' finds a long-named option, it returns 0 if that option's | ||
| 'flag' field is nonzero, the value of the option's 'val' field | ||
| if the 'flag' field is zero. | ||
@@ -516,3 +476,3 @@ The elements of ARGV aren't really const, because we permute them. | ||
| LONGOPTS is a vector of `struct option' terminated by an | ||
| LONGOPTS is a vector of 'struct option' terminated by an | ||
| element containing a name which is zero. | ||
@@ -528,13 +488,7 @@ | ||
| int | ||
| _getopt_internal (argc, argv, optstring, longopts, longind, long_only) | ||
| int argc; | ||
| char *const *argv; | ||
| const char *optstring; | ||
| const struct option *longopts; | ||
| int *longind; | ||
| int long_only; | ||
| _getopt_internal_r (int argc, char **argv, const char *optstring, | ||
| const struct option *longopts, int *longind, | ||
| int long_only, struct _getopt_data *d, int posixly_correct) | ||
| { | ||
| int print_errors = opterr; | ||
| if (optstring[0] == ':') | ||
| print_errors = 0; | ||
| int print_errors = d->opterr; | ||
@@ -544,25 +498,16 @@ if (argc < 1) | ||
| optarg = NULL; | ||
| d->optarg = NULL; | ||
| if (optind == 0 || !__getopt_initialized) | ||
| { | ||
| if (optind == 0) | ||
| optind = 1; /* Don't scan ARGV[0], the program name. */ | ||
| optstring = _getopt_initialize (argc, argv, optstring); | ||
| __getopt_initialized = 1; | ||
| } | ||
| if (d->optind == 0 || !d->__initialized) | ||
| optstring = _getopt_initialize (argc, argv, optstring, d, posixly_correct); | ||
| else if (optstring[0] == '-' || optstring[0] == '+') | ||
| optstring++; | ||
| /* Test whether ARGV[optind] points to a non-option argument. | ||
| Either it does not have option syntax, or there is an environment flag | ||
| from the shell indicating it is not an option. The later information | ||
| is only used when the used in the GNU libc. */ | ||
| #if defined _LIBC && defined USE_NONOPTION_FLAGS | ||
| # define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0' \ | ||
| || (optind < nonoption_flags_len \ | ||
| && __getopt_nonoption_flags[optind] == '1')) | ||
| #else | ||
| # define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0') | ||
| #endif | ||
| if (optstring[0] == ':') | ||
| print_errors = 0; | ||
| if (nextchar == NULL || *nextchar == '\0') | ||
| /* Test whether ARGV[optind] points to a non-option argument. */ | ||
| #define NONOPTION_P (argv[d->optind][0] != '-' || argv[d->optind][1] == '\0') | ||
| if (d->__nextchar == NULL || *d->__nextchar == '\0') | ||
| { | ||
@@ -573,8 +518,8 @@ /* Advance to the next ARGV-element. */ | ||
| moved back by the user (who may also have changed the arguments). */ | ||
| if (last_nonopt > optind) | ||
| last_nonopt = optind; | ||
| if (first_nonopt > optind) | ||
| first_nonopt = optind; | ||
| if (d->__last_nonopt > d->optind) | ||
| d->__last_nonopt = d->optind; | ||
| if (d->__first_nonopt > d->optind) | ||
| d->__first_nonopt = d->optind; | ||
| if (ordering == PERMUTE) | ||
| if (d->__ordering == PERMUTE) | ||
| { | ||
@@ -584,6 +529,7 @@ /* If we have just processed some options following some non-options, | ||
| if (first_nonopt != last_nonopt && last_nonopt != optind) | ||
| exchange ((char **) argv); | ||
| else if (last_nonopt != optind) | ||
| first_nonopt = optind; | ||
| if (d->__first_nonopt != d->__last_nonopt | ||
| && d->__last_nonopt != d->optind) | ||
| exchange (argv, d); | ||
| else if (d->__last_nonopt != d->optind) | ||
| d->__first_nonopt = d->optind; | ||
@@ -593,8 +539,8 @@ /* Skip any additional non-options | ||
| while (optind < argc && NONOPTION_P) | ||
| optind++; | ||
| last_nonopt = optind; | ||
| while (d->optind < argc && NONOPTION_P) | ||
| d->optind++; | ||
| d->__last_nonopt = d->optind; | ||
| } | ||
| /* The special ARGV-element `--' means premature end of options. | ||
| /* The special ARGV-element '--' means premature end of options. | ||
| Skip it like a null option, | ||
@@ -604,13 +550,14 @@ then exchange with previous non-options as if it were an option, | ||
| if (optind != argc && !strcmp (argv[optind], "--")) | ||
| if (d->optind != argc && !strcmp (argv[d->optind], "--")) | ||
| { | ||
| optind++; | ||
| d->optind++; | ||
| if (first_nonopt != last_nonopt && last_nonopt != optind) | ||
| exchange ((char **) argv); | ||
| else if (first_nonopt == last_nonopt) | ||
| first_nonopt = optind; | ||
| last_nonopt = argc; | ||
| if (d->__first_nonopt != d->__last_nonopt | ||
| && d->__last_nonopt != d->optind) | ||
| exchange (argv, d); | ||
| else if (d->__first_nonopt == d->__last_nonopt) | ||
| d->__first_nonopt = d->optind; | ||
| d->__last_nonopt = argc; | ||
| optind = argc; | ||
| d->optind = argc; | ||
| } | ||
@@ -621,8 +568,8 @@ | ||
| if (optind == argc) | ||
| if (d->optind == argc) | ||
| { | ||
| /* Set the next-arg-index to point at the non-options | ||
| that we previously skipped, so the caller will digest them. */ | ||
| if (first_nonopt != last_nonopt) | ||
| optind = first_nonopt; | ||
| if (d->__first_nonopt != d->__last_nonopt) | ||
| d->optind = d->__first_nonopt; | ||
| return -1; | ||
@@ -636,5 +583,5 @@ } | ||
| { | ||
| if (ordering == REQUIRE_ORDER) | ||
| if (d->__ordering == REQUIRE_ORDER) | ||
| return -1; | ||
| optarg = argv[optind++]; | ||
| d->optarg = argv[d->optind++]; | ||
| return 1; | ||
@@ -644,158 +591,42 @@ } | ||
| /* We have found another option-ARGV-element. | ||
| Skip the initial punctuation. */ | ||
| nextchar = (argv[optind] + 1 | ||
| + (longopts != NULL && argv[optind][1] == '-')); | ||
| } | ||
| /* Decode the current option-ARGV-element. */ | ||
| /* Check whether the ARGV-element is a long option. | ||
| If long_only and the ARGV-element has the form "-f", where f is | ||
| a valid short option, don't consider it an abbreviated form of | ||
| a long option that starts with f. Otherwise there would be no | ||
| way to give the -f short option. | ||
| On the other hand, if there's a long option "fubar" and | ||
| the ARGV-element is "-fu", do consider that an abbreviation of | ||
| the long option, just like "--fu", and not "-f" with arg "u". | ||
| This distinction seems to be the most useful approach. */ | ||
| if (longopts != NULL | ||
| && (argv[optind][1] == '-' | ||
| || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1]))))) | ||
| { | ||
| char *nameend; | ||
| const struct option *p; | ||
| const struct option *pfound = NULL; | ||
| int exact = 0; | ||
| int ambig = 0; | ||
| int indfound = -1; | ||
| int option_index; | ||
| for (nameend = nextchar; *nameend && *nameend != '='; nameend++) | ||
| /* Do nothing. */ ; | ||
| /* Test all long options for either exact match | ||
| or abbreviated matches. */ | ||
| for (p = longopts, option_index = 0; p->name; p++, option_index++) | ||
| if (!strncmp (p->name, nextchar, nameend - nextchar)) | ||
| { | ||
| if ((unsigned int) (nameend - nextchar) | ||
| == (unsigned int) strlen (p->name)) | ||
| { | ||
| /* Exact match found. */ | ||
| pfound = p; | ||
| indfound = option_index; | ||
| exact = 1; | ||
| break; | ||
| } | ||
| else if (pfound == NULL) | ||
| { | ||
| /* First nonexact match found. */ | ||
| pfound = p; | ||
| indfound = option_index; | ||
| } | ||
| else if (long_only | ||
| || pfound->has_arg != p->has_arg | ||
| || pfound->flag != p->flag | ||
| || pfound->val != p->val) | ||
| /* Second or later nonexact match found. */ | ||
| ambig = 1; | ||
| } | ||
| if (ambig && !exact) | ||
| Check whether it might be a long option. */ | ||
| if (longopts) | ||
| { | ||
| if (print_errors) | ||
| fprintf (stderr, _("%s: option `%s' is ambiguous\n"), | ||
| argv[0], argv[optind]); | ||
| nextchar += strlen (nextchar); | ||
| optind++; | ||
| optopt = 0; | ||
| return '?'; | ||
| } | ||
| if (pfound != NULL) | ||
| { | ||
| option_index = indfound; | ||
| optind++; | ||
| if (*nameend) | ||
| if (argv[d->optind][1] == '-') | ||
| { | ||
| /* Don't test has_arg with >, because some C compilers don't | ||
| allow it to be used on enums. */ | ||
| if (pfound->has_arg) | ||
| optarg = nameend + 1; | ||
| else | ||
| { | ||
| if (print_errors) | ||
| { | ||
| if (argv[optind - 1][1] == '-') | ||
| /* --option */ | ||
| fprintf (stderr, | ||
| _("%s: option `--%s' doesn't allow an argument\n"), | ||
| argv[0], pfound->name); | ||
| else | ||
| /* +option or -option */ | ||
| fprintf (stderr, | ||
| _("%s: option `%c%s' doesn't allow an argument\n"), | ||
| argv[0], argv[optind - 1][0], pfound->name); | ||
| } | ||
| /* "--foo" is always a long option. The special option | ||
| "--" was handled above. */ | ||
| d->__nextchar = argv[d->optind] + 2; | ||
| return process_long_option (argc, argv, optstring, longopts, | ||
| longind, long_only, d, | ||
| print_errors, "--"); | ||
| } | ||
| nextchar += strlen (nextchar); | ||
| /* If long_only and the ARGV-element has the form "-f", | ||
| where f is a valid short option, don't consider it an | ||
| abbreviated form of a long option that starts with f. | ||
| Otherwise there would be no way to give the -f short | ||
| option. | ||
| optopt = pfound->val; | ||
| return '?'; | ||
| } | ||
| } | ||
| else if (pfound->has_arg == 1) | ||
| On the other hand, if there's a long option "fubar" and | ||
| the ARGV-element is "-fu", do consider that an | ||
| abbreviation of the long option, just like "--fu", and | ||
| not "-f" with arg "u". | ||
| This distinction seems to be the most useful approach. */ | ||
| if (long_only && (argv[d->optind][2] | ||
| || !strchr (optstring, argv[d->optind][1]))) | ||
| { | ||
| if (optind < argc) | ||
| optarg = argv[optind++]; | ||
| else | ||
| { | ||
| if (print_errors) | ||
| fprintf (stderr, | ||
| _("%s: option `%s' requires an argument\n"), | ||
| argv[0], argv[optind - 1]); | ||
| nextchar += strlen (nextchar); | ||
| optopt = pfound->val; | ||
| return optstring[0] == ':' ? ':' : '?'; | ||
| } | ||
| int code; | ||
| d->__nextchar = argv[d->optind] + 1; | ||
| code = process_long_option (argc, argv, optstring, longopts, | ||
| longind, long_only, d, | ||
| print_errors, "-"); | ||
| if (code != -1) | ||
| return code; | ||
| } | ||
| nextchar += strlen (nextchar); | ||
| if (longind != NULL) | ||
| *longind = option_index; | ||
| if (pfound->flag) | ||
| { | ||
| *(pfound->flag) = pfound->val; | ||
| return 0; | ||
| } | ||
| return pfound->val; | ||
| } | ||
| /* Can't find it as a long option. If this is not getopt_long_only, | ||
| or the option starts with '--' or is not a valid short | ||
| option, then it's an error. | ||
| Otherwise interpret it as a short option. */ | ||
| if (!long_only || argv[optind][1] == '-' | ||
| || my_index (optstring, *nextchar) == NULL) | ||
| { | ||
| if (print_errors) | ||
| { | ||
| if (argv[optind][1] == '-') | ||
| /* --option */ | ||
| fprintf (stderr, _("%s: unrecognized option `--%s'\n"), | ||
| argv[0], nextchar); | ||
| else | ||
| /* +option or -option */ | ||
| fprintf (stderr, _("%s: unrecognized option `%c%s'\n"), | ||
| argv[0], argv[optind][0], nextchar); | ||
| } | ||
| nextchar = (char *) ""; | ||
| optind++; | ||
| optopt = 0; | ||
| return '?'; | ||
| } | ||
| /* It is not a long option. Skip the initial punctuation. */ | ||
| d->__nextchar = argv[d->optind] + 1; | ||
| } | ||
@@ -806,52 +637,31 @@ | ||
| { | ||
| char c = *nextchar++; | ||
| char *temp = my_index (optstring, c); | ||
| char c = *d->__nextchar++; | ||
| const char *temp = strchr (optstring, c); | ||
| /* Increment `optind' when we start to process its last character. */ | ||
| if (*nextchar == '\0') | ||
| ++optind; | ||
| /* Increment 'optind' when we start to process its last character. */ | ||
| if (*d->__nextchar == '\0') | ||
| ++d->optind; | ||
| if (temp == NULL || c == ':') | ||
| if (temp == NULL || c == ':' || c == ';') | ||
| { | ||
| if (print_errors) | ||
| { | ||
| if (posixly_correct) | ||
| /* 1003.2 specifies the format of this message. */ | ||
| fprintf (stderr, _("%s: illegal option -- %c\n"), | ||
| argv[0], c); | ||
| else | ||
| fprintf (stderr, _("%s: invalid option -- %c\n"), | ||
| argv[0], c); | ||
| } | ||
| optopt = c; | ||
| fprintf (stderr, _("%s: invalid option -- '%c'\n"), argv[0], c); | ||
| d->optopt = c; | ||
| return '?'; | ||
| } | ||
| /* Convenience. Treat POSIX -W foo same as long option --foo */ | ||
| if (temp[0] == 'W' && temp[1] == ';') | ||
| if (temp[0] == 'W' && temp[1] == ';' && longopts != NULL) | ||
| { | ||
| char *nameend; | ||
| const struct option *p; | ||
| const struct option *pfound = NULL; | ||
| int exact = 0; | ||
| int ambig = 0; | ||
| int indfound = 0; | ||
| int option_index; | ||
| /* This is an option that requires an argument. */ | ||
| if (*nextchar != '\0') | ||
| if (*d->__nextchar != '\0') | ||
| d->optarg = d->__nextchar; | ||
| else if (d->optind == argc) | ||
| { | ||
| optarg = nextchar; | ||
| /* If we end this ARGV-element by taking the rest as an arg, | ||
| we must advance to the next element now. */ | ||
| optind++; | ||
| } | ||
| else if (optind == argc) | ||
| { | ||
| if (print_errors) | ||
| { | ||
| /* 1003.2 specifies the format of this message. */ | ||
| fprintf (stderr, _("%s: option requires an argument -- %c\n"), | ||
| argv[0], c); | ||
| } | ||
| optopt = c; | ||
| fprintf (stderr, | ||
| _("%s: option requires an argument -- '%c'\n"), | ||
| argv[0], c); | ||
| d->optopt = c; | ||
| if (optstring[0] == ':') | ||
@@ -864,90 +674,8 @@ c = ':'; | ||
| else | ||
| /* We already incremented `optind' once; | ||
| increment it again when taking next ARGV-elt as argument. */ | ||
| optarg = argv[optind++]; | ||
| d->optarg = argv[d->optind]; | ||
| /* optarg is now the argument, see if it's in the | ||
| table of longopts. */ | ||
| for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++) | ||
| /* Do nothing. */ ; | ||
| /* Test all long options for either exact match | ||
| or abbreviated matches. */ | ||
| for (p = longopts, option_index = 0; p != NULL && p->name; p++, option_index++) | ||
| if (!strncmp (p->name, nextchar, nameend - nextchar)) | ||
| { | ||
| if ((unsigned int) (nameend - nextchar) == strlen (p->name)) | ||
| { | ||
| /* Exact match found. */ | ||
| pfound = p; | ||
| indfound = option_index; | ||
| exact = 1; | ||
| break; | ||
| } | ||
| else if (pfound == NULL) | ||
| { | ||
| /* First nonexact match found. */ | ||
| pfound = p; | ||
| indfound = option_index; | ||
| } | ||
| else | ||
| /* Second or later nonexact match found. */ | ||
| ambig = 1; | ||
| } | ||
| if (ambig && !exact) | ||
| { | ||
| if (print_errors) | ||
| fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"), | ||
| argv[0], argv[optind]); | ||
| nextchar += strlen (nextchar); | ||
| optind++; | ||
| return '?'; | ||
| } | ||
| if (pfound != NULL) | ||
| { | ||
| option_index = indfound; | ||
| if (*nameend) | ||
| { | ||
| /* Don't test has_arg with >, because some C compilers don't | ||
| allow it to be used on enums. */ | ||
| if (pfound->has_arg) | ||
| optarg = nameend + 1; | ||
| else | ||
| { | ||
| if (print_errors) | ||
| fprintf (stderr, _("\ | ||
| %s: option `-W %s' doesn't allow an argument\n"), | ||
| argv[0], pfound->name); | ||
| nextchar += strlen (nextchar); | ||
| return '?'; | ||
| } | ||
| } | ||
| else if (pfound->has_arg == 1) | ||
| { | ||
| if (optind < argc) | ||
| optarg = argv[optind++]; | ||
| else | ||
| { | ||
| if (print_errors) | ||
| fprintf (stderr, | ||
| _("%s: option `%s' requires an argument\n"), | ||
| argv[0], argv[optind - 1]); | ||
| nextchar += strlen (nextchar); | ||
| return optstring[0] == ':' ? ':' : '?'; | ||
| } | ||
| } | ||
| nextchar += strlen (nextchar); | ||
| if (longind != NULL) | ||
| *longind = option_index; | ||
| if (pfound->flag) | ||
| { | ||
| *(pfound->flag) = pfound->val; | ||
| return 0; | ||
| } | ||
| return pfound->val; | ||
| } | ||
| nextchar = NULL; | ||
| return 'W'; /* Let the application handle it. */ | ||
| d->__nextchar = d->optarg; | ||
| d->optarg = NULL; | ||
| return process_long_option (argc, argv, optstring, longopts, longind, | ||
| 0 /* long_only */, d, print_errors, "-W "); | ||
| } | ||
@@ -959,10 +687,10 @@ if (temp[1] == ':') | ||
| /* This is an option that accepts an argument optionally. */ | ||
| if (*nextchar != '\0') | ||
| if (*d->__nextchar != '\0') | ||
| { | ||
| optarg = nextchar; | ||
| optind++; | ||
| d->optarg = d->__nextchar; | ||
| d->optind++; | ||
| } | ||
| else | ||
| optarg = NULL; | ||
| nextchar = NULL; | ||
| d->optarg = NULL; | ||
| d->__nextchar = NULL; | ||
| } | ||
@@ -972,19 +700,17 @@ else | ||
| /* This is an option that requires an argument. */ | ||
| if (*nextchar != '\0') | ||
| if (*d->__nextchar != '\0') | ||
| { | ||
| optarg = nextchar; | ||
| d->optarg = d->__nextchar; | ||
| /* If we end this ARGV-element by taking the rest as an arg, | ||
| we must advance to the next element now. */ | ||
| optind++; | ||
| d->optind++; | ||
| } | ||
| else if (optind == argc) | ||
| else if (d->optind == argc) | ||
| { | ||
| if (print_errors) | ||
| { | ||
| /* 1003.2 specifies the format of this message. */ | ||
| fprintf (stderr, | ||
| _("%s: option requires an argument -- %c\n"), | ||
| argv[0], c); | ||
| } | ||
| optopt = c; | ||
| fprintf (stderr, | ||
| _("%s: option requires an argument -- '%c'\n"), | ||
| argv[0], c); | ||
| d->optopt = c; | ||
| if (optstring[0] == ':') | ||
@@ -996,6 +722,6 @@ c = ':'; | ||
| else | ||
| /* We already incremented `optind' once; | ||
| /* We already incremented 'optind' once; | ||
| increment it again when taking next ARGV-elt as argument. */ | ||
| optarg = argv[optind++]; | ||
| nextchar = NULL; | ||
| d->optarg = argv[d->optind++]; | ||
| d->__nextchar = NULL; | ||
| } | ||
@@ -1008,24 +734,49 @@ } | ||
| int | ||
| getopt (argc, argv, optstring) | ||
| int argc; | ||
| char *const *argv; | ||
| const char *optstring; | ||
| _getopt_internal (int argc, char **argv, const char *optstring, | ||
| const struct option *longopts, int *longind, int long_only, | ||
| int posixly_correct) | ||
| { | ||
| return _getopt_internal (argc, argv, optstring, | ||
| (const struct option *) 0, | ||
| (int *) 0, | ||
| 0); | ||
| int result; | ||
| getopt_data.optind = optind; | ||
| getopt_data.opterr = opterr; | ||
| result = _getopt_internal_r (argc, argv, optstring, longopts, | ||
| longind, long_only, &getopt_data, | ||
| posixly_correct); | ||
| optind = getopt_data.optind; | ||
| optarg = getopt_data.optarg; | ||
| optopt = getopt_data.optopt; | ||
| return result; | ||
| } | ||
| #endif /* Not ELIDE_CODE. */ | ||
| /* glibc gets a LSB-compliant getopt and a POSIX-complaint __posix_getopt. | ||
| Standalone applications just get a POSIX-compliant getopt. | ||
| POSIX and LSB both require these functions to take 'char *const *argv' | ||
| even though this is incorrect (because of the permutation). */ | ||
| #define GETOPT_ENTRY(NAME, POSIXLY_CORRECT) \ | ||
| int \ | ||
| NAME (int argc, char *const *argv, const char *optstring) \ | ||
| { \ | ||
| return _getopt_internal (argc, (char **)argv, optstring, \ | ||
| NULL, NULL, 0, POSIXLY_CORRECT); \ | ||
| } | ||
| #ifdef _LIBC | ||
| GETOPT_ENTRY(getopt, 0) | ||
| GETOPT_ENTRY(__posix_getopt, 1) | ||
| #else | ||
| GETOPT_ENTRY(getopt, 1) | ||
| #endif | ||
| #ifdef TEST | ||
| /* Compile with -DTEST to make an executable for use in testing | ||
| the above definition of `getopt'. */ | ||
| the above definition of 'getopt'. */ | ||
| int | ||
| main (argc, argv) | ||
| int argc; | ||
| char **argv; | ||
| main (int argc, char **argv) | ||
| { | ||
@@ -1070,3 +821,3 @@ int c; | ||
| case 'c': | ||
| printf ("option c with value `%s'\n", optarg); | ||
| printf ("option c with value '%s'\n", optarg); | ||
| break; | ||
@@ -1073,0 +824,0 @@ |
+16
-158
| /* Declarations for getopt. | ||
| Copyright (C) 1989-1994, 1996-1999, 2001 Free Software Foundation, Inc. | ||
| Copyright (C) 1989-2026 Free Software Foundation, Inc. | ||
| This file is part of the GNU C Library. | ||
| Unlike the bulk of the getopt implementation, this file is NOT part | ||
| of gnulib; gnulib also has a getopt.h but it is different. | ||
@@ -16,166 +18,22 @@ The GNU C Library is free software; you can redistribute it and/or | ||
| You should have received a copy of the GNU Lesser General Public | ||
| License along with the GNU C Library; if not, write to the Free | ||
| Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA | ||
| 02111-1307 USA. */ | ||
| License along with the GNU C Library; if not, see | ||
| <https://www.gnu.org/licenses/>. */ | ||
| #ifndef _GETOPT_H | ||
| #define _GETOPT_H 1 | ||
| #ifndef __need_getopt | ||
| # define _GETOPT_H 1 | ||
| #endif | ||
| #include <features.h> | ||
| /* If __GNU_LIBRARY__ is not already defined, either we are being used | ||
| standalone, or this is the first header included in the source file. | ||
| If we are being used with glibc, we need to include <features.h>, but | ||
| that does not exist if we are standalone. So: if __GNU_LIBRARY__ is | ||
| not defined, include <ctype.h>, which will pull in <features.h> for us | ||
| if it's from glibc. (Why ctype.h? It's guaranteed to exist and it | ||
| doesn't flood the namespace with stuff the way some other headers do.) */ | ||
| #if !defined __GNU_LIBRARY__ | ||
| # include <ctype.h> | ||
| /* The type of the 'argv' argument to getopt_long and getopt_long_only | ||
| is properly 'char **', since both functions may write to the array | ||
| (in order to move all the options to the beginning). However, for | ||
| compatibility with old versions of LSB, glibc has to use 'char *const *' | ||
| instead. */ | ||
| #ifndef __getopt_argv_const | ||
| # define __getopt_argv_const const | ||
| #endif | ||
| #ifdef __cplusplus | ||
| extern "C" { | ||
| #endif | ||
| #include <bits/getopt_core.h> | ||
| #include <bits/getopt_ext.h> | ||
| /* For communication from `getopt' to the caller. | ||
| When `getopt' finds an option that takes an argument, | ||
| the argument value is returned here. | ||
| Also, when `ordering' is RETURN_IN_ORDER, | ||
| each non-option ARGV-element is returned here. */ | ||
| extern char *optarg; | ||
| /* Index in ARGV of the next element to be scanned. | ||
| This is used for communication to and from the caller | ||
| and for communication between successive calls to `getopt'. | ||
| On entry to `getopt', zero means this is the first call; initialize. | ||
| When `getopt' returns -1, this is the index of the first of the | ||
| non-option elements that the caller should itself scan. | ||
| Otherwise, `optind' communicates from one call to the next | ||
| how much of ARGV has been scanned so far. */ | ||
| extern int optind; | ||
| /* Callers store zero here to inhibit the error message `getopt' prints | ||
| for unrecognized options. */ | ||
| extern int opterr; | ||
| /* Set to an option character which was unrecognized. */ | ||
| extern int optopt; | ||
| #ifndef __need_getopt | ||
| /* Describe the long-named options requested by the application. | ||
| The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector | ||
| of `struct option' terminated by an element containing a name which is | ||
| zero. | ||
| The field `has_arg' is: | ||
| no_argument (or 0) if the option does not take an argument, | ||
| required_argument (or 1) if the option requires an argument, | ||
| optional_argument (or 2) if the option takes an optional argument. | ||
| If the field `flag' is not NULL, it points to a variable that is set | ||
| to the value given in the field `val' when the option is found, but | ||
| left unchanged if the option is not found. | ||
| To have a long-named option do something other than set an `int' to | ||
| a compiled-in constant, such as set a value from `optarg', set the | ||
| option's `flag' field to zero and its `val' field to a nonzero | ||
| value (the equivalent single-letter option character, if there is | ||
| one). For long options that have a zero `flag' field, `getopt' | ||
| returns the contents of the `val' field. */ | ||
| struct option | ||
| { | ||
| # if (defined __STDC__ && __STDC__) || defined __cplusplus | ||
| const char *name; | ||
| # else | ||
| char *name; | ||
| # endif | ||
| /* has_arg can't be an enum because some compilers complain about | ||
| type mismatches in all the code that assumes it is an int. */ | ||
| int has_arg; | ||
| int *flag; | ||
| int val; | ||
| }; | ||
| /* Names for the values of the `has_arg' field of `struct option'. */ | ||
| # define no_argument 0 | ||
| # define required_argument 1 | ||
| # define optional_argument 2 | ||
| #endif /* need getopt */ | ||
| /* Get definitions and prototypes for functions to process the | ||
| arguments in ARGV (ARGC of them, minus the program name) for | ||
| options given in OPTS. | ||
| Return the option character from OPTS just read. Return -1 when | ||
| there are no more options. For unrecognized options, or options | ||
| missing arguments, `optopt' is set to the option letter, and '?' is | ||
| returned. | ||
| The OPTS string is a list of characters which are recognized option | ||
| letters, optionally followed by colons, specifying that that letter | ||
| takes an argument, to be placed in `optarg'. | ||
| If a letter in OPTS is followed by two colons, its argument is | ||
| optional. This behavior is specific to the GNU `getopt'. | ||
| The argument `--' causes premature termination of argument | ||
| scanning, explicitly telling `getopt' that there are no more | ||
| options. | ||
| If OPTS begins with `--', then non-option arguments are treated as | ||
| arguments to the option '\0'. This behavior is specific to the GNU | ||
| `getopt'. */ | ||
| #if (defined __STDC__ && __STDC__) || defined __cplusplus | ||
| # ifdef __GNU_LIBRARY__ | ||
| /* Many other libraries have conflicting prototypes for getopt, with | ||
| differences in the consts, in stdlib.h. To avoid compilation | ||
| errors, only prototype getopt for the GNU C library. */ | ||
| extern int getopt (int __argc, char *const *__argv, const char *__shortopts); | ||
| # else /* not __GNU_LIBRARY__ */ | ||
| extern int getopt (); | ||
| # endif /* __GNU_LIBRARY__ */ | ||
| # ifndef __need_getopt | ||
| extern int getopt_long (int __argc, char *const *__argv, const char *__shortopts, | ||
| const struct option *__longopts, int *__longind); | ||
| extern int getopt_long_only (int __argc, char *const *__argv, | ||
| const char *__shortopts, | ||
| const struct option *__longopts, int *__longind); | ||
| /* Internal only. Users should not call this directly. */ | ||
| extern int _getopt_internal (int __argc, char *const *__argv, | ||
| const char *__shortopts, | ||
| const struct option *__longopts, int *__longind, | ||
| int __long_only); | ||
| # endif | ||
| #else /* not __STDC__ */ | ||
| extern int getopt (); | ||
| # ifndef __need_getopt | ||
| extern int getopt_long (); | ||
| extern int getopt_long_only (); | ||
| extern int _getopt_internal (); | ||
| # endif | ||
| #endif /* __STDC__ */ | ||
| #ifdef __cplusplus | ||
| } | ||
| #endif | ||
| /* Make sure we later can get all the definitions and declarations. */ | ||
| #undef __need_getopt | ||
| #endif /* getopt.h */ |
| /* getopt_long and getopt_long_only entry points for GNU getopt. | ||
| Copyright (C) 1987,88,89,90,91,92,93,94,96,97,98 | ||
| Free Software Foundation, Inc. | ||
| This file is part of the GNU C Library. | ||
| Copyright (C) 1987-2026 Free Software Foundation, Inc. | ||
| This file is part of the GNU C Library and is also part of gnulib. | ||
| Patches to this file should be submitted to both projects. | ||
@@ -17,60 +17,27 @@ The GNU C Library is free software; you can redistribute it and/or | ||
| You should have received a copy of the GNU Lesser General Public | ||
| License along with the GNU C Library; if not, write to the Free | ||
| Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA | ||
| 02111-1307 USA. */ | ||
| #ifdef HAVE_CONFIG_H | ||
| #include <config.h> | ||
| License along with the GNU C Library; if not, see | ||
| <https://www.gnu.org/licenses/>. */ | ||
| #ifndef _LIBC | ||
| # include <config.h> | ||
| #endif | ||
| #include "getopt.h" | ||
| #include "getopt_int.h" | ||
| #if !defined __STDC__ || !__STDC__ | ||
| /* This is a separate conditional since some stdc systems | ||
| reject `defined (const)'. */ | ||
| #ifndef const | ||
| #define const | ||
| #endif | ||
| #endif | ||
| int | ||
| getopt_long (int argc, char *__getopt_argv_const *argv, const char *options, | ||
| const struct option *long_options, int *opt_index) | ||
| { | ||
| return _getopt_internal (argc, (char **) argv, options, long_options, | ||
| opt_index, 0, 0); | ||
| } | ||
| #include <stdio.h> | ||
| /* Comment out all this code if we are using the GNU C Library, and are not | ||
| actually compiling the library itself. This code is part of the GNU C | ||
| Library, but also included in many other GNU distributions. Compiling | ||
| and linking in this code is a waste when using the GNU C library | ||
| (especially if it is a shared library). Rather than having every GNU | ||
| program understand `configure --with-gnu-libc' and omit the object files, | ||
| it is simpler to just do this in the source for each such file. */ | ||
| #define GETOPT_INTERFACE_VERSION 2 | ||
| #if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2 | ||
| #include <gnu-versions.h> | ||
| #if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION | ||
| #define ELIDE_CODE | ||
| #endif | ||
| #endif | ||
| #ifndef ELIDE_CODE | ||
| /* This needs to come after some library #include | ||
| to get __GNU_LIBRARY__ defined. */ | ||
| #ifdef __GNU_LIBRARY__ | ||
| #include <stdlib.h> | ||
| #endif | ||
| #ifndef NULL | ||
| #define NULL 0 | ||
| #endif | ||
| int | ||
| getopt_long (argc, argv, options, long_options, opt_index) | ||
| int argc; | ||
| char *const *argv; | ||
| const char *options; | ||
| const struct option *long_options; | ||
| int *opt_index; | ||
| _getopt_long_r (int argc, char **argv, const char *options, | ||
| const struct option *long_options, int *opt_index, | ||
| struct _getopt_data *d) | ||
| { | ||
| return _getopt_internal (argc, argv, options, long_options, opt_index, 0); | ||
| return _getopt_internal_r (argc, argv, options, long_options, opt_index, | ||
| 0, d, 0); | ||
| } | ||
@@ -84,23 +51,27 @@ | ||
| int | ||
| getopt_long_only (argc, argv, options, long_options, opt_index) | ||
| int argc; | ||
| char *const *argv; | ||
| const char *options; | ||
| const struct option *long_options; | ||
| int *opt_index; | ||
| getopt_long_only (int argc, char *__getopt_argv_const *argv, | ||
| const char *options, | ||
| const struct option *long_options, int *opt_index) | ||
| { | ||
| return _getopt_internal (argc, argv, options, long_options, opt_index, 1); | ||
| return _getopt_internal (argc, (char **) argv, options, long_options, | ||
| opt_index, 1, 0); | ||
| } | ||
| int | ||
| _getopt_long_only_r (int argc, char **argv, const char *options, | ||
| const struct option *long_options, int *opt_index, | ||
| struct _getopt_data *d) | ||
| { | ||
| return _getopt_internal_r (argc, argv, options, long_options, opt_index, | ||
| 1, d, 0); | ||
| } | ||
| #endif /* Not ELIDE_CODE. */ | ||
| #ifdef TEST | ||
| #include <stdio.h> | ||
| #include <stdlib.h> | ||
| int | ||
| main (argc, argv) | ||
| int argc; | ||
| char **argv; | ||
| main (int argc, char **argv) | ||
| { | ||
@@ -114,3 +85,3 @@ int c; | ||
| int option_index = 0; | ||
| static struct option long_options[] = | ||
| static const struct option long_options[] = | ||
| { | ||
@@ -165,7 +136,7 @@ {"add", 1, 0, 0}, | ||
| case 'c': | ||
| printf ("option c with value `%s'\n", optarg); | ||
| printf ("option c with value '%s'\n", optarg); | ||
| break; | ||
| case 'd': | ||
| printf ("option d with value `%s'\n", optarg); | ||
| printf ("option d with value '%s'\n", optarg); | ||
| break; | ||
@@ -172,0 +143,0 @@ |
| <?xml version="1.0" encoding="utf-8"?> | ||
| <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | ||
| <Target Name="ShowHotplugState" BeforeTargets="ClCompile"> | ||
| <Message Condition="'$(EnableWindowsHotplug)' == 'true'" Text="EnableWindowsHotplug is enabled" Importance="High" /> | ||
| <Message Condition="'$(EnableWindowsHotplug)' != 'true'" Text="EnableWindowsHotplug is disabled" Importance="High" /> | ||
| </Target> | ||
| <ItemDefinitionGroup Condition="'$(EnableWindowsHotplug)' == 'true'"> | ||
| <ClCompile> | ||
| <PreprocessorDefinitions>LIBUSB_WINDOWS_HOTPLUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> | ||
| </ClCompile> | ||
| </ItemDefinitionGroup> | ||
| <ItemGroup Label="ProjectConfigurations"> | ||
| <ProjectConfiguration Include="Debug|ARM"> | ||
| <Configuration>Debug</Configuration> | ||
| <Platform>ARM</Platform> | ||
| </ProjectConfiguration> | ||
| <ProjectConfiguration Include="Debug|ARM64"> | ||
@@ -20,6 +25,2 @@ <Configuration>Debug</Configuration> | ||
| </ProjectConfiguration> | ||
| <ProjectConfiguration Include="Release|ARM"> | ||
| <Configuration>Release</Configuration> | ||
| <Platform>ARM</Platform> | ||
| </ProjectConfiguration> | ||
| <ProjectConfiguration Include="Release|ARM64"> | ||
@@ -37,6 +38,2 @@ <Configuration>Release</Configuration> | ||
| </ProjectConfiguration> | ||
| <ProjectConfiguration Include="Debug-MT|ARM"> | ||
| <Configuration>Debug-MT</Configuration> | ||
| <Platform>ARM</Platform> | ||
| </ProjectConfiguration> | ||
| <ProjectConfiguration Include="Debug-MT|ARM64"> | ||
@@ -54,6 +51,2 @@ <Configuration>Debug-MT</Configuration> | ||
| </ProjectConfiguration> | ||
| <ProjectConfiguration Include="Release-MT|ARM"> | ||
| <Configuration>Release-MT</Configuration> | ||
| <Platform>ARM</Platform> | ||
| </ProjectConfiguration> | ||
| <ProjectConfiguration Include="Release-MT|ARM64"> | ||
@@ -71,3 +64,63 @@ <Configuration>Release-MT</Configuration> | ||
| </ProjectConfiguration> | ||
| <ProjectConfiguration Include="Debug-Hotplug|ARM64"> | ||
| <Configuration>Debug-Hotplug</Configuration> | ||
| <Platform>ARM64</Platform> | ||
| </ProjectConfiguration> | ||
| <ProjectConfiguration Include="Debug-Hotplug|Win32"> | ||
| <Configuration>Debug-Hotplug</Configuration> | ||
| <Platform>Win32</Platform> | ||
| </ProjectConfiguration> | ||
| <ProjectConfiguration Include="Debug-Hotplug|x64"> | ||
| <Configuration>Debug-Hotplug</Configuration> | ||
| <Platform>x64</Platform> | ||
| </ProjectConfiguration> | ||
| <ProjectConfiguration Include="Release-Hotplug|ARM64"> | ||
| <Configuration>Release-Hotplug</Configuration> | ||
| <Platform>ARM64</Platform> | ||
| </ProjectConfiguration> | ||
| <ProjectConfiguration Include="Release-Hotplug|Win32"> | ||
| <Configuration>Release-Hotplug</Configuration> | ||
| <Platform>Win32</Platform> | ||
| </ProjectConfiguration> | ||
| <ProjectConfiguration Include="Release-Hotplug|x64"> | ||
| <Configuration>Release-Hotplug</Configuration> | ||
| <Platform>x64</Platform> | ||
| </ProjectConfiguration> | ||
| <ProjectConfiguration Include="Debug-Hotplug-MT|ARM64"> | ||
| <Configuration>Debug-Hotplug-MT</Configuration> | ||
| <Platform>ARM64</Platform> | ||
| </ProjectConfiguration> | ||
| <ProjectConfiguration Include="Debug-Hotplug-MT|Win32"> | ||
| <Configuration>Debug-Hotplug-MT</Configuration> | ||
| <Platform>Win32</Platform> | ||
| </ProjectConfiguration> | ||
| <ProjectConfiguration Include="Debug-Hotplug-MT|x64"> | ||
| <Configuration>Debug-Hotplug-MT</Configuration> | ||
| <Platform>x64</Platform> | ||
| </ProjectConfiguration> | ||
| <ProjectConfiguration Include="Release-Hotplug-MT|ARM64"> | ||
| <Configuration>Release-Hotplug-MT</Configuration> | ||
| <Platform>ARM64</Platform> | ||
| </ProjectConfiguration> | ||
| <ProjectConfiguration Include="Release-Hotplug-MT|Win32"> | ||
| <Configuration>Release-Hotplug-MT</Configuration> | ||
| <Platform>Win32</Platform> | ||
| </ProjectConfiguration> | ||
| <ProjectConfiguration Include="Release-Hotplug-MT|x64"> | ||
| <Configuration>Release-Hotplug-MT</Configuration> | ||
| <Platform>x64</Platform> | ||
| </ProjectConfiguration> | ||
| </ItemGroup> | ||
| <PropertyGroup Condition="'$(Configuration)' == 'Debug-Hotplug'"> | ||
| <EnableWindowsHotplug>true</EnableWindowsHotplug> | ||
| </PropertyGroup> | ||
| <PropertyGroup Condition="'$(Configuration)' == 'Debug-Hotplug-MT'"> | ||
| <EnableWindowsHotplug>true</EnableWindowsHotplug> | ||
| </PropertyGroup> | ||
| <PropertyGroup Condition="'$(Configuration)' == 'Release-Hotplug'"> | ||
| <EnableWindowsHotplug>true</EnableWindowsHotplug> | ||
| </PropertyGroup> | ||
| <PropertyGroup Condition="'$(Configuration)' == 'Release-Hotplug-MT'"> | ||
| <EnableWindowsHotplug>true</EnableWindowsHotplug> | ||
| </PropertyGroup> | ||
| </Project> |
+8
-5
@@ -23,8 +23,11 @@ # libusb | ||
| - Hans de Goede <hdegoede@redhat.com> | ||
| - Xiaofan Chen <xiaofanc@gmail.com> | ||
| - Ludovic Rousseau <ludovic.rousseau@gmail.com> | ||
| - Nathan Hjelm <hjelmn@cs.unm.edu> | ||
| - Chris Dickens <christopher.a.dickens@gmail.com> | ||
| - Tormod Volden | ||
| - Sylvain Fasel | ||
| - Sean McBride | ||
| - Nathan Hjelm | ||
| - Xiaofan Chen | ||
| - Hans de Goede | ||
| - Ludovic Rousseau | ||
| - Chris Dickens | ||
| (Please use the mailing list rather than mailing developers directly) |
@@ -233,3 +233,4 @@ /* | ||
| libusb_error_name(tinfo[t].err)); | ||
| } else if (enumerate) { | ||
| } | ||
| if (enumerate) { | ||
| if (t > 0 && tinfo[t].devcount != last_devcount) { | ||
@@ -236,0 +237,0 @@ devcount_mismatch++; |
@@ -8,6 +8,7 @@ // It's not yet possible to automate actual Chrome's device selection, so | ||
| globalThis.navigator = { | ||
| usb: new WebUSB({ | ||
| allowAllDevices: true | ||
| }) | ||
| }; | ||
| // Node.js 21 introduced a global `navigator` object, so assign an empty one only if it's not present yet. | ||
| globalThis.navigator ??= {}; | ||
| navigator.usb = new WebUSB({ | ||
| allowAllDevices: true | ||
| }); |
+1
-1
@@ -5,3 +5,3 @@ { | ||
| "license": "MIT", | ||
| "version": "2.17.0", | ||
| "version": "2.18.0", | ||
| "main": "dist/index.js", | ||
@@ -8,0 +8,0 @@ "engines": { |
+14
-0
@@ -609,2 +609,16 @@ # USB Library for Node.JS | ||
| ## Prerequisites | ||
| A valid node-gyp build system | ||
| ### Windows | ||
| - [chocolatey](https://chocolatey.org/) | ||
| ```bash | ||
| choco install python visualstudio2019-workload-vctools -y | ||
| ``` | ||
| If the automatic installation of VS Tools fails or pauses, open the installer GUI to resume installation. | ||
| ## Setup | ||
@@ -611,0 +625,0 @@ Libusb is included as a submodule, clone this repository and then the submodule as follows: |
+3
-3
| #include "node_usb.h" | ||
| #include "thread_name.h" | ||
| #include "hotplug/hotplug.h" | ||
| #include "hotplug.h" | ||
@@ -133,4 +133,4 @@ Napi::Value SetDebugLevel(const Napi::CallbackInfo& info); | ||
| int res = instanceData->hotplugManager->supportedHotplugEvents(); | ||
| return Napi::Number::New(env, res); | ||
| bool res = instanceData->hotplugManager->supportedHotplugEvents(); | ||
| return Napi::Boolean::New(env, res); | ||
| } | ||
@@ -137,0 +137,0 @@ |
+4
-4
@@ -167,4 +167,4 @@ assert = require('assert') | ||
| it 'times out', (done) -> | ||
| iface.endpoints[2].timeout = 20 | ||
| iface.endpoints[2].transfer 64, (e, d) -> | ||
| iface.endpoints[4].timeout = 20 | ||
| iface.endpoints[4].transfer 64, (e, d) -> | ||
| assert.equal e.errno, usb.LIBUSB_TRANSFER_TIMED_OUT | ||
@@ -232,4 +232,4 @@ done() | ||
| it 'times out', (done) -> | ||
| iface.endpoints[3].timeout = 20 | ||
| iface.endpoints[3].transfer [1,2,3,4], (e) -> | ||
| iface.endpoints[5].timeout = 20 | ||
| iface.endpoints[5].transfer [1,2,3,4], (e) -> | ||
| assert.equal e.errno, usb.LIBUSB_TRANSFER_TIMED_OUT | ||
@@ -236,0 +236,0 @@ done() |
+22
-12
@@ -185,3 +185,4 @@ assert = require('assert') | ||
| device = null | ||
| b = Uint8Array.from([0x30...0x40]).buffer | ||
| b1 = Uint8Array.from([0x30...0x40]).buffer | ||
| b2 = Uint8Array.from([0x32...0x42]).buffer | ||
@@ -195,16 +196,16 @@ before -> | ||
| transferResult = await device.controlTransferOut({ | ||
| requestType: 'device', | ||
| recipient: 'vendor', | ||
| requestType: 'vendor', | ||
| recipient: 'device', | ||
| request: 0x81, | ||
| value: 0, | ||
| index: 0 | ||
| }, b) | ||
| }, b1) | ||
| assert.equal(transferResult.status, 'ok') | ||
| assert.equal(transferResult.bytesWritten, b.byteLength) | ||
| assert.equal(transferResult.bytesWritten, b1.byteLength) | ||
| it 'should control transfer IN', -> | ||
| transferResult = await device.controlTransferIn({ | ||
| requestType: 'device', | ||
| recipient: 'vendor', | ||
| requestType: 'vendor', | ||
| recipient: 'device', | ||
| request: 0x81, | ||
@@ -216,15 +217,24 @@ value: 0, | ||
| assert.equal(transferResult.status, 'ok') | ||
| assert.equal(transferResult.data.buffer.toString(), b.toString()) | ||
| assert.equal(transferResult.data.byteLength, b1.byteLength) | ||
| resultBuffer = Buffer.from(transferResult.data.buffer, transferResult.data.byteOffset, transferResult.data.byteLength); | ||
| expectedBuffer = Buffer.from(b1, 0, b1.byteLength); | ||
| assert(resultBuffer.equals(expectedBuffer)); | ||
| it 'should transfer OUT', -> | ||
| transferResult = await device.transferOut(2, b) | ||
| transferResult = await device.transferOut(4, b2) | ||
| assert.equal(transferResult.status, 'ok') | ||
| assert.equal(transferResult.bytesWritten, b.byteLength) | ||
| assert.equal(transferResult.bytesWritten, b2.byteLength) | ||
| it 'should transfer IN', -> | ||
| transferResult = await device.transferIn(1, 64) | ||
| transferResult = await device.transferIn(3, b2.byteLength) | ||
| assert.equal(transferResult.status, 'ok') | ||
| assert.equal(transferResult.data.byteLength, 64) | ||
| assert.equal(transferResult.data.byteLength, b2.byteLength) | ||
| resultBuffer = Buffer.from(transferResult.data.buffer, transferResult.data.byteOffset, transferResult.data.byteLength); | ||
| expectedBuffer = Buffer.from(b2, 0, b2.byteLength); | ||
| assert(resultBuffer.equals(expectedBuffer)); | ||
| after -> | ||
| device.close() |
| #ifndef _USB_HOTPLUG_H | ||
| #define _USB_HOTPLUG_H | ||
| #include "../node_usb.h" | ||
| #define HOTPLUG_SUPPORTS_NONE 0 | ||
| #define HOTPLUG_SUPPORTS_DEVICES 1 | ||
| #define HOTPLUG_SUPPORTS_IDS 2 | ||
| class HotPlugManager { | ||
| public: | ||
| static std::unique_ptr<HotPlugManager> create(); | ||
| virtual int supportedHotplugEvents() = 0; | ||
| virtual void enableHotplug(const Napi::Env& env, ModuleData* instanceData) = 0; | ||
| virtual void disableHotplug(const Napi::Env& env, ModuleData* instanceData) = 0; | ||
| }; | ||
| void handleHotplug(HotPlug* info); | ||
| #endif |
| #include "hotplug.h" | ||
| struct HotPlug { | ||
| libusb_device* device; | ||
| libusb_hotplug_event event; | ||
| Napi::ObjectReference* hotplugThis; | ||
| }; | ||
| int LIBUSB_CALL hotplug_callback(libusb_context* ctx, libusb_device* device, libusb_hotplug_event event, void* user_data) { | ||
| libusb_ref_device(device); | ||
| ModuleData* instanceData = (ModuleData*)user_data; | ||
| instanceData->hotplugQueue.post(new HotPlug {device, event, &instanceData->hotplugThis}); | ||
| return 0; | ||
| } | ||
| class HotPlugManagerLibUsb: public HotPlugManager { | ||
| int supportedHotplugEvents() { | ||
| int res = libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG); | ||
| return res > 0 ? HOTPLUG_SUPPORTS_DEVICES : HOTPLUG_SUPPORTS_NONE; | ||
| } | ||
| void enableHotplug(const Napi::Env& env, ModuleData* instanceData) { | ||
| libusb_context* usb_context = instanceData->usb_context; | ||
| CHECK_USB(libusb_hotplug_register_callback( | ||
| usb_context, | ||
| (libusb_hotplug_event)(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT), | ||
| (libusb_hotplug_flag)0, | ||
| LIBUSB_HOTPLUG_MATCH_ANY, | ||
| LIBUSB_HOTPLUG_MATCH_ANY, | ||
| LIBUSB_HOTPLUG_MATCH_ANY, | ||
| hotplug_callback, | ||
| instanceData, | ||
| &hotplugHandle | ||
| )); | ||
| } | ||
| void disableHotplug(const Napi::Env& env, ModuleData* instanceData) { | ||
| libusb_context* usb_context = instanceData->usb_context; | ||
| libusb_hotplug_deregister_callback(usb_context, hotplugHandle); | ||
| } | ||
| libusb_hotplug_callback_handle hotplugHandle; | ||
| }; | ||
| std::unique_ptr<HotPlugManager> HotPlugManager::create() { | ||
| return std::make_unique<HotPlugManagerLibUsb>(); | ||
| } | ||
| void handleHotplug(HotPlug* info) { | ||
| Napi::ObjectReference* hotplugThis = info->hotplugThis; | ||
| Napi::Env env = hotplugThis->Env(); | ||
| Napi::HandleScope scope(env); | ||
| libusb_device* dev = info->device; | ||
| libusb_hotplug_event event = info->event; | ||
| delete info; | ||
| DEBUG_LOG("HandleHotplug %p %i", dev, event); | ||
| Napi::Object v8dev = Device::get(env, dev); | ||
| libusb_unref_device(dev); | ||
| Napi::Object v8VidPid = Napi::Object::New(env); | ||
| auto deviceDescriptor = v8dev.Get("deviceDescriptor"); | ||
| if (deviceDescriptor.IsObject()) { | ||
| v8VidPid.Set("idVendor", deviceDescriptor.As<Napi::Object>().Get("idVendor")); | ||
| v8VidPid.Set("idProduct", deviceDescriptor.As<Napi::Object>().Get("idProduct")); | ||
| } | ||
| Napi::String eventName; | ||
| Napi::String changeEventName; | ||
| if (LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED == event) { | ||
| DEBUG_LOG("Device arrived"); | ||
| eventName = Napi::String::New(env, "attach"); | ||
| changeEventName = Napi::String::New(env, "attachIds"); | ||
| } else if (LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT == event) { | ||
| DEBUG_LOG("Device left"); | ||
| eventName = Napi::String::New(env, "detach"); | ||
| changeEventName = Napi::String::New(env, "detachIds"); | ||
| } else { | ||
| DEBUG_LOG("Unhandled hotplug event %d\n", event); | ||
| return; | ||
| } | ||
| hotplugThis->Get("emit").As<Napi::Function>().MakeCallback(hotplugThis->Value(), { eventName, v8dev }); | ||
| hotplugThis->Get("emit").As<Napi::Function>().MakeCallback(hotplugThis->Value(), { changeEventName, v8VidPid }); | ||
| } |
| #include "hotplug.h" | ||
| // Include Windows headers | ||
| #include <windows.h> | ||
| #include <initguid.h> | ||
| #include <Cfgmgr32.h> | ||
| #include <usbiodef.h> | ||
| #include <locale> | ||
| #include <codecvt> | ||
| #include <cwctype> | ||
| #define VID_TAG L"VID_" | ||
| #define PID_TAG L"PID_" | ||
| struct HotPlug { | ||
| int vid; | ||
| int pid; | ||
| CM_NOTIFY_ACTION event; | ||
| Napi::ObjectReference* hotplugThis; | ||
| }; | ||
| void extractVidPid(wchar_t *buf, int *vid, int *pid) | ||
| { | ||
| // Example input: \\?\USB#VID_0FD9&PID_0060#000000000000#{a5dcbf10-6530-11d2-901f-00c04fb951ed} | ||
| *vid = 0; | ||
| *pid = 0; | ||
| if (buf == NULL) | ||
| { | ||
| return; | ||
| } | ||
| auto string = std::wstring(buf); | ||
| std::transform(string.begin(), string.end(), string.begin(), std::towupper); | ||
| wchar_t* temp = new wchar_t[5]; | ||
| temp[4] = L'\0'; | ||
| const wchar_t *vidStr = wcsstr(string.data(), VID_TAG); | ||
| const wchar_t *pidStr = wcsstr(string.data(), PID_TAG); | ||
| if (vidStr != nullptr) | ||
| { | ||
| memcpy(temp, vidStr + wcslen(VID_TAG), 4 * sizeof(wchar_t)); | ||
| *vid = wcstol(temp, NULL, 16); | ||
| } | ||
| if (pidStr != nullptr) | ||
| { | ||
| memcpy(temp, pidStr + wcslen(PID_TAG), 4 * sizeof(wchar_t)); | ||
| *pid = wcstol(temp, NULL, 16); | ||
| } | ||
| } | ||
| DWORD WINAPI MyCMInterfaceNotification(HCMNOTIFICATION hNotify, PVOID Context, CM_NOTIFY_ACTION Action, PCM_NOTIFY_EVENT_DATA EventData, DWORD EventDataSize) | ||
| { | ||
| switch (Action) | ||
| { | ||
| case CM_NOTIFY_ACTION_DEVICEINTERFACEARRIVAL: | ||
| case CM_NOTIFY_ACTION_DEVICEINTERFACEREMOVAL: | ||
| { | ||
| ModuleData* instanceData = (ModuleData*)Context; | ||
| int vid = 0; | ||
| int pid = 0; | ||
| extractVidPid(EventData->u.DeviceInterface.SymbolicLink, &vid, &pid); | ||
| instanceData->hotplugQueue.post(new HotPlug {vid, pid, Action, &instanceData->hotplugThis}); | ||
| break; | ||
| } | ||
| default: | ||
| break; | ||
| } | ||
| return ERROR_SUCCESS; | ||
| } | ||
| class HotPlugManagerWindows : public HotPlugManager | ||
| { | ||
| public: | ||
| HotPlugManagerWindows() | ||
| : hcm(nullptr) | ||
| { | ||
| cmNotifyFilter = { 0 }; | ||
| cmNotifyFilter.cbSize = sizeof(cmNotifyFilter); | ||
| cmNotifyFilter.Flags = 0; | ||
| cmNotifyFilter.FilterType = CM_NOTIFY_FILTER_TYPE_DEVICEINTERFACE; | ||
| cmNotifyFilter.u.DeviceInterface.ClassGuid = GUID_DEVINTERFACE_USB_DEVICE; | ||
| } | ||
| int supportedHotplugEvents() | ||
| { | ||
| return HOTPLUG_SUPPORTS_IDS; | ||
| } | ||
| void enableHotplug(const Napi::Env &env, ModuleData *instanceData) | ||
| { | ||
| if (isRunning) | ||
| { | ||
| return; | ||
| } | ||
| isRunning = true; | ||
| auto res = CM_Register_Notification(&cmNotifyFilter, (PVOID)instanceData, (PCM_NOTIFY_CALLBACK)&MyCMInterfaceNotification, &hcm); | ||
| if (res != CR_SUCCESS) | ||
| { | ||
| isRunning = false; | ||
| THROW_ERROR("RegisterNotification failed") | ||
| } | ||
| } | ||
| void disableHotplug(const Napi::Env &env, ModuleData *instanceData) | ||
| { | ||
| if (isRunning) | ||
| { | ||
| isRunning = false; | ||
| if (hcm) { | ||
| CM_Unregister_Notification(hcm); | ||
| hcm = nullptr; | ||
| } | ||
| } | ||
| } | ||
| private: | ||
| std::atomic<bool> isRunning = {false}; | ||
| HCMNOTIFICATION hcm; | ||
| CM_NOTIFY_FILTER cmNotifyFilter; | ||
| }; | ||
| std::unique_ptr<HotPlugManager> HotPlugManager::create() | ||
| { | ||
| return std::make_unique<HotPlugManagerWindows>(); | ||
| } | ||
| void handleHotplug(HotPlug* info) { | ||
| Napi::ObjectReference* hotplugThis = info->hotplugThis; | ||
| Napi::Env env = hotplugThis->Env(); | ||
| Napi::HandleScope scope(env); | ||
| int vid = info->vid; | ||
| int pid = info->pid; | ||
| Napi::Object v8VidPid = Napi::Object::New(env); | ||
| v8VidPid.Set("idVendor", Napi::Number::New(env, vid)); | ||
| v8VidPid.Set("idProduct", Napi::Number::New(env, pid)); | ||
| CM_NOTIFY_ACTION event = info->event; | ||
| delete info; | ||
| DEBUG_LOG("HandleHotplug %i %i %i", vid, pid, event); | ||
| Napi::String eventName; | ||
| if (CM_NOTIFY_ACTION_DEVICEINTERFACEARRIVAL == event) { | ||
| DEBUG_LOG("Device arrived"); | ||
| eventName = Napi::String::New(env, "attachIds"); | ||
| } else if (CM_NOTIFY_ACTION_DEVICEINTERFACEREMOVAL == event) { | ||
| DEBUG_LOG("Device left"); | ||
| eventName = Napi::String::New(env, "detachIds"); | ||
| } else { | ||
| DEBUG_LOG("Unhandled hotplug event %d\n", event); | ||
| return; | ||
| } | ||
| hotplugThis->Get("emit").As<Napi::Function>().MakeCallback(hotplugThis->Value(), { eventName, v8VidPid }); | ||
| } |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
6956797
2.27%218
6.34%670
2.13%2503
-0.71%