summaryrefslogtreecommitdiff
path: root/contrib/SDL-3.2.8/src/hidapi/windows/hid.c
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2025-12-27 12:03:39 -0800
committer3gg <3gg@shellblade.net>2025-12-27 12:03:39 -0800
commit5a079a2d114f96d4847d1ee305d5b7c16eeec50e (patch)
tree8926ab44f168acf787d8e19608857b3af0f82758 /contrib/SDL-3.2.8/src/hidapi/windows/hid.c
Initial commit
Diffstat (limited to 'contrib/SDL-3.2.8/src/hidapi/windows/hid.c')
-rw-r--r--contrib/SDL-3.2.8/src/hidapi/windows/hid.c1728
1 files changed, 1728 insertions, 0 deletions
diff --git a/contrib/SDL-3.2.8/src/hidapi/windows/hid.c b/contrib/SDL-3.2.8/src/hidapi/windows/hid.c
new file mode 100644
index 0000000..3376ba9
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/hidapi/windows/hid.c
@@ -0,0 +1,1728 @@
1/*******************************************************
2 HIDAPI - Multi-Platform library for
3 communication with HID devices.
4
5 Alan Ott
6 Signal 11 Software
7
8 libusb/hidapi Team
9
10 Copyright 2022, All Rights Reserved.
11
12 At the discretion of the user of this library,
13 this software may be licensed under the terms of the
14 GNU General Public License v3, a BSD-Style license, or the
15 original HIDAPI license as outlined in the LICENSE.txt,
16 LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt
17 files located at the root of the source distribution.
18 These files may also be found in the public source
19 code repository located at:
20 https://github.com/libusb/hidapi .
21********************************************************/
22
23#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
24/* Do not warn about wcsncpy usage.
25 https://docs.microsoft.com/cpp/c-runtime-library/security-features-in-the-crt */
26#define _CRT_SECURE_NO_WARNINGS
27#endif
28
29#ifdef __cplusplus
30extern "C" {
31#endif
32
33#include "hidapi_winapi.h"
34
35#include <windows.h>
36
37#ifndef _NTDEF_
38typedef LONG NTSTATUS;
39#endif
40
41#ifndef WC_ERR_INVALID_CHARS
42#define WC_ERR_INVALID_CHARS 0x00000080
43#endif
44#ifndef _WIN32_WINNT_WIN8
45#define _WIN32_WINNT_WIN8 0x0602
46#endif
47
48#ifdef __CYGWIN__
49#include <ntdef.h>
50#include <wctype.h>
51#define _wcsdup wcsdup
52#endif
53
54/*#define HIDAPI_USE_DDK*/
55
56#include "hidapi_cfgmgr32.h"
57#include "hidapi_hidclass.h"
58#include "hidapi_hidsdi.h"
59
60#ifndef HIDAPI_USING_SDL_RUNTIME
61#include <stdio.h>
62#include <stdlib.h>
63#include <string.h>
64#endif
65
66#ifdef MIN
67#undef MIN
68#endif
69#define MIN(x,y) ((x) < (y)? (x): (y))
70
71/* MAXIMUM_USB_STRING_LENGTH from usbspec.h is 255 */
72/* BLUETOOTH_DEVICE_NAME_SIZE from bluetoothapis.h is 256 */
73#define MAX_STRING_WCHARS 256
74
75/* For certain USB devices, using a buffer larger or equal to 127 wchars results
76 in successful completion of HID API functions, but a broken string is stored
77 in the output buffer. This behaviour persists even if HID API is bypassed and
78 HID IOCTLs are passed to the HID driver directly. Therefore, for USB devices,
79 the buffer MUST NOT exceed 126 WCHARs.
80*/
81
82#define MAX_STRING_WCHARS_USB 126
83
84static struct hid_api_version api_version = {
85 .major = HID_API_VERSION_MAJOR,
86 .minor = HID_API_VERSION_MINOR,
87 .patch = HID_API_VERSION_PATCH
88};
89
90#ifndef HIDAPI_USE_DDK
91/* Since we're not building with the DDK, and the HID header
92 files aren't part of the Windows SDK, we define what we need ourselves.
93 In lookup_functions(), the function pointers
94 defined below are set. */
95
96static HidD_GetHidGuid_ HidD_GetHidGuid;
97static HidD_GetAttributes_ HidD_GetAttributes;
98static HidD_GetSerialNumberString_ HidD_GetSerialNumberString;
99static HidD_GetManufacturerString_ HidD_GetManufacturerString;
100static HidD_GetProductString_ HidD_GetProductString;
101static HidD_SetFeature_ HidD_SetFeature;
102static HidD_GetFeature_ HidD_GetFeature;
103static HidD_GetInputReport_ HidD_GetInputReport;
104static HidD_GetIndexedString_ HidD_GetIndexedString;
105static HidD_GetPreparsedData_ HidD_GetPreparsedData;
106static HidD_FreePreparsedData_ HidD_FreePreparsedData;
107static HidP_GetCaps_ HidP_GetCaps;
108static HidD_SetNumInputBuffers_ HidD_SetNumInputBuffers;
109static HidD_SetOutputReport_ HidD_SetOutputReport;
110
111static CM_Locate_DevNodeW_ CM_Locate_DevNodeW = NULL;
112static CM_Get_Parent_ CM_Get_Parent = NULL;
113static CM_Get_DevNode_PropertyW_ CM_Get_DevNode_PropertyW = NULL;
114static CM_Get_Device_Interface_PropertyW_ CM_Get_Device_Interface_PropertyW = NULL;
115static CM_Get_Device_Interface_List_SizeW_ CM_Get_Device_Interface_List_SizeW = NULL;
116static CM_Get_Device_Interface_ListW_ CM_Get_Device_Interface_ListW = NULL;
117
118static HMODULE hid_lib_handle = NULL;
119static HMODULE cfgmgr32_lib_handle = NULL;
120static BOOLEAN hidapi_initialized = FALSE;
121
122static void free_library_handles(void)
123{
124 if (hid_lib_handle)
125 FreeLibrary(hid_lib_handle);
126 hid_lib_handle = NULL;
127 if (cfgmgr32_lib_handle)
128 FreeLibrary(cfgmgr32_lib_handle);
129 cfgmgr32_lib_handle = NULL;
130}
131
132#if defined(__GNUC__) && __GNUC__ >= 8
133# pragma GCC diagnostic push
134# pragma GCC diagnostic ignored "-Wcast-function-type"
135#endif
136
137static int lookup_functions(void)
138{
139 hid_lib_handle = LoadLibraryW(L"hid.dll");
140 if (hid_lib_handle == NULL) {
141 goto err;
142 }
143
144 cfgmgr32_lib_handle = LoadLibraryW(L"cfgmgr32.dll");
145 if (cfgmgr32_lib_handle == NULL) {
146 goto err;
147 }
148
149#define RESOLVE(lib_handle, x) x = (x##_)GetProcAddress(lib_handle, #x); if (!x) goto err;
150
151 RESOLVE(hid_lib_handle, HidD_GetHidGuid);
152 RESOLVE(hid_lib_handle, HidD_GetAttributes);
153 RESOLVE(hid_lib_handle, HidD_GetSerialNumberString);
154 RESOLVE(hid_lib_handle, HidD_GetManufacturerString);
155 RESOLVE(hid_lib_handle, HidD_GetProductString);
156 RESOLVE(hid_lib_handle, HidD_SetFeature);
157 RESOLVE(hid_lib_handle, HidD_GetFeature);
158 RESOLVE(hid_lib_handle, HidD_GetInputReport);
159 RESOLVE(hid_lib_handle, HidD_GetIndexedString);
160 RESOLVE(hid_lib_handle, HidD_GetPreparsedData);
161 RESOLVE(hid_lib_handle, HidD_FreePreparsedData);
162 RESOLVE(hid_lib_handle, HidP_GetCaps);
163 RESOLVE(hid_lib_handle, HidD_SetNumInputBuffers);
164 RESOLVE(hid_lib_handle, HidD_SetOutputReport);
165
166 RESOLVE(cfgmgr32_lib_handle, CM_Locate_DevNodeW);
167 RESOLVE(cfgmgr32_lib_handle, CM_Get_Parent);
168 RESOLVE(cfgmgr32_lib_handle, CM_Get_DevNode_PropertyW);
169 RESOLVE(cfgmgr32_lib_handle, CM_Get_Device_Interface_PropertyW);
170 RESOLVE(cfgmgr32_lib_handle, CM_Get_Device_Interface_List_SizeW);
171 RESOLVE(cfgmgr32_lib_handle, CM_Get_Device_Interface_ListW);
172
173#undef RESOLVE
174
175 return 0;
176
177err:
178 free_library_handles();
179 return -1;
180}
181
182#if defined(__GNUC__) && __GNUC__ >= 8
183# pragma GCC diagnostic pop
184#endif
185
186#endif /* HIDAPI_USE_DDK */
187
188struct hid_device_ {
189 HANDLE device_handle;
190 BOOL blocking;
191 USHORT output_report_length;
192 unsigned char *write_buf;
193 size_t input_report_length;
194 USHORT feature_report_length;
195 unsigned char *feature_buf;
196 wchar_t *last_error_str;
197 BOOL read_pending;
198 char *read_buf;
199 OVERLAPPED ol;
200 OVERLAPPED write_ol;
201 struct hid_device_info* device_info;
202 BOOL use_hid_write_output_report;
203};
204
205static BOOL IsWindowsVersionOrGreater(WORD wMajorVersion, WORD wMinorVersion, WORD wServicePackMajor)
206{
207 OSVERSIONINFOEXW osvi;
208 DWORDLONG const dwlConditionMask = VerSetConditionMask(
209 VerSetConditionMask(
210 VerSetConditionMask(
211 0, VER_MAJORVERSION, VER_GREATER_EQUAL ),
212 VER_MINORVERSION, VER_GREATER_EQUAL ),
213 VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL );
214
215 memset(&osvi, 0, sizeof(osvi));
216 osvi.dwOSVersionInfoSize = sizeof( osvi );
217 osvi.dwMajorVersion = wMajorVersion;
218 osvi.dwMinorVersion = wMinorVersion;
219 osvi.wServicePackMajor = wServicePackMajor;
220
221 return VerifyVersionInfoW(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR, dwlConditionMask) != FALSE;
222}
223
224static hid_device *new_hid_device(void)
225{
226 hid_device *dev = (hid_device*) calloc(1, sizeof(hid_device));
227
228 if (dev == NULL) {
229 return NULL;
230 }
231
232 dev->device_handle = INVALID_HANDLE_VALUE;
233 dev->blocking = TRUE;
234 dev->output_report_length = 0;
235 dev->write_buf = NULL;
236 dev->input_report_length = 0;
237 dev->feature_report_length = 0;
238 dev->feature_buf = NULL;
239 dev->last_error_str = NULL;
240 dev->read_pending = FALSE;
241 dev->read_buf = NULL;
242 memset(&dev->ol, 0, sizeof(dev->ol));
243 dev->ol.hEvent = CreateEvent(NULL, FALSE, FALSE /*initial state f=nonsignaled*/, NULL);
244 memset(&dev->write_ol, 0, sizeof(dev->write_ol));
245 dev->write_ol.hEvent = CreateEvent(NULL, FALSE, FALSE /*initial state f=nonsignaled*/, NULL);
246 dev->device_info = NULL;
247
248 return dev;
249}
250
251static void free_hid_device(hid_device *dev)
252{
253 CloseHandle(dev->ol.hEvent);
254 CloseHandle(dev->write_ol.hEvent);
255 CloseHandle(dev->device_handle);
256 free(dev->last_error_str);
257 dev->last_error_str = NULL;
258 free(dev->write_buf);
259 free(dev->feature_buf);
260 free(dev->read_buf);
261 hid_free_enumeration(dev->device_info);
262 free(dev);
263}
264
265static void register_winapi_error_to_buffer(wchar_t **error_buffer, const WCHAR *op)
266{
267 WCHAR system_err_buf[1024];
268 DWORD error_code = GetLastError();
269
270 free(*error_buffer);
271 *error_buffer = NULL;
272
273#ifdef HIDAPI_USING_SDL_RUNTIME
274 /* Thread-safe error handling */
275 SDL_ClearError();
276#endif
277
278 /* Only clear out error messages if NULL is passed into op */
279 if (!op) {
280 return;
281 }
282
283 DWORD system_err_len = FormatMessageW(
284 FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
285 NULL,
286 error_code,
287 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
288 system_err_buf, ARRAYSIZE(system_err_buf),
289 NULL);
290
291 DWORD op_len = (DWORD)wcslen(op);
292
293 DWORD op_prefix_len =
294 op_len
295 + 15 /*: (0x00000000) */
296 ;
297 DWORD msg_len =
298 + op_prefix_len
299 + system_err_len
300 ;
301
302 WCHAR *msg = (WCHAR *)calloc(msg_len + 1, sizeof (WCHAR));
303
304 if (!msg)
305 return;
306
307 int printf_written = swprintf(msg, msg_len + 1, L"%.*ls: (0x%08X) %.*ls", (int)op_len, op, error_code, (int)system_err_len, system_err_buf);
308
309 if (printf_written < 0)
310 {
311 /* Highly unlikely */
312 msg[0] = L'\0';
313 return;
314 }
315
316 /* Get rid of the CR and LF that FormatMessage() sticks at the
317 end of the message. Thanks Microsoft! */
318 while(msg[msg_len-1] == L'\r' || msg[msg_len-1] == L'\n' || msg[msg_len-1] == L' ')
319 {
320 msg[msg_len-1] = L'\0';
321 msg_len--;
322 }
323
324#ifdef HIDAPI_USING_SDL_RUNTIME
325 /* Thread-safe error handling */
326 char *error_utf8 = SDL_iconv_wchar_utf8(msg);
327 if (error_utf8) {
328 SDL_SetError("%s", error_utf8);
329 SDL_free(error_utf8);
330 }
331 free(msg);
332#else
333 *error_buffer = msg;
334#endif
335}
336
337#if defined(__GNUC__) && (__GNUC__ + (__GNUC_MINOR__ >= 6) > 4)
338# pragma GCC diagnostic push
339# pragma GCC diagnostic ignored "-Warray-bounds"
340#endif
341/* A bug in GCC/mingw gives:
342 * error: array subscript 0 is outside array bounds of 'wchar_t *[0]' {aka 'short unsigned int *[]'} [-Werror=array-bounds]
343 * | free(*error_buffer);
344 * Which doesn't make sense in this context. */
345
346static void register_string_error_to_buffer(wchar_t **error_buffer, const WCHAR *string_error)
347{
348 free(*error_buffer);
349 *error_buffer = NULL;
350
351#ifdef HIDAPI_USING_SDL_RUNTIME
352 /* Thread-safe error handling */
353 char *error_utf8 = string_error ? SDL_iconv_wchar_utf8(string_error) : NULL;
354 if (error_utf8) {
355 SDL_SetError("%s", error_utf8);
356 SDL_free(error_utf8);
357 } else {
358 SDL_ClearError();
359 }
360#else
361 if (string_error) {
362 *error_buffer = _wcsdup(string_error);
363 }
364#endif /* HIDAPI_USING_SDL_RUNTIME */
365}
366
367#if defined(__GNUC__) && (__GNUC__ + (__GNUC_MINOR__ >= 6) > 4)
368# pragma GCC diagnostic pop
369#endif
370
371static void register_winapi_error(hid_device *dev, const WCHAR *op)
372{
373 register_winapi_error_to_buffer(&dev->last_error_str, op);
374}
375
376static void register_string_error(hid_device *dev, const WCHAR *string_error)
377{
378 register_string_error_to_buffer(&dev->last_error_str, string_error);
379}
380
381static wchar_t *last_global_error_str = NULL;
382
383static void register_global_winapi_error(const WCHAR *op)
384{
385 register_winapi_error_to_buffer(&last_global_error_str, op);
386}
387
388static void register_global_error(const WCHAR *string_error)
389{
390 register_string_error_to_buffer(&last_global_error_str, string_error);
391}
392
393static HANDLE open_device(const wchar_t *path, BOOL open_rw)
394{
395 HANDLE handle;
396 DWORD desired_access = (open_rw)? (GENERIC_WRITE | GENERIC_READ): 0;
397 DWORD share_mode = FILE_SHARE_READ|FILE_SHARE_WRITE;
398
399 handle = CreateFileW(path,
400 desired_access,
401 share_mode,
402 NULL,
403 OPEN_EXISTING,
404 FILE_FLAG_OVERLAPPED,/*FILE_ATTRIBUTE_NORMAL,*/
405 0);
406
407 return handle;
408}
409
410HID_API_EXPORT const struct hid_api_version* HID_API_CALL hid_version(void)
411{
412 return &api_version;
413}
414
415HID_API_EXPORT const char* HID_API_CALL hid_version_str(void)
416{
417 return HID_API_VERSION_STR;
418}
419
420int HID_API_EXPORT hid_init(void)
421{
422 register_global_error(NULL);
423#ifndef HIDAPI_USE_DDK
424 if (!hidapi_initialized) {
425 if (lookup_functions() < 0) {
426 register_global_winapi_error(L"resolve DLL functions");
427 return -1;
428 }
429 hidapi_initialized = TRUE;
430 }
431#endif
432 return 0;
433}
434
435int HID_API_EXPORT hid_exit(void)
436{
437#ifndef HIDAPI_USE_DDK
438 free_library_handles();
439 hidapi_initialized = FALSE;
440#endif
441 register_global_error(NULL);
442 return 0;
443}
444
445static void* hid_internal_get_devnode_property(DEVINST dev_node, const DEVPROPKEY* property_key, DEVPROPTYPE expected_property_type)
446{
447 ULONG len = 0;
448 CONFIGRET cr;
449 DEVPROPTYPE property_type;
450 PBYTE property_value = NULL;
451
452 cr = CM_Get_DevNode_PropertyW(dev_node, property_key, &property_type, NULL, &len, 0);
453 if (cr != CR_BUFFER_SMALL || property_type != expected_property_type)
454 return NULL;
455
456 property_value = (PBYTE)calloc(len, sizeof(BYTE));
457 cr = CM_Get_DevNode_PropertyW(dev_node, property_key, &property_type, property_value, &len, 0);
458 if (cr != CR_SUCCESS) {
459 free(property_value);
460 return NULL;
461 }
462
463 return property_value;
464}
465
466static void* hid_internal_get_device_interface_property(const wchar_t* interface_path, const DEVPROPKEY* property_key, DEVPROPTYPE expected_property_type)
467{
468 ULONG len = 0;
469 CONFIGRET cr;
470 DEVPROPTYPE property_type;
471 PBYTE property_value = NULL;
472
473 cr = CM_Get_Device_Interface_PropertyW(interface_path, property_key, &property_type, NULL, &len, 0);
474 if (cr != CR_BUFFER_SMALL || property_type != expected_property_type)
475 return NULL;
476
477 property_value = (PBYTE)calloc(len, sizeof(BYTE));
478 cr = CM_Get_Device_Interface_PropertyW(interface_path, property_key, &property_type, property_value, &len, 0);
479 if (cr != CR_SUCCESS) {
480 free(property_value);
481 return NULL;
482 }
483
484 return property_value;
485}
486
487static void hid_internal_towupper(wchar_t* string)
488{
489 for (wchar_t* p = string; *p; ++p) *p = towupper(*p);
490}
491
492static int hid_internal_extract_int_token_value(wchar_t* string, const wchar_t* token)
493{
494 int token_value;
495 wchar_t* startptr, * endptr;
496
497 startptr = wcsstr(string, token);
498 if (!startptr)
499 return -1;
500
501 startptr += wcslen(token);
502 token_value = wcstol(startptr, &endptr, 16);
503 if (endptr == startptr)
504 return -1;
505
506 return token_value;
507}
508
509static void hid_internal_get_usb_info(struct hid_device_info* dev, DEVINST dev_node)
510{
511 wchar_t *device_id = NULL, *hardware_ids = NULL;
512
513 device_id = hid_internal_get_devnode_property(dev_node, &DEVPKEY_Device_InstanceId, DEVPROP_TYPE_STRING);
514 if (!device_id)
515 goto end;
516
517 /* Normalize to upper case */
518 hid_internal_towupper(device_id);
519
520 /* Check for Xbox Common Controller class (XUSB) device.
521 https://docs.microsoft.com/windows/win32/xinput/directinput-and-xusb-devices
522 https://docs.microsoft.com/windows/win32/xinput/xinput-and-directinput
523 */
524 if (hid_internal_extract_int_token_value(device_id, L"IG_") != -1) {
525 /* Get devnode parent to reach out USB device. */
526 if (CM_Get_Parent(&dev_node, dev_node, 0) != CR_SUCCESS)
527 goto end;
528 }
529
530 /* Get the hardware ids from devnode */
531 hardware_ids = hid_internal_get_devnode_property(dev_node, &DEVPKEY_Device_HardwareIds, DEVPROP_TYPE_STRING_LIST);
532 if (!hardware_ids)
533 goto end;
534
535 /* Get additional information from USB device's Hardware ID
536 https://docs.microsoft.com/windows-hardware/drivers/install/standard-usb-identifiers
537 https://docs.microsoft.com/windows-hardware/drivers/usbcon/enumeration-of-interfaces-not-grouped-in-collections
538 */
539 for (wchar_t* hardware_id = hardware_ids; *hardware_id; hardware_id += wcslen(hardware_id) + 1) {
540 /* Normalize to upper case */
541 hid_internal_towupper(hardware_id);
542
543 if (dev->release_number == 0) {
544 /* USB_DEVICE_DESCRIPTOR.bcdDevice value. */
545 int release_number = hid_internal_extract_int_token_value(hardware_id, L"REV_");
546 if (release_number != -1) {
547 dev->release_number = (unsigned short)release_number;
548 }
549 }
550
551 if (dev->interface_number == -1) {
552 /* USB_INTERFACE_DESCRIPTOR.bInterfaceNumber value. */
553 int interface_number = hid_internal_extract_int_token_value(hardware_id, L"MI_");
554 if (interface_number != -1) {
555 dev->interface_number = interface_number;
556 }
557 }
558 }
559
560 /* Try to get USB device manufacturer string if not provided by HidD_GetManufacturerString. */
561 if (wcslen(dev->manufacturer_string) == 0) {
562 wchar_t* manufacturer_string = hid_internal_get_devnode_property(dev_node, &DEVPKEY_Device_Manufacturer, DEVPROP_TYPE_STRING);
563 if (manufacturer_string) {
564 free(dev->manufacturer_string);
565 dev->manufacturer_string = manufacturer_string;
566 }
567 }
568
569 /* Try to get USB device serial number if not provided by HidD_GetSerialNumberString. */
570 if (wcslen(dev->serial_number) == 0) {
571 DEVINST usb_dev_node = dev_node;
572 if (dev->interface_number != -1) {
573 /* Get devnode parent to reach out composite parent USB device.
574 https://docs.microsoft.com/windows-hardware/drivers/usbcon/enumeration-of-the-composite-parent-device
575 */
576 if (CM_Get_Parent(&usb_dev_node, dev_node, 0) != CR_SUCCESS)
577 goto end;
578 }
579
580 /* Get the device id of the USB device. */
581 free(device_id);
582 device_id = hid_internal_get_devnode_property(usb_dev_node, &DEVPKEY_Device_InstanceId, DEVPROP_TYPE_STRING);
583 if (!device_id)
584 goto end;
585
586 /* Extract substring after last '\\' of Instance ID.
587 For USB devices it may contain device's serial number.
588 https://docs.microsoft.com/windows-hardware/drivers/install/instance-ids
589 */
590 for (wchar_t *ptr = device_id + wcslen(device_id); ptr > device_id; --ptr) {
591 /* Instance ID is unique only within the scope of the bus.
592 For USB devices it means that serial number is not available. Skip. */
593 if (*ptr == L'&')
594 break;
595
596 if (*ptr == L'\\') {
597 free(dev->serial_number);
598 dev->serial_number = _wcsdup(ptr + 1);
599 break;
600 }
601 }
602 }
603
604 /* If we can't get the interface number, it means that there is only one interface. */
605 if (dev->interface_number == -1)
606 dev->interface_number = 0;
607
608end:
609 free(device_id);
610 free(hardware_ids);
611}
612
613/* HidD_GetProductString/HidD_GetManufacturerString/HidD_GetSerialNumberString is not working for BLE HID devices
614 Request this info via dev node properties instead.
615 https://docs.microsoft.com/answers/questions/401236/hidd-getproductstring-with-ble-hid-device.html
616*/
617static void hid_internal_get_ble_info(struct hid_device_info* dev, DEVINST dev_node)
618{
619 if (wcslen(dev->manufacturer_string) == 0) {
620 /* Manufacturer Name String (UUID: 0x2A29) */
621 wchar_t* manufacturer_string = hid_internal_get_devnode_property(dev_node, (const DEVPROPKEY*)&PKEY_DeviceInterface_Bluetooth_Manufacturer, DEVPROP_TYPE_STRING);
622 if (manufacturer_string) {
623 free(dev->manufacturer_string);
624 dev->manufacturer_string = manufacturer_string;
625 }
626 }
627
628 if (wcslen(dev->serial_number) == 0) {
629 /* Serial Number String (UUID: 0x2A25) */
630 wchar_t* serial_number = hid_internal_get_devnode_property(dev_node, (const DEVPROPKEY*)&PKEY_DeviceInterface_Bluetooth_DeviceAddress, DEVPROP_TYPE_STRING);
631 if (serial_number) {
632 free(dev->serial_number);
633 dev->serial_number = serial_number;
634 }
635 }
636
637 if (wcslen(dev->product_string) == 0) {
638 /* Model Number String (UUID: 0x2A24) */
639 wchar_t* product_string = hid_internal_get_devnode_property(dev_node, (const DEVPROPKEY*)&PKEY_DeviceInterface_Bluetooth_ModelNumber, DEVPROP_TYPE_STRING);
640 if (!product_string) {
641 DEVINST parent_dev_node = 0;
642 /* Fallback: Get devnode grandparent to reach out Bluetooth LE device node */
643 if (CM_Get_Parent(&parent_dev_node, dev_node, 0) == CR_SUCCESS) {
644 /* Device Name (UUID: 0x2A00) */
645 product_string = hid_internal_get_devnode_property(parent_dev_node, &DEVPKEY_NAME, DEVPROP_TYPE_STRING);
646 }
647 }
648
649 if (product_string) {
650 free(dev->product_string);
651 dev->product_string = product_string;
652 }
653 }
654}
655
656#ifdef HIDAPI_IGNORE_DEVICE
657static hid_bus_type get_bus_type(const wchar_t* interface_path)
658{
659 wchar_t *device_id = NULL, *compatible_ids = NULL;
660 CONFIGRET cr;
661 DEVINST dev_node;
662 hid_bus_type bus_type = HID_API_BUS_UNKNOWN;
663
664 /* Get the device id from interface path */
665 device_id = hid_internal_get_device_interface_property(interface_path, &DEVPKEY_Device_InstanceId, DEVPROP_TYPE_STRING);
666 if (!device_id)
667 goto end;
668
669 /* Open devnode from device id */
670 cr = CM_Locate_DevNodeW(&dev_node, (DEVINSTID_W)device_id, CM_LOCATE_DEVNODE_NORMAL);
671 if (cr != CR_SUCCESS)
672 goto end;
673
674 /* Get devnode parent */
675 cr = CM_Get_Parent(&dev_node, dev_node, 0);
676 if (cr != CR_SUCCESS)
677 goto end;
678
679 /* Get the compatible ids from parent devnode */
680 compatible_ids = hid_internal_get_devnode_property(dev_node, &DEVPKEY_Device_CompatibleIds, DEVPROP_TYPE_STRING_LIST);
681 if (!compatible_ids)
682 goto end;
683
684 /* Now we can parse parent's compatible IDs to find out the device bus type */
685 for (wchar_t* compatible_id = compatible_ids; *compatible_id; compatible_id += wcslen(compatible_id) + 1) {
686 /* Normalize to upper case */
687 hid_internal_towupper(compatible_id);
688
689 /* USB devices
690 https://docs.microsoft.com/windows-hardware/drivers/hid/plug-and-play-support
691 https://docs.microsoft.com/windows-hardware/drivers/install/standard-usb-identifiers */
692 if (wcsstr(compatible_id, L"USB") != NULL) {
693 bus_type = HID_API_BUS_USB;
694 break;
695 }
696
697 /* Bluetooth devices
698 https://docs.microsoft.com/windows-hardware/drivers/bluetooth/installing-a-bluetooth-device */
699 if (wcsstr(compatible_id, L"BTHENUM") != NULL) {
700 bus_type = HID_API_BUS_BLUETOOTH;
701 break;
702 }
703
704 /* Bluetooth LE devices */
705 if (wcsstr(compatible_id, L"BTHLEDEVICE") != NULL) {
706 bus_type = HID_API_BUS_BLUETOOTH;
707 break;
708 }
709
710 /* I2C devices
711 https://docs.microsoft.com/windows-hardware/drivers/hid/plug-and-play-support-and-power-management */
712 if (wcsstr(compatible_id, L"PNP0C50") != NULL) {
713 bus_type = HID_API_BUS_I2C;
714 break;
715 }
716
717 /* SPI devices
718 https://docs.microsoft.com/windows-hardware/drivers/hid/plug-and-play-for-spi */
719 if (wcsstr(compatible_id, L"PNP0C51") != NULL) {
720 bus_type = HID_API_BUS_SPI;
721 break;
722 }
723 }
724end:
725 free(device_id);
726 free(compatible_ids);
727 return bus_type;
728}
729#endif /* HIDAPI_IGNORE_DEVICE */
730
731/* Unfortunately, HID_API_BUS_xxx constants alone aren't enough to distinguish between BLUETOOTH and BLE */
732
733#define HID_API_BUS_FLAG_BLE 0x01
734
735typedef struct hid_internal_detect_bus_type_result_ {
736 DEVINST dev_node;
737 hid_bus_type bus_type;
738 unsigned int bus_flags;
739} hid_internal_detect_bus_type_result;
740
741static hid_internal_detect_bus_type_result hid_internal_detect_bus_type(const wchar_t* interface_path)
742{
743 wchar_t *device_id = NULL, *compatible_ids = NULL;
744 CONFIGRET cr;
745 DEVINST dev_node;
746 hid_internal_detect_bus_type_result result = { 0 };
747
748 /* Get the device id from interface path */
749 device_id = hid_internal_get_device_interface_property(interface_path, &DEVPKEY_Device_InstanceId, DEVPROP_TYPE_STRING);
750 if (!device_id)
751 goto end;
752
753 /* Open devnode from device id */
754 cr = CM_Locate_DevNodeW(&dev_node, (DEVINSTID_W)device_id, CM_LOCATE_DEVNODE_NORMAL);
755 if (cr != CR_SUCCESS)
756 goto end;
757
758 /* Get devnode parent */
759 cr = CM_Get_Parent(&dev_node, dev_node, 0);
760 if (cr != CR_SUCCESS)
761 goto end;
762
763 /* Get the compatible ids from parent devnode */
764 compatible_ids = hid_internal_get_devnode_property(dev_node, &DEVPKEY_Device_CompatibleIds, DEVPROP_TYPE_STRING_LIST);
765 if (!compatible_ids)
766 goto end;
767
768 /* Now we can parse parent's compatible IDs to find out the device bus type */
769 for (wchar_t* compatible_id = compatible_ids; *compatible_id; compatible_id += wcslen(compatible_id) + 1) {
770 /* Normalize to upper case */
771 hid_internal_towupper(compatible_id);
772
773 /* USB devices
774 https://docs.microsoft.com/windows-hardware/drivers/hid/plug-and-play-support
775 https://docs.microsoft.com/windows-hardware/drivers/install/standard-usb-identifiers */
776 if (wcsstr(compatible_id, L"USB") != NULL) {
777 result.bus_type = HID_API_BUS_USB;
778 break;
779 }
780
781 /* Bluetooth devices
782 https://docs.microsoft.com/windows-hardware/drivers/bluetooth/installing-a-bluetooth-device */
783 if (wcsstr(compatible_id, L"BTHENUM") != NULL) {
784 result.bus_type = HID_API_BUS_BLUETOOTH;
785 break;
786 }
787
788 /* Bluetooth LE devices */
789 if (wcsstr(compatible_id, L"BTHLEDEVICE") != NULL) {
790 result.bus_type = HID_API_BUS_BLUETOOTH;
791 result.bus_flags |= HID_API_BUS_FLAG_BLE;
792 break;
793 }
794
795 /* I2C devices
796 https://docs.microsoft.com/windows-hardware/drivers/hid/plug-and-play-support-and-power-management */
797 if (wcsstr(compatible_id, L"PNP0C50") != NULL) {
798 result.bus_type = HID_API_BUS_I2C;
799 break;
800 }
801
802 /* SPI devices
803 https://docs.microsoft.com/windows-hardware/drivers/hid/plug-and-play-for-spi */
804 if (wcsstr(compatible_id, L"PNP0C51") != NULL) {
805 result.bus_type = HID_API_BUS_SPI;
806 break;
807 }
808 }
809
810 result.dev_node = dev_node;
811
812end:
813 free(device_id);
814 free(compatible_ids);
815 return result;
816}
817
818static char *hid_internal_UTF16toUTF8(const wchar_t *src)
819{
820 char *dst = NULL;
821#ifdef HIDAPI_USING_SDL_RUNTIME
822 int len = WIN_WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, src, -1, NULL, 0, NULL, NULL);
823#else
824 int len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, src, -1, NULL, 0, NULL, NULL);
825#endif
826 if (len) {
827 dst = (char*)calloc(len, sizeof(char));
828 if (dst == NULL) {
829 return NULL;
830 }
831#ifdef HIDAPI_USING_SDL_RUNTIME
832 WIN_WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, src, -1, dst, len, NULL, NULL);
833#else
834 WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, src, -1, dst, len, NULL, NULL);
835#endif
836 }
837
838 return dst;
839}
840
841static wchar_t *hid_internal_UTF8toUTF16(const char *src)
842{
843 wchar_t *dst = NULL;
844 int len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, src, -1, NULL, 0);
845 if (len) {
846 dst = (wchar_t*)calloc(len, sizeof(wchar_t));
847 if (dst == NULL) {
848 return NULL;
849 }
850 MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, src, -1, dst, len);
851 }
852
853 return dst;
854}
855
856static struct hid_device_info *hid_internal_get_device_info(const wchar_t *path, HANDLE handle)
857{
858 struct hid_device_info *dev = NULL; /* return object */
859 HIDD_ATTRIBUTES attrib;
860 PHIDP_PREPARSED_DATA pp_data = NULL;
861 HIDP_CAPS caps;
862 wchar_t string[MAX_STRING_WCHARS + 1];
863 ULONG len;
864 ULONG size;
865 hid_internal_detect_bus_type_result detect_bus_type_result;
866
867 /* Create the record. */
868 dev = (struct hid_device_info*)calloc(1, sizeof(struct hid_device_info));
869
870 if (dev == NULL) {
871 return NULL;
872 }
873
874 /* Fill out the record */
875 dev->next = NULL;
876 dev->path = hid_internal_UTF16toUTF8(path);
877 dev->interface_number = -1;
878
879 attrib.Size = sizeof(HIDD_ATTRIBUTES);
880 if (HidD_GetAttributes(handle, &attrib)) {
881 /* VID/PID */
882 dev->vendor_id = attrib.VendorID;
883 dev->product_id = attrib.ProductID;
884
885 /* Release Number */
886 dev->release_number = attrib.VersionNumber;
887 }
888
889 /* Get the Usage Page and Usage for this device. */
890 if (HidD_GetPreparsedData(handle, &pp_data)) {
891 if (HidP_GetCaps(pp_data, &caps) == HIDP_STATUS_SUCCESS) {
892 dev->usage_page = caps.UsagePage;
893 dev->usage = caps.Usage;
894 }
895
896 HidD_FreePreparsedData(pp_data);
897 }
898
899 /* detect bus type before reading string descriptors */
900 detect_bus_type_result = hid_internal_detect_bus_type(path);
901 dev->bus_type = detect_bus_type_result.bus_type;
902
903 len = dev->bus_type == HID_API_BUS_USB ? MAX_STRING_WCHARS_USB : MAX_STRING_WCHARS;
904 string[len] = L'\0';
905 size = len * sizeof(wchar_t);
906
907 /* Serial Number */
908 string[0] = L'\0';
909 HidD_GetSerialNumberString(handle, string, size);
910 dev->serial_number = _wcsdup(string);
911
912 /* Manufacturer String */
913 string[0] = L'\0';
914 HidD_GetManufacturerString(handle, string, size);
915 dev->manufacturer_string = _wcsdup(string);
916
917 /* Product String */
918 string[0] = L'\0';
919 HidD_GetProductString(handle, string, size);
920 dev->product_string = _wcsdup(string);
921
922 /* now, the portion that depends on string descriptors */
923 switch (dev->bus_type) {
924 case HID_API_BUS_USB:
925 hid_internal_get_usb_info(dev, detect_bus_type_result.dev_node);
926 break;
927
928 case HID_API_BUS_BLUETOOTH:
929 if (detect_bus_type_result.bus_flags & HID_API_BUS_FLAG_BLE)
930 hid_internal_get_ble_info(dev, detect_bus_type_result.dev_node);
931 break;
932
933 case HID_API_BUS_UNKNOWN:
934 case HID_API_BUS_SPI:
935 case HID_API_BUS_I2C:
936 /* shut down -Wswitch */
937 break;
938 }
939
940 return dev;
941}
942
943static int hid_blacklist(unsigned short vendor_id, unsigned short product_id)
944{
945 size_t i;
946 static const struct { unsigned short vid; unsigned short pid; } known_bad[] = {
947 { 0x045E, 0x0822 }, /* Microsoft Precision Mouse - causes deadlock asking for device details */
948 { 0x0738, 0x2217 }, /* SPEEDLINK COMPETITION PRO - turns into an Android controller when enumerated */
949 { 0x0D8C, 0x0014 }, /* Sharkoon Skiller SGH2 headset - causes deadlock asking for device details */
950 { 0x1532, 0x0109 }, /* Razer Lycosa Gaming keyboard - causes deadlock asking for device details */
951 { 0x1532, 0x010B }, /* Razer Arctosa Gaming keyboard - causes deadlock asking for device details */
952 { 0x1B1C, 0x1B3D }, /* Corsair Gaming keyboard - causes deadlock asking for device details */
953 { 0x1CCF, 0x0000 } /* All Konami Amusement Devices - causes deadlock asking for device details */
954 };
955
956 for (i = 0; i < (sizeof(known_bad)/sizeof(known_bad[0])); i++) {
957 if ((vendor_id == known_bad[i].vid) && (product_id == known_bad[i].pid || known_bad[i].pid == 0x0000)) {
958 return 1;
959 }
960 }
961
962 return 0;
963}
964
965struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id)
966{
967 struct hid_device_info *root = NULL; /* return object */
968 struct hid_device_info *cur_dev = NULL;
969 GUID interface_class_guid;
970 CONFIGRET cr;
971 wchar_t* device_interface_list = NULL;
972 DWORD len;
973
974 if (hid_init() < 0) {
975 /* register_global_error: global error is reset by hid_init */
976 return NULL;
977 }
978
979 /* Retrieve HID Interface Class GUID
980 https://docs.microsoft.com/windows-hardware/drivers/install/guid-devinterface-hid */
981 HidD_GetHidGuid(&interface_class_guid);
982
983 /* Get the list of all device interfaces belonging to the HID class. */
984 /* Retry in case of list was changed between calls to
985 CM_Get_Device_Interface_List_SizeW and CM_Get_Device_Interface_ListW */
986 do {
987 cr = CM_Get_Device_Interface_List_SizeW(&len, &interface_class_guid, NULL, CM_GET_DEVICE_INTERFACE_LIST_PRESENT);
988 if (cr != CR_SUCCESS) {
989 register_global_error(L"Failed to get size of HID device interface list");
990 break;
991 }
992
993 if (device_interface_list != NULL) {
994 free(device_interface_list);
995 }
996
997 device_interface_list = (wchar_t*)calloc(len, sizeof(wchar_t));
998 if (device_interface_list == NULL) {
999 register_global_error(L"Failed to allocate memory for HID device interface list");
1000 return NULL;
1001 }
1002 cr = CM_Get_Device_Interface_ListW(&interface_class_guid, NULL, device_interface_list, len, CM_GET_DEVICE_INTERFACE_LIST_PRESENT);
1003 if (cr != CR_SUCCESS && cr != CR_BUFFER_SMALL) {
1004 register_global_error(L"Failed to get HID device interface list");
1005 }
1006 } while (cr == CR_BUFFER_SMALL);
1007
1008 if (cr != CR_SUCCESS) {
1009 goto end_of_function;
1010 }
1011
1012 /* Iterate over each device interface in the HID class, looking for the right one. */
1013 for (wchar_t* device_interface = device_interface_list; *device_interface; device_interface += wcslen(device_interface) + 1) {
1014 HANDLE device_handle = INVALID_HANDLE_VALUE;
1015 HIDD_ATTRIBUTES attrib;
1016
1017 /* XInput devices don't get real HID reports and are better handled by the raw input driver */
1018 if (wcsstr(device_interface, L"&IG_") != NULL) {
1019 continue;
1020 }
1021
1022 /* Open read-only handle to the device */
1023 device_handle = open_device(device_interface, FALSE);
1024
1025 /* Check validity of device_handle. */
1026 if (device_handle == INVALID_HANDLE_VALUE) {
1027 /* Unable to open the device. */
1028 continue;
1029 }
1030
1031 /* Get the Vendor ID and Product ID for this device. */
1032 attrib.Size = sizeof(HIDD_ATTRIBUTES);
1033 if (!HidD_GetAttributes(device_handle, &attrib)) {
1034 goto cont_close;
1035 }
1036
1037#ifdef HIDAPI_IGNORE_DEVICE
1038 /* See if there are any devices we should skip in enumeration */
1039 hid_bus_type bus_type = get_bus_type(device_interface);
1040 PHIDP_PREPARSED_DATA pp_data = NULL;
1041 HIDP_CAPS caps = { 0 };
1042 if (HidD_GetPreparsedData(device_handle, &pp_data)) {
1043 HidP_GetCaps(pp_data, &caps);
1044 HidD_FreePreparsedData(pp_data);
1045 }
1046 if (HIDAPI_IGNORE_DEVICE(bus_type, attrib.VendorID, attrib.ProductID, caps.UsagePage, caps.Usage)) {
1047 goto cont_close;
1048 }
1049#endif
1050
1051 /* Check the VID/PID to see if we should add this
1052 device to the enumeration list. */
1053 if ((vendor_id == 0x0 || attrib.VendorID == vendor_id) &&
1054 (product_id == 0x0 || attrib.ProductID == product_id) &&
1055 !hid_blacklist(attrib.VendorID, attrib.ProductID)) {
1056
1057 /* VID/PID match. Create the record. */
1058 struct hid_device_info *tmp = hid_internal_get_device_info(device_interface, device_handle);
1059
1060 if (tmp == NULL) {
1061 goto cont_close;
1062 }
1063
1064 if (cur_dev) {
1065 cur_dev->next = tmp;
1066 }
1067 else {
1068 root = tmp;
1069 }
1070 cur_dev = tmp;
1071 }
1072
1073cont_close:
1074 CloseHandle(device_handle);
1075 }
1076
1077 if (root == NULL) {
1078 if (vendor_id == 0 && product_id == 0) {
1079 register_global_error(L"No HID devices found in the system.");
1080 } else {
1081 register_global_error(L"No HID devices with requested VID/PID found in the system.");
1082 }
1083 }
1084
1085end_of_function:
1086 free(device_interface_list);
1087
1088 return root;
1089}
1090
1091void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs)
1092{
1093 /* TODO: Merge this with the Linux version. This function is platform-independent. */
1094 struct hid_device_info *d = devs;
1095 while (d) {
1096 struct hid_device_info *next = d->next;
1097 free(d->path);
1098 free(d->serial_number);
1099 free(d->manufacturer_string);
1100 free(d->product_string);
1101 free(d);
1102 d = next;
1103 }
1104}
1105
1106HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number)
1107{
1108 /* TODO: Merge this functions with the Linux version. This function should be platform independent. */
1109 struct hid_device_info *devs, *cur_dev;
1110 const char *path_to_open = NULL;
1111 hid_device *handle = NULL;
1112
1113 /* register_global_error: global error is reset by hid_enumerate/hid_init */
1114 devs = hid_enumerate(vendor_id, product_id);
1115 if (!devs) {
1116 /* register_global_error: global error is already set by hid_enumerate */
1117 return NULL;
1118 }
1119
1120 cur_dev = devs;
1121 while (cur_dev) {
1122 if (cur_dev->vendor_id == vendor_id &&
1123 cur_dev->product_id == product_id) {
1124 if (serial_number) {
1125 if (cur_dev->serial_number && wcscmp(serial_number, cur_dev->serial_number) == 0) {
1126 path_to_open = cur_dev->path;
1127 break;
1128 }
1129 }
1130 else {
1131 path_to_open = cur_dev->path;
1132 break;
1133 }
1134 }
1135 cur_dev = cur_dev->next;
1136 }
1137
1138 if (path_to_open) {
1139 /* Open the device */
1140 handle = hid_open_path(path_to_open);
1141 } else {
1142 register_global_error(L"Device with requested VID/PID/(SerialNumber) not found");
1143 }
1144
1145 hid_free_enumeration(devs);
1146
1147 return handle;
1148}
1149
1150HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path)
1151{
1152 hid_device *dev = NULL;
1153 wchar_t* interface_path = NULL;
1154 HANDLE device_handle = INVALID_HANDLE_VALUE;
1155 PHIDP_PREPARSED_DATA pp_data = NULL;
1156 HIDP_CAPS caps;
1157
1158 if (hid_init() < 0) {
1159 /* register_global_error: global error is reset by hid_init */
1160 goto end_of_function;
1161 }
1162
1163 interface_path = hid_internal_UTF8toUTF16(path);
1164 if (!interface_path) {
1165 register_global_error(L"Path conversion failure");
1166 goto end_of_function;
1167 }
1168
1169 /* Open a handle to the device */
1170 device_handle = open_device(interface_path, TRUE);
1171
1172 /* Check validity of write_handle. */
1173 if (device_handle == INVALID_HANDLE_VALUE) {
1174 /* System devices, such as keyboards and mice, cannot be opened in
1175 read-write mode, because the system takes exclusive control over
1176 them. This is to prevent keyloggers. However, feature reports
1177 can still be sent and received. Retry opening the device, but
1178 without read/write access. */
1179 device_handle = open_device(interface_path, FALSE);
1180
1181 /* Check the validity of the limited device_handle. */
1182 if (device_handle == INVALID_HANDLE_VALUE) {
1183 register_global_winapi_error(L"open_device");
1184 goto end_of_function;
1185 }
1186 }
1187
1188 /* Set the Input Report buffer size to 64 reports. */
1189 if (!HidD_SetNumInputBuffers(device_handle, 64)) {
1190 register_global_winapi_error(L"set input buffers");
1191 goto end_of_function;
1192 }
1193
1194 /* Get the Input Report length for the device. */
1195 if (!HidD_GetPreparsedData(device_handle, &pp_data)) {
1196 register_global_winapi_error(L"get preparsed data");
1197 goto end_of_function;
1198 }
1199
1200 if (HidP_GetCaps(pp_data, &caps) != HIDP_STATUS_SUCCESS) {
1201 register_global_error(L"HidP_GetCaps");
1202 goto end_of_function;
1203 }
1204
1205 dev = new_hid_device();
1206
1207 if (dev == NULL) {
1208 register_global_error(L"hid_device allocation error");
1209 goto end_of_function;
1210 }
1211
1212 dev->device_handle = device_handle;
1213 device_handle = INVALID_HANDLE_VALUE;
1214
1215 dev->output_report_length = caps.OutputReportByteLength;
1216 dev->input_report_length = caps.InputReportByteLength;
1217 dev->feature_report_length = caps.FeatureReportByteLength;
1218 dev->read_buf = (char*) malloc(dev->input_report_length);
1219 dev->device_info = hid_internal_get_device_info(interface_path, dev->device_handle);
1220
1221 /* On Windows 7, we need to use hid_write_output_report() over Bluetooth */
1222 if (dev->output_report_length > 512) {
1223 dev->use_hid_write_output_report = !IsWindowsVersionOrGreater( HIBYTE( _WIN32_WINNT_WIN8 ), LOBYTE( _WIN32_WINNT_WIN8 ), 0 );
1224 }
1225
1226end_of_function:
1227 free(interface_path);
1228 CloseHandle(device_handle);
1229
1230 if (pp_data) {
1231 HidD_FreePreparsedData(pp_data);
1232 }
1233
1234 return dev;
1235}
1236
1237static int hid_write_output_report(hid_device *dev, const unsigned char *data, size_t length)
1238{
1239 BOOL res;
1240 res = HidD_SetOutputReport(dev->device_handle, (void *)data, (ULONG)length);
1241 if (res)
1242 return (int)length;
1243 else
1244 return -1;
1245}
1246
1247int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char *data, size_t length)
1248{
1249 DWORD bytes_written = 0;
1250 int function_result = -1;
1251 BOOL res;
1252 BOOL overlapped = FALSE;
1253
1254 unsigned char *buf;
1255
1256 if (!data || !length) {
1257 register_string_error(dev, L"Zero buffer/length");
1258 return function_result;
1259 }
1260
1261 register_string_error(dev, NULL);
1262
1263 if (dev->use_hid_write_output_report) {
1264 return hid_write_output_report(dev, data, length);
1265 }
1266
1267 /* Make sure the right number of bytes are passed to WriteFile. Windows
1268 expects the number of bytes which are in the _longest_ report (plus
1269 one for the report number) bytes even if the data is a report
1270 which is shorter than that. Windows gives us this value in
1271 caps.OutputReportByteLength. If a user passes in fewer bytes than this,
1272 use cached temporary buffer which is the proper size. */
1273 if (length >= dev->output_report_length) {
1274 /* The user passed the right number of bytes. Use the buffer as-is. */
1275 buf = (unsigned char *) data;
1276 } else {
1277 if (dev->write_buf == NULL)
1278 dev->write_buf = (unsigned char *) malloc(dev->output_report_length);
1279 buf = dev->write_buf;
1280 memcpy(buf, data, length);
1281 memset(buf + length, 0, dev->output_report_length - length);
1282 length = dev->output_report_length;
1283 }
1284
1285 res = WriteFile(dev->device_handle, buf, (DWORD) length, &bytes_written, &dev->write_ol);
1286
1287 if (!res) {
1288 if (GetLastError() != ERROR_IO_PENDING) {
1289 /* WriteFile() failed. Return error. */
1290 register_winapi_error(dev, L"WriteFile");
1291 goto end_of_function;
1292 }
1293 overlapped = TRUE;
1294 } else {
1295 /* WriteFile() succeeded synchronously. */
1296 function_result = bytes_written;
1297 }
1298
1299 if (overlapped) {
1300 /* Wait for the transaction to complete. This makes
1301 hid_write() synchronous. */
1302 res = WaitForSingleObject(dev->write_ol.hEvent, 1000);
1303 if (res != WAIT_OBJECT_0) {
1304 /* There was a Timeout. */
1305 register_winapi_error(dev, L"hid_write/WaitForSingleObject");
1306 goto end_of_function;
1307 }
1308
1309 /* Get the result. */
1310 res = GetOverlappedResult(dev->device_handle, &dev->write_ol, &bytes_written, FALSE/*wait*/);
1311 if (res) {
1312 function_result = bytes_written;
1313 }
1314 else {
1315 /* The Write operation failed. */
1316 register_winapi_error(dev, L"hid_write/GetOverlappedResult");
1317 goto end_of_function;
1318 }
1319 }
1320
1321end_of_function:
1322 return function_result;
1323}
1324
1325
1326int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds)
1327{
1328 DWORD bytes_read = 0;
1329 size_t copy_len = 0;
1330 BOOL res = FALSE;
1331 BOOL overlapped = FALSE;
1332
1333 if (!data || !length) {
1334 register_string_error(dev, L"Zero buffer/length");
1335 return -1;
1336 }
1337
1338 register_string_error(dev, NULL);
1339
1340 /* Copy the handle for convenience. */
1341 HANDLE ev = dev->ol.hEvent;
1342
1343 if (!dev->read_pending) {
1344 /* Start an Overlapped I/O read. */
1345 dev->read_pending = TRUE;
1346 memset(dev->read_buf, 0, dev->input_report_length);
1347 ResetEvent(ev);
1348 res = ReadFile(dev->device_handle, dev->read_buf, (DWORD) dev->input_report_length, &bytes_read, &dev->ol);
1349
1350 if (!res) {
1351 if (GetLastError() != ERROR_IO_PENDING) {
1352 /* ReadFile() has failed.
1353 Clean up and return error. */
1354 register_winapi_error(dev, L"ReadFile");
1355 CancelIo(dev->device_handle);
1356 dev->read_pending = FALSE;
1357 goto end_of_function;
1358 }
1359 overlapped = TRUE;
1360 }
1361 }
1362 else {
1363 overlapped = TRUE;
1364 }
1365
1366 if (overlapped) {
1367 /* See if there is any data yet. */
1368 res = WaitForSingleObject(ev, milliseconds >= 0 ? (DWORD)milliseconds : INFINITE);
1369 if (res != WAIT_OBJECT_0) {
1370 /* There was no data this time. Return zero bytes available,
1371 but leave the Overlapped I/O running. */
1372 return 0;
1373 }
1374
1375 /* Get the number of bytes read. The actual data has been copied to the data[]
1376 array which was passed to ReadFile(). We must not wait here because we've
1377 already waited on our event above, and since it's auto-reset, it will have
1378 been reset back to unsignalled by now. */
1379 res = GetOverlappedResult(dev->device_handle, &dev->ol, &bytes_read, FALSE/*don't wait now - already did on the prev step*/);
1380 }
1381 /* Set pending back to false, even if GetOverlappedResult() returned error. */
1382 dev->read_pending = FALSE;
1383
1384 if (res && bytes_read > 0) {
1385 if (dev->read_buf[0] == 0x0) {
1386 /* If report numbers aren't being used, but Windows sticks a report
1387 number (0x0) on the beginning of the report anyway. To make this
1388 work like the other platforms, and to make it work more like the
1389 HID spec, we'll skip over this byte. */
1390 bytes_read--;
1391 copy_len = length > bytes_read ? bytes_read : length;
1392 memcpy(data, dev->read_buf+1, copy_len);
1393 }
1394 else {
1395 /* Copy the whole buffer, report number and all. */
1396 copy_len = length > bytes_read ? bytes_read : length;
1397 memcpy(data, dev->read_buf, copy_len);
1398 }
1399 }
1400 if (!res) {
1401 register_winapi_error(dev, L"hid_read_timeout/GetOverlappedResult");
1402 }
1403
1404end_of_function:
1405 if (!res) {
1406 return -1;
1407 }
1408
1409 return (int) copy_len;
1410}
1411
1412int HID_API_EXPORT HID_API_CALL hid_read(hid_device *dev, unsigned char *data, size_t length)
1413{
1414 return hid_read_timeout(dev, data, length, (dev->blocking)? -1: 0);
1415}
1416
1417int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *dev, int nonblock)
1418{
1419 dev->blocking = !nonblock;
1420 return 0; /* Success */
1421}
1422
1423int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length)
1424{
1425 BOOL res = FALSE;
1426 unsigned char *buf;
1427 size_t length_to_send;
1428
1429 if (!data || !length) {
1430 register_string_error(dev, L"Zero buffer/length");
1431 return -1;
1432 }
1433
1434 register_string_error(dev, NULL);
1435
1436 /* Windows expects at least caps.FeatureReportByteLength bytes passed
1437 to HidD_SetFeature(), even if the report is shorter. Any less sent and
1438 the function fails with error ERROR_INVALID_PARAMETER set. Any more
1439 and HidD_SetFeature() silently truncates the data sent in the report
1440 to caps.FeatureReportByteLength. */
1441 if (length >= dev->feature_report_length) {
1442 buf = (unsigned char *) data;
1443 length_to_send = length;
1444 } else {
1445 if (dev->feature_buf == NULL)
1446 dev->feature_buf = (unsigned char *) malloc(dev->feature_report_length);
1447 buf = dev->feature_buf;
1448 memcpy(buf, data, length);
1449 memset(buf + length, 0, dev->feature_report_length - length);
1450 length_to_send = dev->feature_report_length;
1451 }
1452
1453 res = HidD_SetFeature(dev->device_handle, (PVOID)buf, (DWORD) length_to_send);
1454
1455 if (!res) {
1456 register_winapi_error(dev, L"HidD_SetFeature");
1457 return -1;
1458 }
1459
1460 return (int) length;
1461}
1462
1463static int hid_get_report(hid_device *dev, DWORD report_type, unsigned char *data, size_t length)
1464{
1465 BOOL res;
1466 DWORD bytes_returned = 0;
1467
1468 OVERLAPPED ol;
1469 memset(&ol, 0, sizeof(ol));
1470
1471 if (!data || !length) {
1472 register_string_error(dev, L"Zero buffer/length");
1473 return -1;
1474 }
1475
1476 register_string_error(dev, NULL);
1477
1478 res = DeviceIoControl(dev->device_handle,
1479 report_type,
1480 data, (DWORD) length,
1481 data, (DWORD) length,
1482 &bytes_returned, &ol);
1483
1484 if (!res) {
1485 if (GetLastError() != ERROR_IO_PENDING) {
1486 /* DeviceIoControl() failed. Return error. */
1487 register_winapi_error(dev, L"Get Input/Feature Report DeviceIoControl");
1488 return -1;
1489 }
1490 }
1491
1492 /* Wait here until the write is done. This makes
1493 hid_get_feature_report() synchronous. */
1494 res = GetOverlappedResult(dev->device_handle, &ol, &bytes_returned, TRUE/*wait*/);
1495 if (!res) {
1496 /* The operation failed. */
1497 register_winapi_error(dev, L"Get Input/Feature Report GetOverLappedResult");
1498 return -1;
1499 }
1500
1501 /* When numbered reports aren't used,
1502 bytes_returned seem to include only what is actually received from the device
1503 (not including the first byte with 0, as an indication "no numbered reports"). */
1504 if (data[0] == 0x0) {
1505 bytes_returned++;
1506 }
1507
1508 return bytes_returned;
1509}
1510
1511int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length)
1512{
1513 /* We could use HidD_GetFeature() instead, but it doesn't give us an actual length, unfortunately */
1514 return hid_get_report(dev, IOCTL_HID_GET_FEATURE, data, length);
1515}
1516
1517int HID_API_EXPORT HID_API_CALL hid_get_input_report(hid_device *dev, unsigned char *data, size_t length)
1518{
1519 /* We could use HidD_GetInputReport() instead, but it doesn't give us an actual length, unfortunately */
1520 return hid_get_report(dev, IOCTL_HID_GET_INPUT_REPORT, data, length);
1521}
1522
1523#if defined(__GNUC__) && __GNUC__ >= 8
1524# pragma GCC diagnostic push
1525# pragma GCC diagnostic ignored "-Wcast-function-type"
1526#endif
1527void HID_API_EXPORT HID_API_CALL hid_close(hid_device *dev)
1528{
1529 typedef BOOL (WINAPI *CancelIoEx_t)(HANDLE hFile, LPOVERLAPPED lpOverlapped);
1530 CancelIoEx_t CancelIoExFunc = (CancelIoEx_t)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "CancelIoEx");
1531
1532 if (!dev)
1533 return;
1534
1535 if (CancelIoExFunc) {
1536 CancelIoExFunc(dev->device_handle, NULL);
1537 } else {
1538 /* Windows XP, this will only cancel I/O on the current thread */
1539 CancelIo(dev->device_handle);
1540 }
1541 if (dev->read_pending) {
1542 DWORD bytes_read = 0;
1543 GetOverlappedResult(dev->device_handle, &dev->ol, &bytes_read, TRUE/*wait*/);
1544 }
1545 free_hid_device(dev);
1546}
1547#if defined(__GNUC__) && __GNUC__ >= 8
1548# pragma GCC diagnostic pop
1549#endif
1550
1551int HID_API_EXPORT_CALL HID_API_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen)
1552{
1553 if (!string || !maxlen) {
1554 register_string_error(dev, L"Zero buffer/length");
1555 return -1;
1556 }
1557
1558 if (!dev->device_info) {
1559 register_string_error(dev, L"NULL device info");
1560 return -1;
1561 }
1562
1563 wcsncpy(string, dev->device_info->manufacturer_string, maxlen);
1564 string[maxlen - 1] = L'\0';
1565
1566 register_string_error(dev, NULL);
1567
1568 return 0;
1569}
1570
1571int HID_API_EXPORT_CALL HID_API_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen)
1572{
1573 if (!string || !maxlen) {
1574 register_string_error(dev, L"Zero buffer/length");
1575 return -1;
1576 }
1577
1578 if (!dev->device_info) {
1579 register_string_error(dev, L"NULL device info");
1580 return -1;
1581 }
1582
1583 wcsncpy(string, dev->device_info->product_string, maxlen);
1584 string[maxlen - 1] = L'\0';
1585
1586 register_string_error(dev, NULL);
1587
1588 return 0;
1589}
1590
1591int HID_API_EXPORT_CALL HID_API_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen)
1592{
1593 if (!string || !maxlen) {
1594 register_string_error(dev, L"Zero buffer/length");
1595 return -1;
1596 }
1597
1598 if (!dev->device_info) {
1599 register_string_error(dev, L"NULL device info");
1600 return -1;
1601 }
1602
1603 wcsncpy(string, dev->device_info->serial_number, maxlen);
1604 string[maxlen - 1] = L'\0';
1605
1606 register_string_error(dev, NULL);
1607
1608 return 0;
1609}
1610
1611HID_API_EXPORT struct hid_device_info * HID_API_CALL hid_get_device_info(hid_device *dev) {
1612 if (!dev->device_info)
1613 {
1614 register_string_error(dev, L"NULL device info");
1615 return NULL;
1616 }
1617
1618 return dev->device_info;
1619}
1620
1621int HID_API_EXPORT_CALL HID_API_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen)
1622{
1623 BOOL res;
1624
1625 if (dev->device_info && dev->device_info->bus_type == HID_API_BUS_USB && maxlen > MAX_STRING_WCHARS_USB) {
1626 string[MAX_STRING_WCHARS_USB] = L'\0';
1627 maxlen = MAX_STRING_WCHARS_USB;
1628 }
1629
1630 res = HidD_GetIndexedString(dev->device_handle, string_index, string, (ULONG)maxlen * sizeof(wchar_t));
1631 if (!res) {
1632 register_winapi_error(dev, L"HidD_GetIndexedString");
1633 return -1;
1634 }
1635
1636 register_string_error(dev, NULL);
1637
1638 return 0;
1639}
1640
1641int HID_API_EXPORT_CALL hid_winapi_get_container_id(hid_device *dev, GUID *container_id)
1642{
1643 wchar_t *interface_path = NULL, *device_id = NULL;
1644 CONFIGRET cr = CR_FAILURE;
1645 DEVINST dev_node;
1646 DEVPROPTYPE property_type;
1647 ULONG len;
1648
1649 if (!container_id) {
1650 register_string_error(dev, L"Invalid Container ID");
1651 return -1;
1652 }
1653
1654 register_string_error(dev, NULL);
1655
1656 interface_path = hid_internal_UTF8toUTF16(dev->device_info->path);
1657 if (!interface_path) {
1658 register_string_error(dev, L"Path conversion failure");
1659 goto end;
1660 }
1661
1662 /* Get the device id from interface path */
1663 device_id = hid_internal_get_device_interface_property(interface_path, &DEVPKEY_Device_InstanceId, DEVPROP_TYPE_STRING);
1664 if (!device_id) {
1665 register_string_error(dev, L"Failed to get device interface property InstanceId");
1666 goto end;
1667 }
1668
1669 /* Open devnode from device id */
1670 cr = CM_Locate_DevNodeW(&dev_node, (DEVINSTID_W)device_id, CM_LOCATE_DEVNODE_NORMAL);
1671 if (cr != CR_SUCCESS) {
1672 register_string_error(dev, L"Failed to locate device node");
1673 goto end;
1674 }
1675
1676 /* Get the container id from devnode */
1677 len = sizeof(*container_id);
1678 cr = CM_Get_DevNode_PropertyW(dev_node, &DEVPKEY_Device_ContainerId, &property_type, (PBYTE)container_id, &len, 0);
1679 if (cr == CR_SUCCESS && property_type != DEVPROP_TYPE_GUID)
1680 cr = CR_FAILURE;
1681
1682 if (cr != CR_SUCCESS)
1683 register_string_error(dev, L"Failed to read ContainerId property from device node");
1684
1685end:
1686 free(interface_path);
1687 free(device_id);
1688
1689 return cr == CR_SUCCESS ? 0 : -1;
1690}
1691
1692
1693int HID_API_EXPORT_CALL hid_get_report_descriptor(hid_device *dev, unsigned char *buf, size_t buf_size)
1694{
1695 PHIDP_PREPARSED_DATA pp_data = NULL;
1696
1697 if (!HidD_GetPreparsedData(dev->device_handle, &pp_data) || pp_data == NULL) {
1698 register_string_error(dev, L"HidD_GetPreparsedData");
1699 return -1;
1700 }
1701
1702 int res = hid_winapi_descriptor_reconstruct_pp_data(pp_data, buf, buf_size);
1703
1704 HidD_FreePreparsedData(pp_data);
1705
1706 return res;
1707}
1708
1709HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev)
1710{
1711 if (dev) {
1712 if (dev->last_error_str == NULL)
1713 return L"Success";
1714 return (wchar_t*)dev->last_error_str;
1715 }
1716
1717 if (last_global_error_str == NULL)
1718 return L"Success";
1719 return last_global_error_str;
1720}
1721
1722#ifndef hidapi_winapi_EXPORTS
1723#include "hidapi_descriptor_reconstruct.c"
1724#endif
1725
1726#ifdef __cplusplus
1727} /* extern "C" */
1728#endif