Path: blob/master/thirdparty/sdl/hidapi/windows/hid.c
9917 views
/*******************************************************1HIDAPI - Multi-Platform library for2communication with HID devices.34Alan Ott5Signal 11 Software67libusb/hidapi Team89Copyright 2022, All Rights Reserved.1011At the discretion of the user of this library,12this software may be licensed under the terms of the13GNU General Public License v3, a BSD-Style license, or the14original HIDAPI license as outlined in the LICENSE.txt,15LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt16files located at the root of the source distribution.17These files may also be found in the public source18code repository located at:19https://github.com/libusb/hidapi .20********************************************************/2122#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)23/* Do not warn about wcsncpy usage.24https://docs.microsoft.com/cpp/c-runtime-library/security-features-in-the-crt */25#define _CRT_SECURE_NO_WARNINGS26#endif2728#ifdef __cplusplus29extern "C" {30#endif3132#include "hidapi_winapi.h"3334#include <windows.h>3536#ifndef _NTDEF_37typedef LONG NTSTATUS;38#endif3940#ifndef WC_ERR_INVALID_CHARS41#define WC_ERR_INVALID_CHARS 0x0000008042#endif43#ifndef _WIN32_WINNT_WIN844#define _WIN32_WINNT_WIN8 0x060245#endif4647#ifdef __CYGWIN__48#include <ntdef.h>49#include <wctype.h>50#define _wcsdup wcsdup51#endif5253/*#define HIDAPI_USE_DDK*/5455#include "hidapi_cfgmgr32.h"56#include "hidapi_hidclass.h"57#include "hidapi_hidsdi.h"5859#ifndef HIDAPI_USING_SDL_RUNTIME60#include <stdio.h>61#include <stdlib.h>62#include <string.h>63#endif6465#ifdef MIN66#undef MIN67#endif68#define MIN(x,y) ((x) < (y)? (x): (y))6970/* MAXIMUM_USB_STRING_LENGTH from usbspec.h is 255 */71/* BLUETOOTH_DEVICE_NAME_SIZE from bluetoothapis.h is 256 */72#define MAX_STRING_WCHARS 2567374/* For certain USB devices, using a buffer larger or equal to 127 wchars results75in successful completion of HID API functions, but a broken string is stored76in the output buffer. This behaviour persists even if HID API is bypassed and77HID IOCTLs are passed to the HID driver directly. Therefore, for USB devices,78the buffer MUST NOT exceed 126 WCHARs.79*/8081#define MAX_STRING_WCHARS_USB 1268283static struct hid_api_version api_version = {84.major = HID_API_VERSION_MAJOR,85.minor = HID_API_VERSION_MINOR,86.patch = HID_API_VERSION_PATCH87};8889#ifndef HIDAPI_USE_DDK90/* Since we're not building with the DDK, and the HID header91files aren't part of the Windows SDK, we define what we need ourselves.92In lookup_functions(), the function pointers93defined below are set. */9495static HidD_GetHidGuid_ HidD_GetHidGuid;96static HidD_GetAttributes_ HidD_GetAttributes;97static HidD_GetSerialNumberString_ HidD_GetSerialNumberString;98static HidD_GetManufacturerString_ HidD_GetManufacturerString;99static HidD_GetProductString_ HidD_GetProductString;100static HidD_SetFeature_ HidD_SetFeature;101static HidD_GetFeature_ HidD_GetFeature;102static HidD_GetInputReport_ HidD_GetInputReport;103static HidD_GetIndexedString_ HidD_GetIndexedString;104static HidD_GetPreparsedData_ HidD_GetPreparsedData;105static HidD_FreePreparsedData_ HidD_FreePreparsedData;106static HidP_GetCaps_ HidP_GetCaps;107static HidD_SetNumInputBuffers_ HidD_SetNumInputBuffers;108static HidD_SetOutputReport_ HidD_SetOutputReport;109110static CM_Locate_DevNodeW_ CM_Locate_DevNodeW = NULL;111static CM_Get_Parent_ CM_Get_Parent = NULL;112static CM_Get_DevNode_PropertyW_ CM_Get_DevNode_PropertyW = NULL;113static CM_Get_Device_Interface_PropertyW_ CM_Get_Device_Interface_PropertyW = NULL;114static CM_Get_Device_Interface_List_SizeW_ CM_Get_Device_Interface_List_SizeW = NULL;115static CM_Get_Device_Interface_ListW_ CM_Get_Device_Interface_ListW = NULL;116117static HMODULE hid_lib_handle = NULL;118static HMODULE cfgmgr32_lib_handle = NULL;119static BOOLEAN hidapi_initialized = FALSE;120121static void free_library_handles(void)122{123if (hid_lib_handle)124FreeLibrary(hid_lib_handle);125hid_lib_handle = NULL;126if (cfgmgr32_lib_handle)127FreeLibrary(cfgmgr32_lib_handle);128cfgmgr32_lib_handle = NULL;129}130131#if defined(__GNUC__) && __GNUC__ >= 8132# pragma GCC diagnostic push133# pragma GCC diagnostic ignored "-Wcast-function-type"134#endif135136static int lookup_functions(void)137{138hid_lib_handle = LoadLibraryW(L"hid.dll");139if (hid_lib_handle == NULL) {140goto err;141}142143cfgmgr32_lib_handle = LoadLibraryW(L"cfgmgr32.dll");144if (cfgmgr32_lib_handle == NULL) {145goto err;146}147148#define RESOLVE(lib_handle, x) x = (x##_)GetProcAddress(lib_handle, #x); if (!x) goto err;149150RESOLVE(hid_lib_handle, HidD_GetHidGuid);151RESOLVE(hid_lib_handle, HidD_GetAttributes);152RESOLVE(hid_lib_handle, HidD_GetSerialNumberString);153RESOLVE(hid_lib_handle, HidD_GetManufacturerString);154RESOLVE(hid_lib_handle, HidD_GetProductString);155RESOLVE(hid_lib_handle, HidD_SetFeature);156RESOLVE(hid_lib_handle, HidD_GetFeature);157RESOLVE(hid_lib_handle, HidD_GetInputReport);158RESOLVE(hid_lib_handle, HidD_GetIndexedString);159RESOLVE(hid_lib_handle, HidD_GetPreparsedData);160RESOLVE(hid_lib_handle, HidD_FreePreparsedData);161RESOLVE(hid_lib_handle, HidP_GetCaps);162RESOLVE(hid_lib_handle, HidD_SetNumInputBuffers);163RESOLVE(hid_lib_handle, HidD_SetOutputReport);164165RESOLVE(cfgmgr32_lib_handle, CM_Locate_DevNodeW);166RESOLVE(cfgmgr32_lib_handle, CM_Get_Parent);167RESOLVE(cfgmgr32_lib_handle, CM_Get_DevNode_PropertyW);168RESOLVE(cfgmgr32_lib_handle, CM_Get_Device_Interface_PropertyW);169RESOLVE(cfgmgr32_lib_handle, CM_Get_Device_Interface_List_SizeW);170RESOLVE(cfgmgr32_lib_handle, CM_Get_Device_Interface_ListW);171172#undef RESOLVE173174return 0;175176err:177free_library_handles();178return -1;179}180181#if defined(__GNUC__) && __GNUC__ >= 8182# pragma GCC diagnostic pop183#endif184185#endif /* HIDAPI_USE_DDK */186187struct hid_device_ {188HANDLE device_handle;189BOOL blocking;190USHORT output_report_length;191unsigned char *write_buf;192size_t input_report_length;193USHORT feature_report_length;194unsigned char *feature_buf;195wchar_t *last_error_str;196BOOL read_pending;197char *read_buf;198OVERLAPPED ol;199OVERLAPPED write_ol;200struct hid_device_info* device_info;201BOOL use_hid_write_output_report;202};203204static BOOL IsWindowsVersionOrGreater(WORD wMajorVersion, WORD wMinorVersion, WORD wServicePackMajor)205{206OSVERSIONINFOEXW osvi;207DWORDLONG const dwlConditionMask = VerSetConditionMask(208VerSetConditionMask(209VerSetConditionMask(2100, VER_MAJORVERSION, VER_GREATER_EQUAL ),211VER_MINORVERSION, VER_GREATER_EQUAL ),212VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL );213214memset(&osvi, 0, sizeof(osvi));215osvi.dwOSVersionInfoSize = sizeof( osvi );216osvi.dwMajorVersion = wMajorVersion;217osvi.dwMinorVersion = wMinorVersion;218osvi.wServicePackMajor = wServicePackMajor;219220return VerifyVersionInfoW(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR, dwlConditionMask) != FALSE;221}222223static hid_device *new_hid_device(void)224{225hid_device *dev = (hid_device*) calloc(1, sizeof(hid_device));226227if (dev == NULL) {228return NULL;229}230231dev->device_handle = INVALID_HANDLE_VALUE;232dev->blocking = TRUE;233dev->output_report_length = 0;234dev->write_buf = NULL;235dev->input_report_length = 0;236dev->feature_report_length = 0;237dev->feature_buf = NULL;238dev->last_error_str = NULL;239dev->read_pending = FALSE;240dev->read_buf = NULL;241memset(&dev->ol, 0, sizeof(dev->ol));242dev->ol.hEvent = CreateEvent(NULL, FALSE, FALSE /*initial state f=nonsignaled*/, NULL);243memset(&dev->write_ol, 0, sizeof(dev->write_ol));244dev->write_ol.hEvent = CreateEvent(NULL, FALSE, FALSE /*initial state f=nonsignaled*/, NULL);245dev->device_info = NULL;246247return dev;248}249250static void free_hid_device(hid_device *dev)251{252CloseHandle(dev->ol.hEvent);253CloseHandle(dev->write_ol.hEvent);254CloseHandle(dev->device_handle);255free(dev->last_error_str);256dev->last_error_str = NULL;257free(dev->write_buf);258free(dev->feature_buf);259free(dev->read_buf);260hid_free_enumeration(dev->device_info);261free(dev);262}263264static void register_winapi_error_to_buffer(wchar_t **error_buffer, const WCHAR *op)265{266WCHAR system_err_buf[1024];267DWORD error_code = GetLastError();268269free(*error_buffer);270*error_buffer = NULL;271272#ifdef HIDAPI_USING_SDL_RUNTIME273/* Thread-safe error handling */274SDL_ClearError();275#endif276277/* Only clear out error messages if NULL is passed into op */278if (!op) {279return;280}281282DWORD system_err_len = FormatMessageW(283FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,284NULL,285error_code,286MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),287system_err_buf, ARRAYSIZE(system_err_buf),288NULL);289290DWORD op_len = (DWORD)wcslen(op);291292DWORD op_prefix_len =293op_len294+ 15 /*: (0x00000000) */295;296DWORD msg_len =297+ op_prefix_len298+ system_err_len299;300301WCHAR *msg = (WCHAR *)calloc(msg_len + 1, sizeof (WCHAR));302303if (!msg)304return;305306int 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);307308if (printf_written < 0)309{310/* Highly unlikely */311msg[0] = L'\0';312return;313}314315/* Get rid of the CR and LF that FormatMessage() sticks at the316end of the message. Thanks Microsoft! */317while(msg[msg_len-1] == L'\r' || msg[msg_len-1] == L'\n' || msg[msg_len-1] == L' ')318{319msg[msg_len-1] = L'\0';320msg_len--;321}322323#ifdef HIDAPI_USING_SDL_RUNTIME324/* Thread-safe error handling */325char *error_utf8 = SDL_iconv_wchar_utf8(msg);326if (error_utf8) {327SDL_SetError("%s", error_utf8);328SDL_free(error_utf8);329}330free(msg);331#else332*error_buffer = msg;333#endif334}335336#if defined(__GNUC__) && (__GNUC__ + (__GNUC_MINOR__ >= 6) > 4)337# pragma GCC diagnostic push338# pragma GCC diagnostic ignored "-Warray-bounds"339#endif340/* A bug in GCC/mingw gives:341* error: array subscript 0 is outside array bounds of 'wchar_t *[0]' {aka 'short unsigned int *[]'} [-Werror=array-bounds]342* | free(*error_buffer);343* Which doesn't make sense in this context. */344345static void register_string_error_to_buffer(wchar_t **error_buffer, const WCHAR *string_error)346{347free(*error_buffer);348*error_buffer = NULL;349350#ifdef HIDAPI_USING_SDL_RUNTIME351/* Thread-safe error handling */352char *error_utf8 = string_error ? SDL_iconv_wchar_utf8(string_error) : NULL;353if (error_utf8) {354SDL_SetError("%s", error_utf8);355SDL_free(error_utf8);356} else {357SDL_ClearError();358}359#else360if (string_error) {361*error_buffer = _wcsdup(string_error);362}363#endif /* HIDAPI_USING_SDL_RUNTIME */364}365366#if defined(__GNUC__) && (__GNUC__ + (__GNUC_MINOR__ >= 6) > 4)367# pragma GCC diagnostic pop368#endif369370static void register_winapi_error(hid_device *dev, const WCHAR *op)371{372register_winapi_error_to_buffer(&dev->last_error_str, op);373}374375static void register_string_error(hid_device *dev, const WCHAR *string_error)376{377register_string_error_to_buffer(&dev->last_error_str, string_error);378}379380static wchar_t *last_global_error_str = NULL;381382static void register_global_winapi_error(const WCHAR *op)383{384register_winapi_error_to_buffer(&last_global_error_str, op);385}386387static void register_global_error(const WCHAR *string_error)388{389register_string_error_to_buffer(&last_global_error_str, string_error);390}391392static HANDLE open_device(const wchar_t *path, BOOL open_rw)393{394HANDLE handle;395DWORD desired_access = (open_rw)? (GENERIC_WRITE | GENERIC_READ): 0;396DWORD share_mode = FILE_SHARE_READ|FILE_SHARE_WRITE;397398handle = CreateFileW(path,399desired_access,400share_mode,401NULL,402OPEN_EXISTING,403FILE_FLAG_OVERLAPPED,/*FILE_ATTRIBUTE_NORMAL,*/4040);405406return handle;407}408409HID_API_EXPORT const struct hid_api_version* HID_API_CALL hid_version(void)410{411return &api_version;412}413414HID_API_EXPORT const char* HID_API_CALL hid_version_str(void)415{416return HID_API_VERSION_STR;417}418419int HID_API_EXPORT hid_init(void)420{421register_global_error(NULL);422#ifndef HIDAPI_USE_DDK423if (!hidapi_initialized) {424if (lookup_functions() < 0) {425register_global_winapi_error(L"resolve DLL functions");426return -1;427}428hidapi_initialized = TRUE;429}430#endif431return 0;432}433434int HID_API_EXPORT hid_exit(void)435{436#ifndef HIDAPI_USE_DDK437free_library_handles();438hidapi_initialized = FALSE;439#endif440register_global_error(NULL);441return 0;442}443444static void* hid_internal_get_devnode_property(DEVINST dev_node, const DEVPROPKEY* property_key, DEVPROPTYPE expected_property_type)445{446ULONG len = 0;447CONFIGRET cr;448DEVPROPTYPE property_type;449PBYTE property_value = NULL;450451cr = CM_Get_DevNode_PropertyW(dev_node, property_key, &property_type, NULL, &len, 0);452if (cr != CR_BUFFER_SMALL || property_type != expected_property_type)453return NULL;454455property_value = (PBYTE)calloc(len, sizeof(BYTE));456cr = CM_Get_DevNode_PropertyW(dev_node, property_key, &property_type, property_value, &len, 0);457if (cr != CR_SUCCESS) {458free(property_value);459return NULL;460}461462return property_value;463}464465static void* hid_internal_get_device_interface_property(const wchar_t* interface_path, const DEVPROPKEY* property_key, DEVPROPTYPE expected_property_type)466{467ULONG len = 0;468CONFIGRET cr;469DEVPROPTYPE property_type;470PBYTE property_value = NULL;471472cr = CM_Get_Device_Interface_PropertyW(interface_path, property_key, &property_type, NULL, &len, 0);473if (cr != CR_BUFFER_SMALL || property_type != expected_property_type)474return NULL;475476property_value = (PBYTE)calloc(len, sizeof(BYTE));477cr = CM_Get_Device_Interface_PropertyW(interface_path, property_key, &property_type, property_value, &len, 0);478if (cr != CR_SUCCESS) {479free(property_value);480return NULL;481}482483return property_value;484}485486static void hid_internal_towupper(wchar_t* string)487{488for (wchar_t* p = string; *p; ++p) *p = towupper(*p);489}490491static int hid_internal_extract_int_token_value(wchar_t* string, const wchar_t* token)492{493int token_value;494wchar_t* startptr, * endptr;495496startptr = wcsstr(string, token);497if (!startptr)498return -1;499500startptr += wcslen(token);501token_value = wcstol(startptr, &endptr, 16);502if (endptr == startptr)503return -1;504505return token_value;506}507508static void hid_internal_get_usb_info(struct hid_device_info* dev, DEVINST dev_node)509{510wchar_t *device_id = NULL, *hardware_ids = NULL;511512device_id = hid_internal_get_devnode_property(dev_node, &DEVPKEY_Device_InstanceId, DEVPROP_TYPE_STRING);513if (!device_id)514goto end;515516/* Normalize to upper case */517hid_internal_towupper(device_id);518519/* Check for Xbox Common Controller class (XUSB) device.520https://docs.microsoft.com/windows/win32/xinput/directinput-and-xusb-devices521https://docs.microsoft.com/windows/win32/xinput/xinput-and-directinput522*/523if (hid_internal_extract_int_token_value(device_id, L"IG_") != -1) {524/* Get devnode parent to reach out USB device. */525if (CM_Get_Parent(&dev_node, dev_node, 0) != CR_SUCCESS)526goto end;527}528529/* Get the hardware ids from devnode */530hardware_ids = hid_internal_get_devnode_property(dev_node, &DEVPKEY_Device_HardwareIds, DEVPROP_TYPE_STRING_LIST);531if (!hardware_ids)532goto end;533534/* Get additional information from USB device's Hardware ID535https://docs.microsoft.com/windows-hardware/drivers/install/standard-usb-identifiers536https://docs.microsoft.com/windows-hardware/drivers/usbcon/enumeration-of-interfaces-not-grouped-in-collections537*/538for (wchar_t* hardware_id = hardware_ids; *hardware_id; hardware_id += wcslen(hardware_id) + 1) {539/* Normalize to upper case */540hid_internal_towupper(hardware_id);541542if (dev->release_number == 0) {543/* USB_DEVICE_DESCRIPTOR.bcdDevice value. */544int release_number = hid_internal_extract_int_token_value(hardware_id, L"REV_");545if (release_number != -1) {546dev->release_number = (unsigned short)release_number;547}548}549550if (dev->interface_number == -1) {551/* USB_INTERFACE_DESCRIPTOR.bInterfaceNumber value. */552int interface_number = hid_internal_extract_int_token_value(hardware_id, L"MI_");553if (interface_number != -1) {554dev->interface_number = interface_number;555}556}557}558559/* Try to get USB device manufacturer string if not provided by HidD_GetManufacturerString. */560if (wcslen(dev->manufacturer_string) == 0) {561wchar_t* manufacturer_string = hid_internal_get_devnode_property(dev_node, &DEVPKEY_Device_Manufacturer, DEVPROP_TYPE_STRING);562if (manufacturer_string) {563free(dev->manufacturer_string);564dev->manufacturer_string = manufacturer_string;565}566}567568/* Try to get USB device serial number if not provided by HidD_GetSerialNumberString. */569if (wcslen(dev->serial_number) == 0) {570DEVINST usb_dev_node = dev_node;571if (dev->interface_number != -1) {572/* Get devnode parent to reach out composite parent USB device.573https://docs.microsoft.com/windows-hardware/drivers/usbcon/enumeration-of-the-composite-parent-device574*/575if (CM_Get_Parent(&usb_dev_node, dev_node, 0) != CR_SUCCESS)576goto end;577}578579/* Get the device id of the USB device. */580free(device_id);581device_id = hid_internal_get_devnode_property(usb_dev_node, &DEVPKEY_Device_InstanceId, DEVPROP_TYPE_STRING);582if (!device_id)583goto end;584585/* Extract substring after last '\\' of Instance ID.586For USB devices it may contain device's serial number.587https://docs.microsoft.com/windows-hardware/drivers/install/instance-ids588*/589for (wchar_t *ptr = device_id + wcslen(device_id); ptr > device_id; --ptr) {590/* Instance ID is unique only within the scope of the bus.591For USB devices it means that serial number is not available. Skip. */592if (*ptr == L'&')593break;594595if (*ptr == L'\\') {596free(dev->serial_number);597dev->serial_number = _wcsdup(ptr + 1);598break;599}600}601}602603/* If we can't get the interface number, it means that there is only one interface. */604if (dev->interface_number == -1)605dev->interface_number = 0;606607end:608free(device_id);609free(hardware_ids);610}611612/* HidD_GetProductString/HidD_GetManufacturerString/HidD_GetSerialNumberString is not working for BLE HID devices613Request this info via dev node properties instead.614https://docs.microsoft.com/answers/questions/401236/hidd-getproductstring-with-ble-hid-device.html615*/616static void hid_internal_get_ble_info(struct hid_device_info* dev, DEVINST dev_node)617{618if (wcslen(dev->manufacturer_string) == 0) {619/* Manufacturer Name String (UUID: 0x2A29) */620wchar_t* manufacturer_string = hid_internal_get_devnode_property(dev_node, (const DEVPROPKEY*)&PKEY_DeviceInterface_Bluetooth_Manufacturer, DEVPROP_TYPE_STRING);621if (manufacturer_string) {622free(dev->manufacturer_string);623dev->manufacturer_string = manufacturer_string;624}625}626627if (wcslen(dev->serial_number) == 0) {628/* Serial Number String (UUID: 0x2A25) */629wchar_t* serial_number = hid_internal_get_devnode_property(dev_node, (const DEVPROPKEY*)&PKEY_DeviceInterface_Bluetooth_DeviceAddress, DEVPROP_TYPE_STRING);630if (serial_number) {631free(dev->serial_number);632dev->serial_number = serial_number;633}634}635636if (wcslen(dev->product_string) == 0) {637/* Model Number String (UUID: 0x2A24) */638wchar_t* product_string = hid_internal_get_devnode_property(dev_node, (const DEVPROPKEY*)&PKEY_DeviceInterface_Bluetooth_ModelNumber, DEVPROP_TYPE_STRING);639if (!product_string) {640DEVINST parent_dev_node = 0;641/* Fallback: Get devnode grandparent to reach out Bluetooth LE device node */642if (CM_Get_Parent(&parent_dev_node, dev_node, 0) == CR_SUCCESS) {643/* Device Name (UUID: 0x2A00) */644product_string = hid_internal_get_devnode_property(parent_dev_node, &DEVPKEY_NAME, DEVPROP_TYPE_STRING);645}646}647648if (product_string) {649free(dev->product_string);650dev->product_string = product_string;651}652}653}654655#ifdef HIDAPI_IGNORE_DEVICE656static hid_bus_type get_bus_type(const wchar_t* interface_path)657{658wchar_t *device_id = NULL, *compatible_ids = NULL;659CONFIGRET cr;660DEVINST dev_node;661hid_bus_type bus_type = HID_API_BUS_UNKNOWN;662663/* Get the device id from interface path */664device_id = hid_internal_get_device_interface_property(interface_path, &DEVPKEY_Device_InstanceId, DEVPROP_TYPE_STRING);665if (!device_id)666goto end;667668/* Open devnode from device id */669cr = CM_Locate_DevNodeW(&dev_node, (DEVINSTID_W)device_id, CM_LOCATE_DEVNODE_NORMAL);670if (cr != CR_SUCCESS)671goto end;672673/* Get devnode parent */674cr = CM_Get_Parent(&dev_node, dev_node, 0);675if (cr != CR_SUCCESS)676goto end;677678/* Get the compatible ids from parent devnode */679compatible_ids = hid_internal_get_devnode_property(dev_node, &DEVPKEY_Device_CompatibleIds, DEVPROP_TYPE_STRING_LIST);680if (!compatible_ids)681goto end;682683/* Now we can parse parent's compatible IDs to find out the device bus type */684for (wchar_t* compatible_id = compatible_ids; *compatible_id; compatible_id += wcslen(compatible_id) + 1) {685/* Normalize to upper case */686hid_internal_towupper(compatible_id);687688/* USB devices689https://docs.microsoft.com/windows-hardware/drivers/hid/plug-and-play-support690https://docs.microsoft.com/windows-hardware/drivers/install/standard-usb-identifiers */691if (wcsstr(compatible_id, L"USB") != NULL) {692bus_type = HID_API_BUS_USB;693break;694}695696/* Bluetooth devices697https://docs.microsoft.com/windows-hardware/drivers/bluetooth/installing-a-bluetooth-device */698if (wcsstr(compatible_id, L"BTHENUM") != NULL) {699bus_type = HID_API_BUS_BLUETOOTH;700break;701}702703/* Bluetooth LE devices */704if (wcsstr(compatible_id, L"BTHLEDEVICE") != NULL) {705bus_type = HID_API_BUS_BLUETOOTH;706break;707}708709/* I2C devices710https://docs.microsoft.com/windows-hardware/drivers/hid/plug-and-play-support-and-power-management */711if (wcsstr(compatible_id, L"PNP0C50") != NULL) {712bus_type = HID_API_BUS_I2C;713break;714}715716/* SPI devices717https://docs.microsoft.com/windows-hardware/drivers/hid/plug-and-play-for-spi */718if (wcsstr(compatible_id, L"PNP0C51") != NULL) {719bus_type = HID_API_BUS_SPI;720break;721}722}723end:724free(device_id);725free(compatible_ids);726return bus_type;727}728#endif /* HIDAPI_IGNORE_DEVICE */729730/* Unfortunately, HID_API_BUS_xxx constants alone aren't enough to distinguish between BLUETOOTH and BLE */731732#define HID_API_BUS_FLAG_BLE 0x01733734typedef struct hid_internal_detect_bus_type_result_ {735DEVINST dev_node;736hid_bus_type bus_type;737unsigned int bus_flags;738} hid_internal_detect_bus_type_result;739740static hid_internal_detect_bus_type_result hid_internal_detect_bus_type(const wchar_t* interface_path)741{742wchar_t *device_id = NULL, *compatible_ids = NULL;743CONFIGRET cr;744DEVINST dev_node;745hid_internal_detect_bus_type_result result = { 0 };746747/* Get the device id from interface path */748device_id = hid_internal_get_device_interface_property(interface_path, &DEVPKEY_Device_InstanceId, DEVPROP_TYPE_STRING);749if (!device_id)750goto end;751752/* Open devnode from device id */753cr = CM_Locate_DevNodeW(&dev_node, (DEVINSTID_W)device_id, CM_LOCATE_DEVNODE_NORMAL);754if (cr != CR_SUCCESS)755goto end;756757/* Get devnode parent */758cr = CM_Get_Parent(&dev_node, dev_node, 0);759if (cr != CR_SUCCESS)760goto end;761762/* Get the compatible ids from parent devnode */763compatible_ids = hid_internal_get_devnode_property(dev_node, &DEVPKEY_Device_CompatibleIds, DEVPROP_TYPE_STRING_LIST);764if (!compatible_ids)765goto end;766767/* Now we can parse parent's compatible IDs to find out the device bus type */768for (wchar_t* compatible_id = compatible_ids; *compatible_id; compatible_id += wcslen(compatible_id) + 1) {769/* Normalize to upper case */770hid_internal_towupper(compatible_id);771772/* USB devices773https://docs.microsoft.com/windows-hardware/drivers/hid/plug-and-play-support774https://docs.microsoft.com/windows-hardware/drivers/install/standard-usb-identifiers */775if (wcsstr(compatible_id, L"USB") != NULL) {776result.bus_type = HID_API_BUS_USB;777break;778}779780/* Bluetooth devices781https://docs.microsoft.com/windows-hardware/drivers/bluetooth/installing-a-bluetooth-device */782if (wcsstr(compatible_id, L"BTHENUM") != NULL) {783result.bus_type = HID_API_BUS_BLUETOOTH;784break;785}786787/* Bluetooth LE devices */788if (wcsstr(compatible_id, L"BTHLEDEVICE") != NULL) {789result.bus_type = HID_API_BUS_BLUETOOTH;790result.bus_flags |= HID_API_BUS_FLAG_BLE;791break;792}793794/* I2C devices795https://docs.microsoft.com/windows-hardware/drivers/hid/plug-and-play-support-and-power-management */796if (wcsstr(compatible_id, L"PNP0C50") != NULL) {797result.bus_type = HID_API_BUS_I2C;798break;799}800801/* SPI devices802https://docs.microsoft.com/windows-hardware/drivers/hid/plug-and-play-for-spi */803if (wcsstr(compatible_id, L"PNP0C51") != NULL) {804result.bus_type = HID_API_BUS_SPI;805break;806}807}808809result.dev_node = dev_node;810811end:812free(device_id);813free(compatible_ids);814return result;815}816817static char *hid_internal_UTF16toUTF8(const wchar_t *src)818{819char *dst = NULL;820#ifdef HIDAPI_USING_SDL_RUNTIME821int len = WIN_WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, src, -1, NULL, 0, NULL, NULL);822#else823int len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, src, -1, NULL, 0, NULL, NULL);824#endif825if (len) {826dst = (char*)calloc(len, sizeof(char));827if (dst == NULL) {828return NULL;829}830#ifdef HIDAPI_USING_SDL_RUNTIME831WIN_WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, src, -1, dst, len, NULL, NULL);832#else833WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, src, -1, dst, len, NULL, NULL);834#endif835}836837return dst;838}839840static wchar_t *hid_internal_UTF8toUTF16(const char *src)841{842wchar_t *dst = NULL;843int len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, src, -1, NULL, 0);844if (len) {845dst = (wchar_t*)calloc(len, sizeof(wchar_t));846if (dst == NULL) {847return NULL;848}849MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, src, -1, dst, len);850}851852return dst;853}854855static struct hid_device_info *hid_internal_get_device_info(const wchar_t *path, HANDLE handle)856{857struct hid_device_info *dev = NULL; /* return object */858HIDD_ATTRIBUTES attrib;859PHIDP_PREPARSED_DATA pp_data = NULL;860HIDP_CAPS caps;861wchar_t string[MAX_STRING_WCHARS + 1];862ULONG len;863ULONG size;864hid_internal_detect_bus_type_result detect_bus_type_result;865866/* Create the record. */867dev = (struct hid_device_info*)calloc(1, sizeof(struct hid_device_info));868869if (dev == NULL) {870return NULL;871}872873/* Fill out the record */874dev->next = NULL;875dev->path = hid_internal_UTF16toUTF8(path);876dev->interface_number = -1;877878attrib.Size = sizeof(HIDD_ATTRIBUTES);879if (HidD_GetAttributes(handle, &attrib)) {880/* VID/PID */881dev->vendor_id = attrib.VendorID;882dev->product_id = attrib.ProductID;883884/* Release Number */885dev->release_number = attrib.VersionNumber;886}887888/* Get the Usage Page and Usage for this device. */889if (HidD_GetPreparsedData(handle, &pp_data)) {890if (HidP_GetCaps(pp_data, &caps) == HIDP_STATUS_SUCCESS) {891dev->usage_page = caps.UsagePage;892dev->usage = caps.Usage;893}894895HidD_FreePreparsedData(pp_data);896}897898/* detect bus type before reading string descriptors */899detect_bus_type_result = hid_internal_detect_bus_type(path);900dev->bus_type = detect_bus_type_result.bus_type;901902len = dev->bus_type == HID_API_BUS_USB ? MAX_STRING_WCHARS_USB : MAX_STRING_WCHARS;903string[len] = L'\0';904size = len * sizeof(wchar_t);905906/* Serial Number */907string[0] = L'\0';908HidD_GetSerialNumberString(handle, string, size);909dev->serial_number = _wcsdup(string);910911/* Manufacturer String */912string[0] = L'\0';913HidD_GetManufacturerString(handle, string, size);914dev->manufacturer_string = _wcsdup(string);915916/* Product String */917string[0] = L'\0';918HidD_GetProductString(handle, string, size);919dev->product_string = _wcsdup(string);920921/* now, the portion that depends on string descriptors */922switch (dev->bus_type) {923case HID_API_BUS_USB:924hid_internal_get_usb_info(dev, detect_bus_type_result.dev_node);925break;926927case HID_API_BUS_BLUETOOTH:928if (detect_bus_type_result.bus_flags & HID_API_BUS_FLAG_BLE)929hid_internal_get_ble_info(dev, detect_bus_type_result.dev_node);930break;931932case HID_API_BUS_UNKNOWN:933case HID_API_BUS_SPI:934case HID_API_BUS_I2C:935/* shut down -Wswitch */936break;937}938939return dev;940}941942static int hid_blacklist(unsigned short vendor_id, unsigned short product_id)943{944size_t i;945static const struct { unsigned short vid; unsigned short pid; } known_bad[] = {946{ 0x045E, 0x0822 }, /* Microsoft Precision Mouse - causes deadlock asking for device details */947{ 0x0738, 0x2217 }, /* SPEEDLINK COMPETITION PRO - turns into an Android controller when enumerated */948{ 0x0D8C, 0x0014 }, /* Sharkoon Skiller SGH2 headset - causes deadlock asking for device details */949{ 0x1532, 0x0109 }, /* Razer Lycosa Gaming keyboard - causes deadlock asking for device details */950{ 0x1532, 0x010B }, /* Razer Arctosa Gaming keyboard - causes deadlock asking for device details */951{ 0x1B1C, 0x1B3D }, /* Corsair Gaming keyboard - causes deadlock asking for device details */952{ 0x1CCF, 0x0000 } /* All Konami Amusement Devices - causes deadlock asking for device details */953};954955for (i = 0; i < (sizeof(known_bad)/sizeof(known_bad[0])); i++) {956if ((vendor_id == known_bad[i].vid) && (product_id == known_bad[i].pid || known_bad[i].pid == 0x0000)) {957return 1;958}959}960961return 0;962}963964struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id)965{966struct hid_device_info *root = NULL; /* return object */967struct hid_device_info *cur_dev = NULL;968GUID interface_class_guid;969CONFIGRET cr;970wchar_t* device_interface_list = NULL;971DWORD len;972973if (hid_init() < 0) {974/* register_global_error: global error is reset by hid_init */975return NULL;976}977978/* Retrieve HID Interface Class GUID979https://docs.microsoft.com/windows-hardware/drivers/install/guid-devinterface-hid */980HidD_GetHidGuid(&interface_class_guid);981982/* Get the list of all device interfaces belonging to the HID class. */983/* Retry in case of list was changed between calls to984CM_Get_Device_Interface_List_SizeW and CM_Get_Device_Interface_ListW */985do {986cr = CM_Get_Device_Interface_List_SizeW(&len, &interface_class_guid, NULL, CM_GET_DEVICE_INTERFACE_LIST_PRESENT);987if (cr != CR_SUCCESS) {988register_global_error(L"Failed to get size of HID device interface list");989break;990}991992if (device_interface_list != NULL) {993free(device_interface_list);994}995996device_interface_list = (wchar_t*)calloc(len, sizeof(wchar_t));997if (device_interface_list == NULL) {998register_global_error(L"Failed to allocate memory for HID device interface list");999return NULL;1000}1001cr = CM_Get_Device_Interface_ListW(&interface_class_guid, NULL, device_interface_list, len, CM_GET_DEVICE_INTERFACE_LIST_PRESENT);1002if (cr != CR_SUCCESS && cr != CR_BUFFER_SMALL) {1003register_global_error(L"Failed to get HID device interface list");1004}1005} while (cr == CR_BUFFER_SMALL);10061007if (cr != CR_SUCCESS) {1008goto end_of_function;1009}10101011/* Iterate over each device interface in the HID class, looking for the right one. */1012for (wchar_t* device_interface = device_interface_list; *device_interface; device_interface += wcslen(device_interface) + 1) {1013HANDLE device_handle = INVALID_HANDLE_VALUE;1014HIDD_ATTRIBUTES attrib;10151016/* XInput devices don't get real HID reports and are better handled by the raw input driver */1017if (wcsstr(device_interface, L"&IG_") != NULL) {1018continue;1019}10201021/* Open read-only handle to the device */1022device_handle = open_device(device_interface, FALSE);10231024/* Check validity of device_handle. */1025if (device_handle == INVALID_HANDLE_VALUE) {1026/* Unable to open the device. */1027continue;1028}10291030/* Get the Vendor ID and Product ID for this device. */1031attrib.Size = sizeof(HIDD_ATTRIBUTES);1032if (!HidD_GetAttributes(device_handle, &attrib)) {1033goto cont_close;1034}10351036#ifdef HIDAPI_IGNORE_DEVICE1037/* See if there are any devices we should skip in enumeration */1038hid_bus_type bus_type = get_bus_type(device_interface);1039PHIDP_PREPARSED_DATA pp_data = NULL;1040HIDP_CAPS caps = { 0 };1041if (HidD_GetPreparsedData(device_handle, &pp_data)) {1042HidP_GetCaps(pp_data, &caps);1043HidD_FreePreparsedData(pp_data);1044}1045if (HIDAPI_IGNORE_DEVICE(bus_type, attrib.VendorID, attrib.ProductID, caps.UsagePage, caps.Usage)) {1046goto cont_close;1047}1048#endif10491050/* Check the VID/PID to see if we should add this1051device to the enumeration list. */1052if ((vendor_id == 0x0 || attrib.VendorID == vendor_id) &&1053(product_id == 0x0 || attrib.ProductID == product_id) &&1054!hid_blacklist(attrib.VendorID, attrib.ProductID)) {10551056/* VID/PID match. Create the record. */1057struct hid_device_info *tmp = hid_internal_get_device_info(device_interface, device_handle);10581059if (tmp == NULL) {1060goto cont_close;1061}10621063if (cur_dev) {1064cur_dev->next = tmp;1065}1066else {1067root = tmp;1068}1069cur_dev = tmp;1070}10711072cont_close:1073CloseHandle(device_handle);1074}10751076if (root == NULL) {1077if (vendor_id == 0 && product_id == 0) {1078register_global_error(L"No HID devices found in the system.");1079} else {1080register_global_error(L"No HID devices with requested VID/PID found in the system.");1081}1082}10831084end_of_function:1085free(device_interface_list);10861087return root;1088}10891090void 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. */1093struct hid_device_info *d = devs;1094while (d) {1095struct hid_device_info *next = d->next;1096free(d->path);1097free(d->serial_number);1098free(d->manufacturer_string);1099free(d->product_string);1100free(d);1101d = next;1102}1103}11041105HID_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. */1108struct hid_device_info *devs, *cur_dev;1109const char *path_to_open = NULL;1110hid_device *handle = NULL;11111112/* register_global_error: global error is reset by hid_enumerate/hid_init */1113devs = hid_enumerate(vendor_id, product_id);1114if (!devs) {1115/* register_global_error: global error is already set by hid_enumerate */1116return NULL;1117}11181119cur_dev = devs;1120while (cur_dev) {1121if (cur_dev->vendor_id == vendor_id &&1122cur_dev->product_id == product_id) {1123if (serial_number) {1124if (cur_dev->serial_number && wcscmp(serial_number, cur_dev->serial_number) == 0) {1125path_to_open = cur_dev->path;1126break;1127}1128}1129else {1130path_to_open = cur_dev->path;1131break;1132}1133}1134cur_dev = cur_dev->next;1135}11361137if (path_to_open) {1138/* Open the device */1139handle = hid_open_path(path_to_open);1140} else {1141register_global_error(L"Device with requested VID/PID/(SerialNumber) not found");1142}11431144hid_free_enumeration(devs);11451146return handle;1147}11481149HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path)1150{1151hid_device *dev = NULL;1152wchar_t* interface_path = NULL;1153HANDLE device_handle = INVALID_HANDLE_VALUE;1154PHIDP_PREPARSED_DATA pp_data = NULL;1155HIDP_CAPS caps;11561157if (hid_init() < 0) {1158/* register_global_error: global error is reset by hid_init */1159goto end_of_function;1160}11611162interface_path = hid_internal_UTF8toUTF16(path);1163if (!interface_path) {1164register_global_error(L"Path conversion failure");1165goto end_of_function;1166}11671168/* Open a handle to the device */1169device_handle = open_device(interface_path, TRUE);11701171/* Check validity of write_handle. */1172if (device_handle == INVALID_HANDLE_VALUE) {1173/* System devices, such as keyboards and mice, cannot be opened in1174read-write mode, because the system takes exclusive control over1175them. This is to prevent keyloggers. However, feature reports1176can still be sent and received. Retry opening the device, but1177without read/write access. */1178device_handle = open_device(interface_path, FALSE);11791180/* Check the validity of the limited device_handle. */1181if (device_handle == INVALID_HANDLE_VALUE) {1182register_global_winapi_error(L"open_device");1183goto end_of_function;1184}1185}11861187/* Set the Input Report buffer size to 64 reports. */1188if (!HidD_SetNumInputBuffers(device_handle, 64)) {1189register_global_winapi_error(L"set input buffers");1190goto end_of_function;1191}11921193/* Get the Input Report length for the device. */1194if (!HidD_GetPreparsedData(device_handle, &pp_data)) {1195register_global_winapi_error(L"get preparsed data");1196goto end_of_function;1197}11981199if (HidP_GetCaps(pp_data, &caps) != HIDP_STATUS_SUCCESS) {1200register_global_error(L"HidP_GetCaps");1201goto end_of_function;1202}12031204dev = new_hid_device();12051206if (dev == NULL) {1207register_global_error(L"hid_device allocation error");1208goto end_of_function;1209}12101211dev->device_handle = device_handle;1212device_handle = INVALID_HANDLE_VALUE;12131214dev->output_report_length = caps.OutputReportByteLength;1215dev->input_report_length = caps.InputReportByteLength;1216dev->feature_report_length = caps.FeatureReportByteLength;1217dev->read_buf = (char*) malloc(dev->input_report_length);1218dev->device_info = hid_internal_get_device_info(interface_path, dev->device_handle);12191220/* On Windows 7, we need to use hid_write_output_report() over Bluetooth */1221if (dev->output_report_length > 512) {1222dev->use_hid_write_output_report = !IsWindowsVersionOrGreater( HIBYTE( _WIN32_WINNT_WIN8 ), LOBYTE( _WIN32_WINNT_WIN8 ), 0 );1223}12241225end_of_function:1226free(interface_path);1227CloseHandle(device_handle);12281229if (pp_data) {1230HidD_FreePreparsedData(pp_data);1231}12321233return dev;1234}12351236static int hid_write_output_report(hid_device *dev, const unsigned char *data, size_t length)1237{1238BOOL res;1239res = HidD_SetOutputReport(dev->device_handle, (void *)data, (ULONG)length);1240if (res)1241return (int)length;1242else1243return -1;1244}12451246int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char *data, size_t length)1247{1248DWORD bytes_written = 0;1249int function_result = -1;1250BOOL res;1251BOOL overlapped = FALSE;12521253unsigned char *buf;12541255if (!data || !length) {1256register_string_error(dev, L"Zero buffer/length");1257return function_result;1258}12591260register_string_error(dev, NULL);12611262if (dev->use_hid_write_output_report) {1263return hid_write_output_report(dev, data, length);1264}12651266/* Make sure the right number of bytes are passed to WriteFile. Windows1267expects the number of bytes which are in the _longest_ report (plus1268one for the report number) bytes even if the data is a report1269which is shorter than that. Windows gives us this value in1270caps.OutputReportByteLength. If a user passes in fewer bytes than this,1271use cached temporary buffer which is the proper size. */1272if (length >= dev->output_report_length) {1273/* The user passed the right number of bytes. Use the buffer as-is. */1274buf = (unsigned char *) data;1275} else {1276if (dev->write_buf == NULL)1277dev->write_buf = (unsigned char *) malloc(dev->output_report_length);1278buf = dev->write_buf;1279memcpy(buf, data, length);1280memset(buf + length, 0, dev->output_report_length - length);1281length = dev->output_report_length;1282}12831284res = WriteFile(dev->device_handle, buf, (DWORD) length, &bytes_written, &dev->write_ol);12851286if (!res) {1287if (GetLastError() != ERROR_IO_PENDING) {1288/* WriteFile() failed. Return error. */1289register_winapi_error(dev, L"WriteFile");1290goto end_of_function;1291}1292overlapped = TRUE;1293} else {1294/* WriteFile() succeeded synchronously. */1295function_result = bytes_written;1296}12971298if (overlapped) {1299/* Wait for the transaction to complete. This makes1300hid_write() synchronous. */1301res = WaitForSingleObject(dev->write_ol.hEvent, 1000);1302if (res != WAIT_OBJECT_0) {1303/* There was a Timeout. */1304register_winapi_error(dev, L"hid_write/WaitForSingleObject");1305goto end_of_function;1306}13071308/* Get the result. */1309res = GetOverlappedResult(dev->device_handle, &dev->write_ol, &bytes_written, FALSE/*wait*/);1310if (res) {1311function_result = bytes_written;1312}1313else {1314/* The Write operation failed. */1315register_winapi_error(dev, L"hid_write/GetOverlappedResult");1316goto end_of_function;1317}1318}13191320end_of_function:1321return function_result;1322}132313241325int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds)1326{1327DWORD bytes_read = 0;1328size_t copy_len = 0;1329BOOL res = FALSE;1330BOOL overlapped = FALSE;13311332if (!data || !length) {1333register_string_error(dev, L"Zero buffer/length");1334return -1;1335}13361337register_string_error(dev, NULL);13381339/* Copy the handle for convenience. */1340HANDLE ev = dev->ol.hEvent;13411342if (!dev->read_pending) {1343/* Start an Overlapped I/O read. */1344dev->read_pending = TRUE;1345memset(dev->read_buf, 0, dev->input_report_length);1346ResetEvent(ev);1347res = ReadFile(dev->device_handle, dev->read_buf, (DWORD) dev->input_report_length, &bytes_read, &dev->ol);13481349if (!res) {1350if (GetLastError() != ERROR_IO_PENDING) {1351/* ReadFile() has failed.1352Clean up and return error. */1353register_winapi_error(dev, L"ReadFile");1354CancelIo(dev->device_handle);1355dev->read_pending = FALSE;1356goto end_of_function;1357}1358overlapped = TRUE;1359}1360}1361else {1362overlapped = TRUE;1363}13641365if (overlapped) {1366/* See if there is any data yet. */1367res = WaitForSingleObject(ev, milliseconds >= 0 ? (DWORD)milliseconds : INFINITE);1368if (res != WAIT_OBJECT_0) {1369/* There was no data this time. Return zero bytes available,1370but leave the Overlapped I/O running. */1371return 0;1372}13731374/* Get the number of bytes read. The actual data has been copied to the data[]1375array which was passed to ReadFile(). We must not wait here because we've1376already waited on our event above, and since it's auto-reset, it will have1377been reset back to unsignalled by now. */1378res = 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. */1381dev->read_pending = FALSE;13821383if (res && bytes_read > 0) {1384if (dev->read_buf[0] == 0x0) {1385/* If report numbers aren't being used, but Windows sticks a report1386number (0x0) on the beginning of the report anyway. To make this1387work like the other platforms, and to make it work more like the1388HID spec, we'll skip over this byte. */1389bytes_read--;1390copy_len = length > bytes_read ? bytes_read : length;1391memcpy(data, dev->read_buf+1, copy_len);1392}1393else {1394/* Copy the whole buffer, report number and all. */1395copy_len = length > bytes_read ? bytes_read : length;1396memcpy(data, dev->read_buf, copy_len);1397}1398}1399if (!res) {1400register_winapi_error(dev, L"hid_read_timeout/GetOverlappedResult");1401}14021403end_of_function:1404if (!res) {1405return -1;1406}14071408return (int) copy_len;1409}14101411int HID_API_EXPORT HID_API_CALL hid_read(hid_device *dev, unsigned char *data, size_t length)1412{1413return hid_read_timeout(dev, data, length, (dev->blocking)? -1: 0);1414}14151416int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *dev, int nonblock)1417{1418dev->blocking = !nonblock;1419return 0; /* Success */1420}14211422int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length)1423{1424BOOL res = FALSE;1425unsigned char *buf;1426size_t length_to_send;14271428if (!data || !length) {1429register_string_error(dev, L"Zero buffer/length");1430return -1;1431}14321433register_string_error(dev, NULL);14341435/* Windows expects at least caps.FeatureReportByteLength bytes passed1436to HidD_SetFeature(), even if the report is shorter. Any less sent and1437the function fails with error ERROR_INVALID_PARAMETER set. Any more1438and HidD_SetFeature() silently truncates the data sent in the report1439to caps.FeatureReportByteLength. */1440if (length >= dev->feature_report_length) {1441buf = (unsigned char *) data;1442length_to_send = length;1443} else {1444if (dev->feature_buf == NULL)1445dev->feature_buf = (unsigned char *) malloc(dev->feature_report_length);1446buf = dev->feature_buf;1447memcpy(buf, data, length);1448memset(buf + length, 0, dev->feature_report_length - length);1449length_to_send = dev->feature_report_length;1450}14511452res = HidD_SetFeature(dev->device_handle, (PVOID)buf, (DWORD) length_to_send);14531454if (!res) {1455register_winapi_error(dev, L"HidD_SetFeature");1456return -1;1457}14581459return (int) length;1460}14611462static int hid_get_report(hid_device *dev, DWORD report_type, unsigned char *data, size_t length)1463{1464BOOL res;1465DWORD bytes_returned = 0;14661467OVERLAPPED ol;1468memset(&ol, 0, sizeof(ol));14691470if (!data || !length) {1471register_string_error(dev, L"Zero buffer/length");1472return -1;1473}14741475register_string_error(dev, NULL);14761477res = DeviceIoControl(dev->device_handle,1478report_type,1479data, (DWORD) length,1480data, (DWORD) length,1481&bytes_returned, &ol);14821483if (!res) {1484if (GetLastError() != ERROR_IO_PENDING) {1485/* DeviceIoControl() failed. Return error. */1486register_winapi_error(dev, L"Get Input/Feature Report DeviceIoControl");1487return -1;1488}1489}14901491/* Wait here until the write is done. This makes1492hid_get_feature_report() synchronous. */1493res = GetOverlappedResult(dev->device_handle, &ol, &bytes_returned, TRUE/*wait*/);1494if (!res) {1495/* The operation failed. */1496register_winapi_error(dev, L"Get Input/Feature Report GetOverLappedResult");1497return -1;1498}14991500/* When numbered reports aren't used,1501bytes_returned seem to include only what is actually received from the device1502(not including the first byte with 0, as an indication "no numbered reports"). */1503if (data[0] == 0x0) {1504bytes_returned++;1505}15061507return bytes_returned;1508}15091510int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length)1511{1512/* We could use HidD_GetFeature() instead, but it doesn't give us an actual length, unfortunately */1513return hid_get_report(dev, IOCTL_HID_GET_FEATURE, data, length);1514}15151516int HID_API_EXPORT HID_API_CALL hid_get_input_report(hid_device *dev, unsigned char *data, size_t length)1517{1518/* We could use HidD_GetInputReport() instead, but it doesn't give us an actual length, unfortunately */1519return hid_get_report(dev, IOCTL_HID_GET_INPUT_REPORT, data, length);1520}15211522#if defined(__GNUC__) && __GNUC__ >= 81523# pragma GCC diagnostic push1524# pragma GCC diagnostic ignored "-Wcast-function-type"1525#endif1526void HID_API_EXPORT HID_API_CALL hid_close(hid_device *dev)1527{1528typedef BOOL (WINAPI *CancelIoEx_t)(HANDLE hFile, LPOVERLAPPED lpOverlapped);1529CancelIoEx_t CancelIoExFunc = (CancelIoEx_t)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "CancelIoEx");15301531if (!dev)1532return;15331534if (CancelIoExFunc) {1535CancelIoExFunc(dev->device_handle, NULL);1536} else {1537/* Windows XP, this will only cancel I/O on the current thread */1538CancelIo(dev->device_handle);1539}1540if (dev->read_pending) {1541DWORD bytes_read = 0;1542GetOverlappedResult(dev->device_handle, &dev->ol, &bytes_read, TRUE/*wait*/);1543}1544free_hid_device(dev);1545}1546#if defined(__GNUC__) && __GNUC__ >= 81547# pragma GCC diagnostic pop1548#endif15491550int HID_API_EXPORT_CALL HID_API_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen)1551{1552if (!string || !maxlen) {1553register_string_error(dev, L"Zero buffer/length");1554return -1;1555}15561557if (!dev->device_info) {1558register_string_error(dev, L"NULL device info");1559return -1;1560}15611562wcsncpy(string, dev->device_info->manufacturer_string, maxlen);1563string[maxlen - 1] = L'\0';15641565register_string_error(dev, NULL);15661567return 0;1568}15691570int HID_API_EXPORT_CALL HID_API_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen)1571{1572if (!string || !maxlen) {1573register_string_error(dev, L"Zero buffer/length");1574return -1;1575}15761577if (!dev->device_info) {1578register_string_error(dev, L"NULL device info");1579return -1;1580}15811582wcsncpy(string, dev->device_info->product_string, maxlen);1583string[maxlen - 1] = L'\0';15841585register_string_error(dev, NULL);15861587return 0;1588}15891590int HID_API_EXPORT_CALL HID_API_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen)1591{1592if (!string || !maxlen) {1593register_string_error(dev, L"Zero buffer/length");1594return -1;1595}15961597if (!dev->device_info) {1598register_string_error(dev, L"NULL device info");1599return -1;1600}16011602wcsncpy(string, dev->device_info->serial_number, maxlen);1603string[maxlen - 1] = L'\0';16041605register_string_error(dev, NULL);16061607return 0;1608}16091610HID_API_EXPORT struct hid_device_info * HID_API_CALL hid_get_device_info(hid_device *dev) {1611if (!dev->device_info)1612{1613register_string_error(dev, L"NULL device info");1614return NULL;1615}16161617return dev->device_info;1618}16191620int HID_API_EXPORT_CALL HID_API_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen)1621{1622BOOL res;16231624if (dev->device_info && dev->device_info->bus_type == HID_API_BUS_USB && maxlen > MAX_STRING_WCHARS_USB) {1625string[MAX_STRING_WCHARS_USB] = L'\0';1626maxlen = MAX_STRING_WCHARS_USB;1627}16281629res = HidD_GetIndexedString(dev->device_handle, string_index, string, (ULONG)maxlen * sizeof(wchar_t));1630if (!res) {1631register_winapi_error(dev, L"HidD_GetIndexedString");1632return -1;1633}16341635register_string_error(dev, NULL);16361637return 0;1638}16391640int HID_API_EXPORT_CALL hid_winapi_get_container_id(hid_device *dev, GUID *container_id)1641{1642wchar_t *interface_path = NULL, *device_id = NULL;1643CONFIGRET cr = CR_FAILURE;1644DEVINST dev_node;1645DEVPROPTYPE property_type;1646ULONG len;16471648if (!container_id) {1649register_string_error(dev, L"Invalid Container ID");1650return -1;1651}16521653register_string_error(dev, NULL);16541655interface_path = hid_internal_UTF8toUTF16(dev->device_info->path);1656if (!interface_path) {1657register_string_error(dev, L"Path conversion failure");1658goto end;1659}16601661/* Get the device id from interface path */1662device_id = hid_internal_get_device_interface_property(interface_path, &DEVPKEY_Device_InstanceId, DEVPROP_TYPE_STRING);1663if (!device_id) {1664register_string_error(dev, L"Failed to get device interface property InstanceId");1665goto end;1666}16671668/* Open devnode from device id */1669cr = CM_Locate_DevNodeW(&dev_node, (DEVINSTID_W)device_id, CM_LOCATE_DEVNODE_NORMAL);1670if (cr != CR_SUCCESS) {1671register_string_error(dev, L"Failed to locate device node");1672goto end;1673}16741675/* Get the container id from devnode */1676len = sizeof(*container_id);1677cr = CM_Get_DevNode_PropertyW(dev_node, &DEVPKEY_Device_ContainerId, &property_type, (PBYTE)container_id, &len, 0);1678if (cr == CR_SUCCESS && property_type != DEVPROP_TYPE_GUID)1679cr = CR_FAILURE;16801681if (cr != CR_SUCCESS)1682register_string_error(dev, L"Failed to read ContainerId property from device node");16831684end:1685free(interface_path);1686free(device_id);16871688return cr == CR_SUCCESS ? 0 : -1;1689}169016911692int HID_API_EXPORT_CALL hid_get_report_descriptor(hid_device *dev, unsigned char *buf, size_t buf_size)1693{1694PHIDP_PREPARSED_DATA pp_data = NULL;16951696if (!HidD_GetPreparsedData(dev->device_handle, &pp_data) || pp_data == NULL) {1697register_string_error(dev, L"HidD_GetPreparsedData");1698return -1;1699}17001701int res = hid_winapi_descriptor_reconstruct_pp_data(pp_data, buf, buf_size);17021703HidD_FreePreparsedData(pp_data);17041705return res;1706}17071708HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev)1709{1710if (dev) {1711if (dev->last_error_str == NULL)1712return L"Success";1713return (wchar_t*)dev->last_error_str;1714}17151716if (last_global_error_str == NULL)1717return L"Success";1718return last_global_error_str;1719}17201721#ifndef hidapi_winapi_EXPORTS1722#include "hidapi_descriptor_reconstruct.c"1723#endif17241725#ifdef __cplusplus1726} /* extern "C" */1727#endif172817291730