Atlas - hid.c
Home / ext / SDL / src / hidapi / windows Lines: 2 | Size: 51203 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]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 { 0x1532, 0x0227 }, /* Razer Huntsman Gaming keyboard - long delay asking for device details */ 953 { 0x1B1C, 0x1B3D }, /* Corsair Gaming keyboard - causes deadlock asking for device details */ 954 { 0x1CCF, 0x0000 } /* All Konami Amusement Devices - causes deadlock asking for device details */ 955 }; 956 957 for (i = 0; i < (sizeof(known_bad)/sizeof(known_bad[0])); i++) { 958 if ((vendor_id == known_bad[i].vid) && (product_id == known_bad[i].pid || known_bad[i].pid == 0x0000)) { 959 return 1; 960 } 961 } 962 963 return 0; 964} 965 966struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id) 967{ 968 struct hid_device_info *root = NULL; /* return object */ 969 struct hid_device_info *cur_dev = NULL; 970 GUID interface_class_guid; 971 CONFIGRET cr; 972 wchar_t* device_interface_list = NULL; 973 DWORD len; 974 975 if (hid_init() < 0) { 976 /* register_global_error: global error is reset by hid_init */ 977 return NULL; 978 } 979 980 /* Retrieve HID Interface Class GUID 981 https://docs.microsoft.com/windows-hardware/drivers/install/guid-devinterface-hid */ 982 HidD_GetHidGuid(&interface_class_guid); 983 984 /* Get the list of all device interfaces belonging to the HID class. */ 985 /* Retry in case of list was changed between calls to 986 CM_Get_Device_Interface_List_SizeW and CM_Get_Device_Interface_ListW */ 987 do { 988 cr = CM_Get_Device_Interface_List_SizeW(&len, &interface_class_guid, NULL, CM_GET_DEVICE_INTERFACE_LIST_PRESENT); 989 if (cr != CR_SUCCESS) { 990 register_global_error(L"Failed to get size of HID device interface list"); 991 break; 992 } 993 994 free(device_interface_list); // This should NOT be SDL_free() 995 996 device_interface_list = (wchar_t*)calloc(len, sizeof(wchar_t)); 997 if (device_interface_list == NULL) { 998 register_global_error(L"Failed to allocate memory for HID device interface list"); 999 return NULL; 1000 } 1001 cr = CM_Get_Device_Interface_ListW(&interface_class_guid, NULL, device_interface_list, len, CM_GET_DEVICE_INTERFACE_LIST_PRESENT); 1002 if (cr != CR_SUCCESS && cr != CR_BUFFER_SMALL) { 1003 register_global_error(L"Failed to get HID device interface list"); 1004 } 1005 } while (cr == CR_BUFFER_SMALL); 1006 1007 if (cr != CR_SUCCESS) { 1008 goto end_of_function; 1009 } 1010 1011 /* Iterate over each device interface in the HID class, looking for the right one. */ 1012 for (wchar_t* device_interface = device_interface_list; *device_interface; device_interface += wcslen(device_interface) + 1) { 1013 HANDLE device_handle = INVALID_HANDLE_VALUE; 1014 HIDD_ATTRIBUTES attrib; 1015 1016 /* XInput devices don't get real HID reports and are better handled by the raw input driver */ 1017 if (wcsstr(device_interface, L"&IG_") != NULL) { 1018 continue; 1019 } 1020 1021 /* Open read-only handle to the device */ 1022 device_handle = open_device(device_interface, FALSE); 1023 1024 /* Check validity of device_handle. */ 1025 if (device_handle == INVALID_HANDLE_VALUE) { 1026 /* Unable to open the device. */ 1027 continue; 1028 } 1029 1030#ifdef HIDAPI_IGNORE_DEVICE 1031 hid_bus_type bus_type = HID_API_BUS_UNKNOWN; 1032 PHIDP_PREPARSED_DATA pp_data = NULL; 1033 HIDP_CAPS caps = { 0 }; 1034#endif 1035 1036 /* Get the Vendor ID and Product ID for this device. */ 1037 attrib.Size = sizeof(HIDD_ATTRIBUTES); 1038 if (!HidD_GetAttributes(device_handle, &attrib)) { 1039 goto cont_close; 1040 } 1041 1042#ifdef HIDAPI_IGNORE_DEVICE 1043 /* See if there are any devices we should skip in enumeration */ 1044 bus_type = get_bus_type(device_interface); 1045 if (HidD_GetPreparsedData(device_handle, &pp_data)) { 1046 HidP_GetCaps(pp_data, &caps); 1047 HidD_FreePreparsedData(pp_data); 1048 } 1049 if (HIDAPI_IGNORE_DEVICE(bus_type, attrib.VendorID, attrib.ProductID, caps.UsagePage, caps.Usage, false)) { 1050 goto cont_close; 1051 } 1052#endif 1053 1054 /* Check the VID/PID to see if we should add this 1055 device to the enumeration list. */ 1056 if ((vendor_id == 0x0 || attrib.VendorID == vendor_id) && 1057 (product_id == 0x0 || attrib.ProductID == product_id) && 1058 !hid_blacklist(attrib.VendorID, attrib.ProductID)) { 1059 1060 /* VID/PID match. Create the record. */ 1061 struct hid_device_info *tmp = hid_internal_get_device_info(device_interface, device_handle); 1062 1063 if (tmp == NULL) { 1064 goto cont_close; 1065 } 1066 1067 if (cur_dev) { 1068 cur_dev->next = tmp; 1069 } 1070 else { 1071 root = tmp; 1072 } 1073 cur_dev = tmp; 1074 } 1075 1076cont_close: 1077 CloseHandle(device_handle); 1078 } 1079 1080 if (root == NULL) { 1081 if (vendor_id == 0 && product_id == 0) { 1082 register_global_error(L"No HID devices found in the system."); 1083 } else { 1084 register_global_error(L"No HID devices with requested VID/PID found in the system."); 1085 } 1086 } 1087 1088end_of_function: 1089 free(device_interface_list); 1090 1091 return root; 1092} 1093 1094void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs) 1095{ 1096 /* TODO: Merge this with the Linux version. This function is platform-independent. */ 1097 struct hid_device_info *d = devs; 1098 while (d) { 1099 struct hid_device_info *next = d->next; 1100 free(d->path); 1101 free(d->serial_number); 1102 free(d->manufacturer_string); 1103 free(d->product_string); 1104 free(d); 1105 d = next; 1106 } 1107} 1108 1109HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number) 1110{ 1111 /* TODO: Merge this functions with the Linux version. This function should be platform independent. */ 1112 struct hid_device_info *devs, *cur_dev; 1113 const char *path_to_open = NULL; 1114 hid_device *handle = NULL; 1115 1116 /* register_global_error: global error is reset by hid_enumerate/hid_init */ 1117 devs = hid_enumerate(vendor_id, product_id); 1118 if (!devs) { 1119 /* register_global_error: global error is already set by hid_enumerate */ 1120 return NULL; 1121 } 1122 1123 cur_dev = devs; 1124 while (cur_dev) { 1125 if (cur_dev->vendor_id == vendor_id && 1126 cur_dev->product_id == product_id) { 1127 if (serial_number) { 1128 if (cur_dev->serial_number && wcscmp(serial_number, cur_dev->serial_number) == 0) { 1129 path_to_open = cur_dev->path; 1130 break; 1131 } 1132 } 1133 else { 1134 path_to_open = cur_dev->path; 1135 break; 1136 } 1137 } 1138 cur_dev = cur_dev->next; 1139 } 1140 1141 if (path_to_open) { 1142 /* Open the device */ 1143 handle = hid_open_path(path_to_open); 1144 } else { 1145 register_global_error(L"Device with requested VID/PID/(SerialNumber) not found"); 1146 } 1147 1148 hid_free_enumeration(devs); 1149 1150 return handle; 1151} 1152 1153HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path) 1154{ 1155 hid_device *dev = NULL; 1156 wchar_t* interface_path = NULL; 1157 HANDLE device_handle = INVALID_HANDLE_VALUE; 1158 PHIDP_PREPARSED_DATA pp_data = NULL; 1159 HIDP_CAPS caps; 1160 1161 if (hid_init() < 0) { 1162 /* register_global_error: global error is reset by hid_init */ 1163 goto end_of_function; 1164 } 1165 1166 interface_path = hid_internal_UTF8toUTF16(path); 1167 if (!interface_path) { 1168 register_global_error(L"Path conversion failure"); 1169 goto end_of_function; 1170 } 1171 1172 /* Open a handle to the device */ 1173 device_handle = open_device(interface_path, TRUE); 1174 1175 /* Check validity of write_handle. */ 1176 if (device_handle == INVALID_HANDLE_VALUE) { 1177 /* System devices, such as keyboards and mice, cannot be opened in 1178 read-write mode, because the system takes exclusive control over 1179 them. This is to prevent keyloggers. However, feature reports 1180 can still be sent and received. Retry opening the device, but 1181 without read/write access. */ 1182 device_handle = open_device(interface_path, FALSE); 1183 1184 /* Check the validity of the limited device_handle. */ 1185 if (device_handle == INVALID_HANDLE_VALUE) { 1186 register_global_winapi_error(L"open_device"); 1187 goto end_of_function; 1188 } 1189 } 1190 1191 /* Set the Input Report buffer size to 64 reports. */ 1192 if (!HidD_SetNumInputBuffers(device_handle, 64)) { 1193 register_global_winapi_error(L"set input buffers"); 1194 goto end_of_function; 1195 } 1196 1197 /* Get the Input Report length for the device. */ 1198 if (!HidD_GetPreparsedData(device_handle, &pp_data)) { 1199 register_global_winapi_error(L"get preparsed data"); 1200 goto end_of_function; 1201 } 1202 1203 if (HidP_GetCaps(pp_data, &caps) != HIDP_STATUS_SUCCESS) { 1204 register_global_error(L"HidP_GetCaps"); 1205 goto end_of_function; 1206 } 1207 1208 dev = new_hid_device(); 1209 1210 if (dev == NULL) { 1211 register_global_error(L"hid_device allocation error"); 1212 goto end_of_function; 1213 } 1214 1215 dev->device_handle = device_handle; 1216 device_handle = INVALID_HANDLE_VALUE; 1217 1218 dev->output_report_length = caps.OutputReportByteLength; 1219 dev->input_report_length = caps.InputReportByteLength; 1220 dev->feature_report_length = caps.FeatureReportByteLength; 1221 dev->read_buf = (char*) malloc(dev->input_report_length); 1222 dev->device_info = hid_internal_get_device_info(interface_path, dev->device_handle); 1223 1224 /* On Windows 7, we need to use hid_write_output_report() over Bluetooth */ 1225 if (dev->output_report_length > 512) { 1226 dev->use_hid_write_output_report = !IsWindowsVersionOrGreater( HIBYTE( _WIN32_WINNT_WIN8 ), LOBYTE( _WIN32_WINNT_WIN8 ), 0 ); 1227 } 1228 1229end_of_function: 1230 free(interface_path); 1231 CloseHandle(device_handle); 1232 1233 if (pp_data) { 1234 HidD_FreePreparsedData(pp_data); 1235 } 1236 1237 return dev; 1238} 1239 1240static int hid_write_output_report(hid_device *dev, const unsigned char *data, size_t length) 1241{ 1242 BOOL res; 1243 res = HidD_SetOutputReport(dev->device_handle, (void *)data, (ULONG)length); 1244 if (res) 1245 return (int)length; 1246 else 1247 return -1; 1248} 1249 1250int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char *data, size_t length) 1251{ 1252 DWORD bytes_written = 0; 1253 int function_result = -1; 1254 BOOL res; 1255 BOOL overlapped = FALSE; 1256 1257 unsigned char *buf; 1258 1259 if (!data || !length) { 1260 register_string_error(dev, L"Zero buffer/length"); 1261 return function_result; 1262 } 1263 1264 register_string_error(dev, NULL); 1265 1266 if (dev->use_hid_write_output_report) { 1267 return hid_write_output_report(dev, data, length); 1268 } 1269 1270 /* Make sure the right number of bytes are passed to WriteFile. Windows 1271 expects the number of bytes which are in the _longest_ report (plus 1272 one for the report number) bytes even if the data is a report 1273 which is shorter than that. Windows gives us this value in 1274 caps.OutputReportByteLength. If a user passes in fewer bytes than this, 1275 use cached temporary buffer which is the proper size. */ 1276 if (length >= dev->output_report_length) { 1277 /* The user passed the right number of bytes. Use the buffer as-is. */ 1278 buf = (unsigned char *) data; 1279 } else { 1280 if (dev->write_buf == NULL) 1281 dev->write_buf = (unsigned char *) malloc(dev->output_report_length); 1282 buf = dev->write_buf; 1283 memcpy(buf, data, length); 1284 memset(buf + length, 0, dev->output_report_length - length); 1285 length = dev->output_report_length; 1286 } 1287 1288 res = WriteFile(dev->device_handle, buf, (DWORD) length, &bytes_written, &dev->write_ol); 1289 1290 if (!res) { 1291 if (GetLastError() != ERROR_IO_PENDING) { 1292 /* WriteFile() failed. Return error. */ 1293 register_winapi_error(dev, L"WriteFile"); 1294 goto end_of_function; 1295 } 1296 overlapped = TRUE; 1297 } else { 1298 /* WriteFile() succeeded synchronously. */ 1299 function_result = bytes_written; 1300 } 1301 1302 if (overlapped) { 1303 /* Wait for the transaction to complete. This makes 1304 hid_write() synchronous. */ 1305 res = WaitForSingleObject(dev->write_ol.hEvent, 1000); 1306 if (res != WAIT_OBJECT_0) { 1307 /* There was a Timeout. */ 1308 register_winapi_error(dev, L"hid_write/WaitForSingleObject"); 1309 goto end_of_function; 1310 } 1311 1312 /* Get the result. */ 1313 res = GetOverlappedResult(dev->device_handle, &dev->write_ol, &bytes_written, FALSE/*wait*/); 1314 if (res) { 1315 function_result = bytes_written; 1316 } 1317 else { 1318 /* The Write operation failed. */ 1319 register_winapi_error(dev, L"hid_write/GetOverlappedResult"); 1320 goto end_of_function; 1321 } 1322 } 1323 1324end_of_function: 1325 return function_result; 1326} 1327 1328 1329int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds) 1330{ 1331 DWORD bytes_read = 0; 1332 size_t copy_len = 0; 1333 BOOL res = FALSE; 1334 BOOL overlapped = FALSE; 1335 1336 if (!data || !length) { 1337 register_string_error(dev, L"Zero buffer/length"); 1338 return -1; 1339 } 1340 1341 register_string_error(dev, NULL); 1342 1343 /* Copy the handle for convenience. */ 1344 HANDLE ev = dev->ol.hEvent; 1345 1346 if (!dev->read_pending) { 1347 /* Start an Overlapped I/O read. */ 1348 dev->read_pending = TRUE; 1349 memset(dev->read_buf, 0, dev->input_report_length); 1350 ResetEvent(ev); 1351 res = ReadFile(dev->device_handle, dev->read_buf, (DWORD) dev->input_report_length, &bytes_read, &dev->ol); 1352 1353 if (!res) { 1354 if (GetLastError() != ERROR_IO_PENDING) { 1355 /* ReadFile() has failed. 1356 Clean up and return error. */ 1357 register_winapi_error(dev, L"ReadFile"); 1358 CancelIo(dev->device_handle); 1359 dev->read_pending = FALSE; 1360 goto end_of_function; 1361 } 1362 overlapped = TRUE; 1363 } 1364 } 1365 else { 1366 overlapped = TRUE; 1367 } 1368 1369 if (overlapped) { 1370 /* See if there is any data yet. */ 1371 res = WaitForSingleObject(ev, milliseconds >= 0 ? (DWORD)milliseconds : INFINITE); 1372 if (res != WAIT_OBJECT_0) { 1373 /* There was no data this time. Return zero bytes available, 1374 but leave the Overlapped I/O running. */ 1375 return 0; 1376 } 1377 1378 /* Get the number of bytes read. The actual data has been copied to the data[] 1379 array which was passed to ReadFile(). We must not wait here because we've 1380 already waited on our event above, and since it's auto-reset, it will have 1381 been reset back to unsignalled by now. */ 1382 res = GetOverlappedResult(dev->device_handle, &dev->ol, &bytes_read, FALSE/*don't wait now - already did on the prev step*/); 1383 } 1384 /* Set pending back to false, even if GetOverlappedResult() returned error. */ 1385 dev->read_pending = FALSE; 1386 1387 if (res && bytes_read > 0) { 1388 if (dev->read_buf[0] == 0x0) { 1389 /* If report numbers aren't being used, but Windows sticks a report 1390 number (0x0) on the beginning of the report anyway. To make this 1391 work like the other platforms, and to make it work more like the 1392 HID spec, we'll skip over this byte. */ 1393 bytes_read--; 1394 copy_len = length > bytes_read ? bytes_read : length; 1395 memcpy(data, dev->read_buf+1, copy_len); 1396 } 1397 else { 1398 /* Copy the whole buffer, report number and all. */ 1399 copy_len = length > bytes_read ? bytes_read : length; 1400 memcpy(data, dev->read_buf, copy_len); 1401 } 1402 } 1403 if (!res) { 1404 if (GetLastError() == ERROR_OPERATION_ABORTED) { 1405 /* The read request was issued on another thread. 1406 This is harmless, so just ignore it. */ 1407 return 0; 1408 } 1409 register_winapi_error(dev, L"hid_read_timeout/GetOverlappedResult"); 1410 } 1411 1412end_of_function: 1413 if (!res) { 1414 return -1; 1415 } 1416 1417 return (int) copy_len; 1418} 1419 1420int HID_API_EXPORT HID_API_CALL hid_read(hid_device *dev, unsigned char *data, size_t length) 1421{ 1422 return hid_read_timeout(dev, data, length, (dev->blocking)? -1: 0); 1423} 1424 1425int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *dev, int nonblock) 1426{ 1427 dev->blocking = !nonblock; 1428 return 0; /* Success */ 1429} 1430 1431int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length) 1432{ 1433 BOOL res = FALSE; 1434 unsigned char *buf; 1435 size_t length_to_send; 1436 1437 if (!data || !length) { 1438 register_string_error(dev, L"Zero buffer/length"); 1439 return -1; 1440 } 1441 1442 register_string_error(dev, NULL); 1443 1444 /* Windows expects at least caps.FeatureReportByteLength bytes passed 1445 to HidD_SetFeature(), even if the report is shorter. Any less sent and 1446 the function fails with error ERROR_INVALID_PARAMETER set. Any more 1447 and HidD_SetFeature() silently truncates the data sent in the report 1448 to caps.FeatureReportByteLength. */ 1449 if (length >= dev->feature_report_length) { 1450 buf = (unsigned char *) data; 1451 length_to_send = length; 1452 } else { 1453 if (dev->feature_buf == NULL) 1454 dev->feature_buf = (unsigned char *) malloc(dev->feature_report_length); 1455 buf = dev->feature_buf; 1456 memcpy(buf, data, length); 1457 memset(buf + length, 0, dev->feature_report_length - length); 1458 length_to_send = dev->feature_report_length; 1459 } 1460 1461 res = HidD_SetFeature(dev->device_handle, (PVOID)buf, (DWORD) length_to_send); 1462 1463 if (!res) { 1464 register_winapi_error(dev, L"HidD_SetFeature"); 1465 return -1; 1466 } 1467 1468 return (int) length; 1469} 1470 1471static int hid_get_report(hid_device *dev, DWORD report_type, unsigned char *data, size_t length) 1472{ 1473 BOOL res; 1474 DWORD bytes_returned = 0; 1475 1476 OVERLAPPED ol; 1477 memset(&ol, 0, sizeof(ol)); 1478 1479 if (!data || !length) { 1480 register_string_error(dev, L"Zero buffer/length"); 1481 return -1; 1482 } 1483 1484 register_string_error(dev, NULL); 1485 1486 res = DeviceIoControl(dev->device_handle, 1487 report_type, 1488 data, (DWORD) length, 1489 data, (DWORD) length, 1490 &bytes_returned, &ol); 1491 1492 if (!res) { 1493 if (GetLastError() != ERROR_IO_PENDING) { 1494 /* DeviceIoControl() failed. Return error. */ 1495 register_winapi_error(dev, L"Get Input/Feature Report DeviceIoControl"); 1496 return -1; 1497 } 1498 } 1499 1500 /* Wait here until the write is done. This makes 1501 hid_get_feature_report() synchronous. */ 1502 res = GetOverlappedResult(dev->device_handle, &ol, &bytes_returned, TRUE/*wait*/); 1503 if (!res) { 1504 /* The operation failed. */ 1505 register_winapi_error(dev, L"Get Input/Feature Report GetOverLappedResult"); 1506 return -1; 1507 } 1508 1509 /* When numbered reports aren't used, 1510 bytes_returned seem to include only what is actually received from the device 1511 (not including the first byte with 0, as an indication "no numbered reports"). */ 1512 if (data[0] == 0x0) { 1513 bytes_returned++; 1514 } 1515 1516 return bytes_returned; 1517} 1518 1519int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length) 1520{ 1521 /* We could use HidD_GetFeature() instead, but it doesn't give us an actual length, unfortunately */ 1522 return hid_get_report(dev, IOCTL_HID_GET_FEATURE, data, length); 1523} 1524 1525int HID_API_EXPORT HID_API_CALL hid_get_input_report(hid_device *dev, unsigned char *data, size_t length) 1526{ 1527 /* We could use HidD_GetInputReport() instead, but it doesn't give us an actual length, unfortunately */ 1528 return hid_get_report(dev, IOCTL_HID_GET_INPUT_REPORT, data, length); 1529} 1530 1531#if defined(__GNUC__) && __GNUC__ >= 8 1532# pragma GCC diagnostic push 1533# pragma GCC diagnostic ignored "-Wcast-function-type" 1534#endif 1535void HID_API_EXPORT HID_API_CALL hid_close(hid_device *dev) 1536{ 1537 typedef BOOL (WINAPI *CancelIoEx_t)(HANDLE hFile, LPOVERLAPPED lpOverlapped); 1538 CancelIoEx_t CancelIoExFunc = (CancelIoEx_t)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "CancelIoEx"); 1539 1540 if (!dev) 1541 return; 1542 1543 if (CancelIoExFunc) { 1544 CancelIoExFunc(dev->device_handle, NULL); 1545 } else { 1546 /* Windows XP, this will only cancel I/O on the current thread */ 1547 CancelIo(dev->device_handle); 1548 } 1549 if (dev->read_pending) { 1550 DWORD bytes_read = 0; 1551 GetOverlappedResult(dev->device_handle, &dev->ol, &bytes_read, TRUE/*wait*/); 1552 } 1553 free_hid_device(dev); 1554} 1555#if defined(__GNUC__) && __GNUC__ >= 8 1556# pragma GCC diagnostic pop 1557#endif 1558 1559int HID_API_EXPORT_CALL HID_API_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen) 1560{ 1561 if (!string || !maxlen) { 1562 register_string_error(dev, L"Zero buffer/length"); 1563 return -1; 1564 } 1565 1566 if (!dev->device_info) { 1567 register_string_error(dev, L"NULL device info"); 1568 return -1; 1569 } 1570 1571 wcsncpy(string, dev->device_info->manufacturer_string, maxlen); 1572 string[maxlen - 1] = L'\0'; 1573 1574 register_string_error(dev, NULL); 1575 1576 return 0; 1577} 1578 1579int HID_API_EXPORT_CALL HID_API_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen) 1580{ 1581 if (!string || !maxlen) { 1582 register_string_error(dev, L"Zero buffer/length"); 1583 return -1; 1584 } 1585 1586 if (!dev->device_info) { 1587 register_string_error(dev, L"NULL device info"); 1588 return -1; 1589 } 1590 1591 wcsncpy(string, dev->device_info->product_string, maxlen); 1592 string[maxlen - 1] = L'\0'; 1593 1594 register_string_error(dev, NULL); 1595 1596 return 0; 1597} 1598 1599int HID_API_EXPORT_CALL HID_API_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen) 1600{ 1601 if (!string || !maxlen) { 1602 register_string_error(dev, L"Zero buffer/length"); 1603 return -1; 1604 } 1605 1606 if (!dev->device_info) { 1607 register_string_error(dev, L"NULL device info"); 1608 return -1; 1609 } 1610 1611 wcsncpy(string, dev->device_info->serial_number, maxlen); 1612 string[maxlen - 1] = L'\0'; 1613 1614 register_string_error(dev, NULL); 1615 1616 return 0; 1617} 1618 1619HID_API_EXPORT struct hid_device_info * HID_API_CALL hid_get_device_info(hid_device *dev) { 1620 if (!dev->device_info) 1621 { 1622 register_string_error(dev, L"NULL device info"); 1623 return NULL; 1624 } 1625 1626 return dev->device_info; 1627} 1628 1629int HID_API_EXPORT_CALL HID_API_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen) 1630{ 1631 BOOL res; 1632 1633 if (dev->device_info && dev->device_info->bus_type == HID_API_BUS_USB && maxlen > MAX_STRING_WCHARS_USB) { 1634 string[MAX_STRING_WCHARS_USB] = L'\0'; 1635 maxlen = MAX_STRING_WCHARS_USB; 1636 } 1637 1638 res = HidD_GetIndexedString(dev->device_handle, string_index, string, (ULONG)maxlen * sizeof(wchar_t)); 1639 if (!res) { 1640 register_winapi_error(dev, L"HidD_GetIndexedString"); 1641 return -1; 1642 } 1643 1644 register_string_error(dev, NULL); 1645 1646 return 0; 1647} 1648 1649int HID_API_EXPORT_CALL hid_winapi_get_container_id(hid_device *dev, GUID *container_id) 1650{ 1651 wchar_t *interface_path = NULL, *device_id = NULL; 1652 CONFIGRET cr = CR_FAILURE; 1653 DEVINST dev_node; 1654 DEVPROPTYPE property_type; 1655 ULONG len; 1656 1657 if (!container_id) { 1658 register_string_error(dev, L"Invalid Container ID"); 1659 return -1; 1660 } 1661 1662 register_string_error(dev, NULL); 1663 1664 interface_path = hid_internal_UTF8toUTF16(dev->device_info->path); 1665 if (!interface_path) { 1666 register_string_error(dev, L"Path conversion failure"); 1667 goto end; 1668 } 1669 1670 /* Get the device id from interface path */ 1671 device_id = hid_internal_get_device_interface_property(interface_path, &DEVPKEY_Device_InstanceId, DEVPROP_TYPE_STRING); 1672 if (!device_id) { 1673 register_string_error(dev, L"Failed to get device interface property InstanceId"); 1674 goto end; 1675 } 1676 1677 /* Open devnode from device id */ 1678 cr = CM_Locate_DevNodeW(&dev_node, (DEVINSTID_W)device_id, CM_LOCATE_DEVNODE_NORMAL); 1679 if (cr != CR_SUCCESS) { 1680 register_string_error(dev, L"Failed to locate device node"); 1681 goto end; 1682 } 1683 1684 /* Get the container id from devnode */ 1685 len = sizeof(*container_id); 1686 cr = CM_Get_DevNode_PropertyW(dev_node, &DEVPKEY_Device_ContainerId, &property_type, (PBYTE)container_id, &len, 0); 1687 if (cr == CR_SUCCESS && property_type != DEVPROP_TYPE_GUID) 1688 cr = CR_FAILURE; 1689 1690 if (cr != CR_SUCCESS) 1691 register_string_error(dev, L"Failed to read ContainerId property from device node"); 1692 1693end: 1694 free(interface_path); 1695 free(device_id); 1696 1697 return cr == CR_SUCCESS ? 0 : -1; 1698} 1699 1700 1701int HID_API_EXPORT_CALL hid_get_report_descriptor(hid_device *dev, unsigned char *buf, size_t buf_size) 1702{ 1703 PHIDP_PREPARSED_DATA pp_data = NULL; 1704 1705 if (!HidD_GetPreparsedData(dev->device_handle, &pp_data) || pp_data == NULL) { 1706 register_string_error(dev, L"HidD_GetPreparsedData"); 1707 return -1; 1708 } 1709 1710 int res = hid_winapi_descriptor_reconstruct_pp_data(pp_data, buf, buf_size); 1711 1712 HidD_FreePreparsedData(pp_data); 1713 1714 return res; 1715} 1716 1717HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev) 1718{ 1719 if (dev) { 1720 if (dev->last_error_str == NULL) 1721 return L"Success"; 1722 return (wchar_t*)dev->last_error_str; 1723 } 1724 1725 if (last_global_error_str == NULL) 1726 return L"Success"; 1727 return last_global_error_str; 1728} 1729 1730#ifndef hidapi_winapi_EXPORTS 1731#include "hidapi_descriptor_reconstruct.c" 1732#endif 1733 1734#ifdef __cplusplus 1735} /* extern "C" */ 1736#endif 1737[FILE END](C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.