2020 https://github.com/libusb/hidapi .
2121********************************************************/
2222
23+ #if defined(_MSC_VER ) && !defined(_CRT_SECURE_NO_WARNINGS )
24+ // Do not warn about mbsrtowcs and 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+
2329#include <windows.h>
2430
2531#ifndef _NTDEF_
@@ -135,6 +141,34 @@ static struct hid_api_version api_version = {
135141
136142 static HMODULE lib_handle = NULL ;
137143 static BOOLEAN initialized = FALSE;
144+
145+ typedef DWORD RETURN_TYPE ;
146+ typedef RETURN_TYPE CONFIGRET ;
147+ typedef DWORD DEVNODE , DEVINST ;
148+ typedef DEVNODE * PDEVNODE , * PDEVINST ;
149+ typedef WCHAR * DEVNODEID_W , * DEVINSTID_W ;
150+
151+ #define CR_SUCCESS (0x00000000)
152+ #define CR_BUFFER_SMALL (0x0000001A)
153+
154+ #define CM_LOCATE_DEVNODE_NORMAL 0x00000000
155+
156+ #define DEVPROP_TYPEMOD_LIST 0x00002000
157+
158+ #define DEVPROP_TYPE_STRING 0x00000012
159+ #define DEVPROP_TYPE_STRING_LIST (DEVPROP_TYPE_STRING|DEVPROP_TYPEMOD_LIST)
160+
161+ typedef CONFIGRET (__stdcall* CM_Locate_DevNodeW_ )(PDEVINST pdnDevInst , DEVINSTID_W pDeviceID , ULONG ulFlags );
162+ typedef CONFIGRET (__stdcall* CM_Get_Parent_ )(PDEVINST pdnDevInst , DEVINST dnDevInst , ULONG ulFlags );
163+ typedef CONFIGRET (__stdcall* CM_Get_DevNode_PropertyW_ )(DEVINST dnDevInst , CONST DEVPROPKEY * PropertyKey , DEVPROPTYPE * PropertyType , PBYTE PropertyBuffer , PULONG PropertyBufferSize , ULONG ulFlags );
164+ typedef CONFIGRET (__stdcall* CM_Get_Device_Interface_PropertyW_ )(LPCWSTR pszDeviceInterface , CONST DEVPROPKEY * PropertyKey , DEVPROPTYPE * PropertyType , PBYTE PropertyBuffer , PULONG PropertyBufferSize , ULONG ulFlags );
165+
166+ static CM_Locate_DevNodeW_ CM_Locate_DevNodeW = NULL ;
167+ static CM_Get_Parent_ CM_Get_Parent = NULL ;
168+ static CM_Get_DevNode_PropertyW_ CM_Get_DevNode_PropertyW = NULL ;
169+ static CM_Get_Device_Interface_PropertyW_ CM_Get_Device_Interface_PropertyW = NULL ;
170+
171+ static HMODULE cfgmgr32_lib_handle = NULL ;
138172#endif /* HIDAPI_USE_DDK */
139173
140174struct hid_device_ {
@@ -151,6 +185,7 @@ struct hid_device_ {
151185 char * read_buf ;
152186 OVERLAPPED ol ;
153187 OVERLAPPED write_ol ;
188+ struct hid_device_info * device_info ;
154189};
155190
156191static hid_device * new_hid_device ()
@@ -171,6 +206,7 @@ static hid_device *new_hid_device()
171206 dev -> ol .hEvent = CreateEvent (NULL , FALSE, FALSE /*initial state f=nonsignaled*/ , NULL );
172207 memset (& dev -> write_ol , 0 , sizeof (dev -> write_ol ));
173208 dev -> write_ol .hEvent = CreateEvent (NULL , FALSE, FALSE /*inital state f=nonsignaled*/ , NULL );
209+ dev -> device_info = NULL ;
174210
175211 return dev ;
176212}
@@ -184,6 +220,7 @@ static void free_hid_device(hid_device *dev)
184220 free (dev -> write_buf );
185221 free (dev -> feature_buf );
186222 free (dev -> read_buf );
223+ free (dev -> device_info );
187224 free (dev );
188225}
189226
@@ -248,6 +285,29 @@ static int lookup_functions()
248285 else
249286 return -1 ;
250287
288+ cfgmgr32_lib_handle = LoadLibraryA ("cfgmgr32.dll" );
289+ if (cfgmgr32_lib_handle ) {
290+ #if defined(__GNUC__ )
291+ # pragma GCC diagnostic push
292+ # pragma GCC diagnostic ignored "-Wcast-function-type"
293+ #endif
294+ #define RESOLVE (x ) x = (x##_)GetProcAddress(cfgmgr32_lib_handle, #x);
295+ RESOLVE (CM_Locate_DevNodeW );
296+ RESOLVE (CM_Get_Parent );
297+ RESOLVE (CM_Get_DevNode_PropertyW );
298+ RESOLVE (CM_Get_Device_Interface_PropertyW );
299+ #undef RESOLVE
300+ #if defined(__GNUC__ )
301+ # pragma GCC diagnostic pop
302+ #endif
303+ }
304+ else {
305+ CM_Locate_DevNodeW = NULL ;
306+ CM_Get_Parent = NULL ;
307+ CM_Get_DevNode_PropertyW = NULL ;
308+ CM_Get_Device_Interface_PropertyW = NULL ;
309+ }
310+
251311 return 0 ;
252312}
253313#endif
@@ -299,11 +359,135 @@ int HID_API_EXPORT hid_exit(void)
299359 if (lib_handle )
300360 FreeLibrary (lib_handle );
301361 lib_handle = NULL ;
362+ if (cfgmgr32_lib_handle )
363+ FreeLibrary (cfgmgr32_lib_handle );
364+ cfgmgr32_lib_handle = NULL ;
302365 initialized = FALSE;
303366#endif
304367 return 0 ;
305368}
306369
370+ static void hid_internal_get_ble_info (struct hid_device_info * dev , DEVINST dev_node )
371+ {
372+ ULONG len ;
373+ CONFIGRET cr ;
374+ DEVPROPTYPE property_type ;
375+
376+ static DEVPROPKEY DEVPKEY_NAME = { { 0xb725f130 , 0x47ef , 0x101a , 0xa5 , 0xf1 , 0x02 , 0x60 , 0x8c , 0x9e , 0xeb , 0xac }, 10 }; // DEVPROP_TYPE_STRING
377+ static DEVPROPKEY PKEY_DeviceInterface_Bluetooth_DeviceAddress = { { 0x2BD67D8B , 0x8BEB , 0x48D5 , 0x87 , 0xE0 , 0x6C , 0xDA , 0x34 , 0x28 , 0x04 , 0x0A }, 1 }; // DEVPROP_TYPE_STRING
378+ static DEVPROPKEY PKEY_DeviceInterface_Bluetooth_Manufacturer = { { 0x2BD67D8B , 0x8BEB , 0x48D5 , 0x87 , 0xE0 , 0x6C , 0xDA , 0x34 , 0x28 , 0x04 , 0x0A }, 4 }; // DEVPROP_TYPE_STRING
379+
380+ /* Manufacturer String */
381+ len = 0 ;
382+ cr = CM_Get_DevNode_PropertyW (dev_node , & PKEY_DeviceInterface_Bluetooth_Manufacturer , & property_type , NULL , & len , 0 );
383+ if (cr == CR_BUFFER_SMALL && property_type == DEVPROP_TYPE_STRING ) {
384+ free (dev -> manufacturer_string );
385+ dev -> manufacturer_string = (wchar_t * )calloc (len , sizeof (BYTE ));
386+ CM_Get_DevNode_PropertyW (dev_node , & PKEY_DeviceInterface_Bluetooth_Manufacturer , & property_type , (PBYTE )dev -> manufacturer_string , & len , 0 );
387+ }
388+
389+ /* Serial Number String (MAC Address) */
390+ len = 0 ;
391+ cr = CM_Get_DevNode_PropertyW (dev_node , & PKEY_DeviceInterface_Bluetooth_DeviceAddress , & property_type , NULL , & len , 0 );
392+ if (cr == CR_BUFFER_SMALL && property_type == DEVPROP_TYPE_STRING ) {
393+ free (dev -> serial_number );
394+ dev -> serial_number = (wchar_t * )calloc (len , sizeof (BYTE ));
395+ CM_Get_DevNode_PropertyW (dev_node , & PKEY_DeviceInterface_Bluetooth_DeviceAddress , & property_type , (PBYTE )dev -> serial_number , & len , 0 );
396+ }
397+
398+ /* Get devnode grandparent to reach out Bluetooth LE device node */
399+ cr = CM_Get_Parent (& dev_node , dev_node , 0 );
400+ if (cr != CR_SUCCESS )
401+ return ;
402+
403+ /* Product String */
404+ len = 0 ;
405+ cr = CM_Get_DevNode_PropertyW (dev_node , & DEVPKEY_NAME , & property_type , NULL , & len , 0 );
406+ if (cr == CR_BUFFER_SMALL && property_type == DEVPROP_TYPE_STRING ) {
407+ free (dev -> product_string );
408+ dev -> product_string = (wchar_t * )calloc (len , sizeof (BYTE ));
409+ CM_Get_DevNode_PropertyW (dev_node , & DEVPKEY_NAME , & property_type , (PBYTE )dev -> product_string , & len , 0 );
410+ }
411+ }
412+
413+ static void hid_internal_get_info (struct hid_device_info * dev )
414+ {
415+ char * tmp = NULL ;
416+ wchar_t * interface_path = NULL , * device_id = NULL , * compatible_ids = NULL ;
417+ mbstate_t state ;
418+ ULONG len ;
419+ CONFIGRET cr ;
420+ DEVPROPTYPE property_type ;
421+ DEVINST dev_node ;
422+
423+ static DEVPROPKEY DEVPKEY_Device_InstanceId = { { 0x78c34fc8 , 0x104a , 0x4aca , 0x9e , 0xa4 , 0x52 , 0x4d , 0x52 , 0x99 , 0x6e , 0x57 }, 256 }; // DEVPROP_TYPE_STRING
424+ static DEVPROPKEY DEVPKEY_Device_CompatibleIds = { { 0xa45c254e , 0xdf1c , 0x4efd , 0x80 , 0x20 , 0x67 , 0xd1 , 0x46 , 0xa8 , 0x50 , 0xe0 }, 4 }; // DEVPROP_TYPE_STRING_LIST
425+
426+ if (!CM_Get_Device_Interface_PropertyW ||
427+ !CM_Locate_DevNodeW ||
428+ !CM_Get_Parent ||
429+ !CM_Get_DevNode_PropertyW )
430+ goto end ;
431+
432+ tmp = dev -> path ;
433+
434+ len = (ULONG )strlen (tmp );
435+ interface_path = (wchar_t * )calloc (len + 1 , sizeof (wchar_t ));
436+ memset (& state , 0 , sizeof (state ));
437+
438+ if (mbsrtowcs (interface_path , & tmp , len , & state ) == (size_t )-1 )
439+ goto end ;
440+
441+ /* Get the device id from interface path */
442+ len = 0 ;
443+ cr = CM_Get_Device_Interface_PropertyW (interface_path , & DEVPKEY_Device_InstanceId , & property_type , NULL , & len , 0 );
444+ if (cr == CR_BUFFER_SMALL && property_type == DEVPROP_TYPE_STRING ) {
445+ device_id = (wchar_t * )calloc (len , sizeof (BYTE ));
446+ cr = CM_Get_Device_Interface_PropertyW (interface_path , & DEVPKEY_Device_InstanceId , & property_type , (PBYTE )device_id , & len , 0 );
447+ }
448+ if (cr != CR_SUCCESS )
449+ goto end ;
450+
451+ /* Open devnode from device id */
452+ cr = CM_Locate_DevNodeW (& dev_node , (DEVINSTID_W )device_id , CM_LOCATE_DEVNODE_NORMAL );
453+ if (cr != CR_SUCCESS )
454+ goto end ;
455+
456+ /* Get devnode parent */
457+ cr = CM_Get_Parent (& dev_node , dev_node , 0 );
458+ if (cr != CR_SUCCESS )
459+ goto end ;
460+
461+ /* Get the compatible ids from parent devnode */
462+ len = 0 ;
463+ cr = CM_Get_DevNode_PropertyW (dev_node , & DEVPKEY_Device_CompatibleIds , & property_type , NULL , & len , 0 );
464+ if (cr == CR_BUFFER_SMALL && property_type == DEVPROP_TYPE_STRING_LIST ) {
465+ compatible_ids = (wchar_t * )calloc (len , sizeof (BYTE ));
466+ cr = CM_Get_DevNode_PropertyW (dev_node , & DEVPKEY_Device_CompatibleIds , & property_type , (PBYTE )compatible_ids , & len , 0 );
467+ }
468+ if (cr != CR_SUCCESS )
469+ goto end ;
470+
471+ /* Now we can parse parent's compatible IDs to find out the device bus type */
472+ for (wchar_t * compatible_id = compatible_ids ; * compatible_id ; compatible_id += wcslen (compatible_id ) + 1 ) {
473+ /* Normalize to upper case */
474+ for (wchar_t * p = compatible_id ; * p ; ++ p ) * p = towupper (* p );
475+
476+ /* Bluetooth LE devices */
477+ if (wcsstr (compatible_id , L"BTHLEDEVICE" ) != NULL ) {
478+ /* HidD_GetProductString/HidD_GetManufacturerString/HidD_GetSerialNumberString is not working for BLE HID devices
479+ Request this info via dev node properties instead.
480+ https://docs.microsoft.com/answers/questions/401236/hidd-getproductstring-with-ble-hid-device.html */
481+ hid_internal_get_ble_info (dev , dev_node );
482+ break ;
483+ }
484+ }
485+ end :
486+ free (interface_path );
487+ free (device_id );
488+ free (compatible_ids );
489+ }
490+
307491static struct hid_device_info * hid_get_device_info (const char * path , HANDLE handle )
308492{
309493 struct hid_device_info * dev = NULL ; /* return object */
@@ -390,6 +574,8 @@ static struct hid_device_info *hid_get_device_info(const char *path, HANDLE hand
390574 }
391575 }
392576
577+ hid_internal_get_info (dev );
578+
393579 return dev ;
394580}
395581
@@ -650,6 +836,8 @@ HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path)
650836
651837 dev -> read_buf = (char * ) malloc (dev -> input_report_length );
652838
839+ dev -> device_info = hid_get_device_info (path , dev -> device_handle );
840+
653841 return dev ;
654842
655843err_pp_data :
@@ -948,39 +1136,33 @@ void HID_API_EXPORT HID_API_CALL hid_close(hid_device *dev)
9481136
9491137int HID_API_EXPORT_CALL HID_API_CALL hid_get_manufacturer_string (hid_device * dev , wchar_t * string , size_t maxlen )
9501138{
951- BOOL res ;
952-
953- res = HidD_GetManufacturerString (dev -> device_handle , string , sizeof (wchar_t ) * (DWORD ) MIN (maxlen , MAX_STRING_WCHARS ));
954- if (!res ) {
955- register_error (dev , "HidD_GetManufacturerString" );
1139+ if (!dev -> device_info || !string || !maxlen )
9561140 return -1 ;
957- }
1141+
1142+ wcsncpy (string , dev -> device_info -> manufacturer_string , maxlen );
1143+ string [maxlen ] = L'\0' ;
9581144
9591145 return 0 ;
9601146}
9611147
9621148int HID_API_EXPORT_CALL HID_API_CALL hid_get_product_string (hid_device * dev , wchar_t * string , size_t maxlen )
9631149{
964- BOOL res ;
965-
966- res = HidD_GetProductString (dev -> device_handle , string , sizeof (wchar_t ) * (DWORD ) MIN (maxlen , MAX_STRING_WCHARS ));
967- if (!res ) {
968- register_error (dev , "HidD_GetProductString" );
1150+ if (!dev -> device_info || !string || !maxlen )
9691151 return -1 ;
970- }
1152+
1153+ wcsncpy (string , dev -> device_info -> product_string , maxlen );
1154+ string [maxlen ] = L'\0' ;
9711155
9721156 return 0 ;
9731157}
9741158
9751159int HID_API_EXPORT_CALL HID_API_CALL hid_get_serial_number_string (hid_device * dev , wchar_t * string , size_t maxlen )
9761160{
977- BOOL res ;
978-
979- res = HidD_GetSerialNumberString (dev -> device_handle , string , sizeof (wchar_t ) * (DWORD ) MIN (maxlen , MAX_STRING_WCHARS ));
980- if (!res ) {
981- register_error (dev , "HidD_GetSerialNumberString" );
1161+ if (!dev -> device_info || !string || !maxlen )
9821162 return -1 ;
983- }
1163+
1164+ wcsncpy (string , dev -> device_info -> serial_number , maxlen );
1165+ string [maxlen ] = L'\0' ;
9841166
9851167 return 0 ;
9861168}
0 commit comments