Atlas - hid.c

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