#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
#define _CRT_SECURE_NO_WARNINGS
#endif
#include <windows.h>
#ifndef _NTDEF_
typedef LONG NTSTATUS;
#endif
#ifdef __MINGW32__
#include <ntdef.h>
#include <winbase.h>
#endif
#ifdef __CYGWIN__
#include <ntdef.h>
#define _wcsdup wcsdup
#endif
#define MAX_STRING_WCHARS 0xFFF
#ifdef __cplusplus
extern "C" {
#endif
#include <setupapi.h>
#include <winioctl.h>
#ifdef HIDAPI_USE_DDK
#include <hidsdi.h>
#endif
#define HID_OUT_CTL_CODE(id) \
CTL_CODE(FILE_DEVICE_KEYBOARD, (id), METHOD_OUT_DIRECT, FILE_ANY_ACCESS)
#define IOCTL_HID_GET_FEATURE HID_OUT_CTL_CODE(100)
#define IOCTL_HID_GET_INPUT_REPORT HID_OUT_CTL_CODE(104)
#ifdef __cplusplus
}
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wctype.h>
#include "hidapi.h"
#undef MIN
#define MIN(x,y) ((x) < (y)? (x): (y))
#ifdef __cplusplus
extern "C" {
#endif
static struct hid_api_version api_version = {
.major = HID_API_VERSION_MAJOR,
.minor = HID_API_VERSION_MINOR,
.patch = HID_API_VERSION_PATCH
};
#ifndef HIDAPI_USE_DDK
typedef struct _HIDD_ATTRIBUTES{
ULONG Size;
USHORT VendorID;
USHORT ProductID;
USHORT VersionNumber;
} HIDD_ATTRIBUTES, *PHIDD_ATTRIBUTES;
typedef USHORT USAGE;
typedef struct _HIDP_CAPS {
USAGE Usage;
USAGE UsagePage;
USHORT InputReportByteLength;
USHORT OutputReportByteLength;
USHORT FeatureReportByteLength;
USHORT Reserved[17];
USHORT fields_not_used_by_hidapi[10];
} HIDP_CAPS, *PHIDP_CAPS;
typedef void* PHIDP_PREPARSED_DATA;
#define HIDP_STATUS_SUCCESS 0x110000
typedef void (__stdcall *HidD_GetHidGuid_)(LPGUID hid_guid);
typedef BOOLEAN (__stdcall *HidD_GetAttributes_)(HANDLE device, PHIDD_ATTRIBUTES attrib);
typedef BOOLEAN (__stdcall *HidD_GetSerialNumberString_)(HANDLE device, PVOID buffer, ULONG buffer_len);
typedef BOOLEAN (__stdcall *HidD_GetManufacturerString_)(HANDLE handle, PVOID buffer, ULONG buffer_len);
typedef BOOLEAN (__stdcall *HidD_GetProductString_)(HANDLE handle, PVOID buffer, ULONG buffer_len);
typedef BOOLEAN (__stdcall *HidD_SetFeature_)(HANDLE handle, PVOID data, ULONG length);
typedef BOOLEAN (__stdcall *HidD_GetFeature_)(HANDLE handle, PVOID data, ULONG length);
typedef BOOLEAN (__stdcall *HidD_GetInputReport_)(HANDLE handle, PVOID data, ULONG length);
typedef BOOLEAN (__stdcall *HidD_GetIndexedString_)(HANDLE handle, ULONG string_index, PVOID buffer, ULONG buffer_len);
typedef BOOLEAN (__stdcall *HidD_GetPreparsedData_)(HANDLE handle, PHIDP_PREPARSED_DATA *preparsed_data);
typedef BOOLEAN (__stdcall *HidD_FreePreparsedData_)(PHIDP_PREPARSED_DATA preparsed_data);
typedef NTSTATUS (__stdcall *HidP_GetCaps_)(PHIDP_PREPARSED_DATA preparsed_data, HIDP_CAPS *caps);
typedef BOOLEAN (__stdcall *HidD_SetNumInputBuffers_)(HANDLE handle, ULONG number_buffers);
static HidD_GetHidGuid_ HidD_GetHidGuid;
static HidD_GetAttributes_ HidD_GetAttributes;
static HidD_GetSerialNumberString_ HidD_GetSerialNumberString;
static HidD_GetManufacturerString_ HidD_GetManufacturerString;
static HidD_GetProductString_ HidD_GetProductString;
static HidD_SetFeature_ HidD_SetFeature;
static HidD_GetFeature_ HidD_GetFeature;
static HidD_GetInputReport_ HidD_GetInputReport;
static HidD_GetIndexedString_ HidD_GetIndexedString;
static HidD_GetPreparsedData_ HidD_GetPreparsedData;
static HidD_FreePreparsedData_ HidD_FreePreparsedData;
static HidP_GetCaps_ HidP_GetCaps;
static HidD_SetNumInputBuffers_ HidD_SetNumInputBuffers;
static HMODULE lib_handle = NULL;
static BOOLEAN initialized = FALSE;
typedef DWORD RETURN_TYPE;
typedef RETURN_TYPE CONFIGRET;
typedef DWORD DEVNODE, DEVINST;
typedef DEVNODE* PDEVNODE, * PDEVINST;
typedef WCHAR* DEVNODEID_W, * DEVINSTID_W;
#define CR_SUCCESS (0x00000000)
#define CR_BUFFER_SMALL (0x0000001A)
#define CM_LOCATE_DEVNODE_NORMAL 0x00000000
#define DEVPROP_TYPEMOD_LIST 0x00002000
#define DEVPROP_TYPE_STRING 0x00000012
#define DEVPROP_TYPE_STRING_LIST (DEVPROP_TYPE_STRING|DEVPROP_TYPEMOD_LIST)
typedef CONFIGRET(__stdcall* CM_Locate_DevNodeW_)(PDEVINST pdnDevInst, DEVINSTID_W pDeviceID, ULONG ulFlags);
typedef CONFIGRET(__stdcall* CM_Get_Parent_)(PDEVINST pdnDevInst, DEVINST dnDevInst, ULONG ulFlags);
typedef CONFIGRET(__stdcall* CM_Get_DevNode_PropertyW_)(DEVINST dnDevInst, CONST DEVPROPKEY* PropertyKey, DEVPROPTYPE* PropertyType, PBYTE PropertyBuffer, PULONG PropertyBufferSize, ULONG ulFlags);
typedef CONFIGRET(__stdcall* CM_Get_Device_Interface_PropertyW_)(LPCWSTR pszDeviceInterface, CONST DEVPROPKEY* PropertyKey, DEVPROPTYPE* PropertyType, PBYTE PropertyBuffer, PULONG PropertyBufferSize, ULONG ulFlags);
static CM_Locate_DevNodeW_ CM_Locate_DevNodeW = NULL;
static CM_Get_Parent_ CM_Get_Parent = NULL;
static CM_Get_DevNode_PropertyW_ CM_Get_DevNode_PropertyW = NULL;
static CM_Get_Device_Interface_PropertyW_ CM_Get_Device_Interface_PropertyW = NULL;
static HMODULE cfgmgr32_lib_handle = NULL;
#endif
struct hid_device_ {
HANDLE device_handle;
BOOL blocking;
USHORT output_report_length;
unsigned char *write_buf;
size_t input_report_length;
USHORT feature_report_length;
unsigned char *feature_buf;
void *last_error_str;
DWORD last_error_num;
BOOL read_pending;
char *read_buf;
OVERLAPPED ol;
OVERLAPPED write_ol;
struct hid_device_info* device_info;
};
static hid_device *new_hid_device()
{
hid_device *dev = (hid_device*) calloc(1, sizeof(hid_device));
dev->device_handle = INVALID_HANDLE_VALUE;
dev->blocking = TRUE;
dev->output_report_length = 0;
dev->write_buf = NULL;
dev->input_report_length = 0;
dev->feature_report_length = 0;
dev->feature_buf = NULL;
dev->last_error_str = NULL;
dev->last_error_num = 0;
dev->read_pending = FALSE;
dev->read_buf = NULL;
memset(&dev->ol, 0, sizeof(dev->ol));
dev->ol.hEvent = CreateEvent(NULL, FALSE, FALSE , NULL);
memset(&dev->write_ol, 0, sizeof(dev->write_ol));
dev->write_ol.hEvent = CreateEvent(NULL, FALSE, FALSE , NULL);
dev->device_info = NULL;
return dev;
}
static void free_hid_device(hid_device *dev)
{
CloseHandle(dev->ol.hEvent);
CloseHandle(dev->write_ol.hEvent);
CloseHandle(dev->device_handle);
LocalFree(dev->last_error_str);
free(dev->write_buf);
free(dev->feature_buf);
free(dev->read_buf);
free(dev->device_info);
free(dev);
}
static void register_error(hid_device *dev, const char *op)
{
WCHAR *ptr, *msg;
(void)op; FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPWSTR)&msg, 0,
NULL);
ptr = msg;
while (*ptr) {
if (*ptr == L'\r') {
*ptr = L'\0';
break;
}
ptr++;
}
LocalFree(dev->last_error_str);
dev->last_error_str = msg;
}
#ifndef HIDAPI_USE_DDK
static int lookup_functions()
{
lib_handle = LoadLibraryA("hid.dll");
if (lib_handle) {
#if defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wcast-function-type"
#endif
#define RESOLVE(x) x = (x##_)GetProcAddress(lib_handle, #x); if (!x) return -1;
RESOLVE(HidD_GetHidGuid);
RESOLVE(HidD_GetAttributes);
RESOLVE(HidD_GetSerialNumberString);
RESOLVE(HidD_GetManufacturerString);
RESOLVE(HidD_GetProductString);
RESOLVE(HidD_SetFeature);
RESOLVE(HidD_GetFeature);
RESOLVE(HidD_GetInputReport);
RESOLVE(HidD_GetIndexedString);
RESOLVE(HidD_GetPreparsedData);
RESOLVE(HidD_FreePreparsedData);
RESOLVE(HidP_GetCaps);
RESOLVE(HidD_SetNumInputBuffers);
#undef RESOLVE
#if defined(__GNUC__)
# pragma GCC diagnostic pop
#endif
}
else
return -1;
cfgmgr32_lib_handle = LoadLibraryA("cfgmgr32.dll");
if (cfgmgr32_lib_handle) {
#if defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wcast-function-type"
#endif
#define RESOLVE(x) x = (x##_)GetProcAddress(cfgmgr32_lib_handle, #x);
RESOLVE(CM_Locate_DevNodeW);
RESOLVE(CM_Get_Parent);
RESOLVE(CM_Get_DevNode_PropertyW);
RESOLVE(CM_Get_Device_Interface_PropertyW);
#undef RESOLVE
#if defined(__GNUC__)
# pragma GCC diagnostic pop
#endif
}
else {
CM_Locate_DevNodeW = NULL;
CM_Get_Parent = NULL;
CM_Get_DevNode_PropertyW = NULL;
CM_Get_Device_Interface_PropertyW = NULL;
}
return 0;
}
#endif
static HANDLE open_device(const char *path, BOOL open_rw)
{
HANDLE handle;
DWORD desired_access = (open_rw)? (GENERIC_WRITE | GENERIC_READ): 0;
DWORD share_mode = FILE_SHARE_READ|FILE_SHARE_WRITE;
handle = CreateFileA(path,
desired_access,
share_mode,
NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
0);
return handle;
}
HID_API_EXPORT const struct hid_api_version* HID_API_CALL hid_version()
{
return &api_version;
}
HID_API_EXPORT const char* HID_API_CALL hid_version_str()
{
return HID_API_VERSION_STR;
}
int HID_API_EXPORT hid_init(void)
{
#ifndef HIDAPI_USE_DDK
if (!initialized) {
if (lookup_functions() < 0) {
hid_exit();
return -1;
}
initialized = TRUE;
}
#endif
return 0;
}
int HID_API_EXPORT hid_exit(void)
{
#ifndef HIDAPI_USE_DDK
if (lib_handle)
FreeLibrary(lib_handle);
lib_handle = NULL;
if (cfgmgr32_lib_handle)
FreeLibrary(cfgmgr32_lib_handle);
cfgmgr32_lib_handle = NULL;
initialized = FALSE;
#endif
return 0;
}
static void hid_internal_get_ble_info(struct hid_device_info* dev, DEVINST dev_node)
{
ULONG len;
CONFIGRET cr;
DEVPROPTYPE property_type;
static DEVPROPKEY DEVPKEY_NAME = { { 0xb725f130, 0x47ef, 0x101a, 0xa5, 0xf1, 0x02, 0x60, 0x8c, 0x9e, 0xeb, 0xac }, 10 }; static DEVPROPKEY PKEY_DeviceInterface_Bluetooth_DeviceAddress = { { 0x2BD67D8B, 0x8BEB, 0x48D5, 0x87, 0xE0, 0x6C, 0xDA, 0x34, 0x28, 0x04, 0x0A }, 1 }; static DEVPROPKEY PKEY_DeviceInterface_Bluetooth_Manufacturer = { { 0x2BD67D8B, 0x8BEB, 0x48D5, 0x87, 0xE0, 0x6C, 0xDA, 0x34, 0x28, 0x04, 0x0A }, 4 };
len = 0;
cr = CM_Get_DevNode_PropertyW(dev_node, &PKEY_DeviceInterface_Bluetooth_Manufacturer, &property_type, NULL, &len, 0);
if (cr == CR_BUFFER_SMALL && property_type == DEVPROP_TYPE_STRING) {
free(dev->manufacturer_string);
dev->manufacturer_string = (wchar_t*)calloc(len, sizeof(BYTE));
CM_Get_DevNode_PropertyW(dev_node, &PKEY_DeviceInterface_Bluetooth_Manufacturer, &property_type, (PBYTE)dev->manufacturer_string, &len, 0);
}
len = 0;
cr = CM_Get_DevNode_PropertyW(dev_node, &PKEY_DeviceInterface_Bluetooth_DeviceAddress, &property_type, NULL, &len, 0);
if (cr == CR_BUFFER_SMALL && property_type == DEVPROP_TYPE_STRING) {
free(dev->serial_number);
dev->serial_number = (wchar_t*)calloc(len, sizeof(BYTE));
CM_Get_DevNode_PropertyW(dev_node, &PKEY_DeviceInterface_Bluetooth_DeviceAddress, &property_type, (PBYTE)dev->serial_number, &len, 0);
}
cr = CM_Get_Parent(&dev_node, dev_node, 0);
if (cr != CR_SUCCESS)
return;
len = 0;
cr = CM_Get_DevNode_PropertyW(dev_node, &DEVPKEY_NAME, &property_type, NULL, &len, 0);
if (cr == CR_BUFFER_SMALL && property_type == DEVPROP_TYPE_STRING) {
free(dev->product_string);
dev->product_string = (wchar_t*)calloc(len, sizeof(BYTE));
CM_Get_DevNode_PropertyW(dev_node, &DEVPKEY_NAME, &property_type, (PBYTE)dev->product_string, &len, 0);
}
}
static void hid_internal_get_info(struct hid_device_info* dev)
{
const char *tmp = NULL;
wchar_t *interface_path = NULL, *device_id = NULL, *compatible_ids = NULL;
mbstate_t state;
ULONG len;
CONFIGRET cr;
DEVPROPTYPE property_type;
DEVINST dev_node;
static DEVPROPKEY DEVPKEY_Device_InstanceId = { { 0x78c34fc8, 0x104a, 0x4aca, 0x9e, 0xa4, 0x52, 0x4d, 0x52, 0x99, 0x6e, 0x57 }, 256 }; static DEVPROPKEY DEVPKEY_Device_CompatibleIds = { { 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0}, 4 };
if (!CM_Get_Device_Interface_PropertyW ||
!CM_Locate_DevNodeW ||
!CM_Get_Parent ||
!CM_Get_DevNode_PropertyW)
goto end;
tmp = dev->path;
len = (ULONG)strlen(tmp);
interface_path = (wchar_t*)calloc(len + 1, sizeof(wchar_t));
memset(&state, 0, sizeof(state));
if (mbsrtowcs(interface_path, &tmp, len, &state) == (size_t)-1)
goto end;
len = 0;
cr = CM_Get_Device_Interface_PropertyW(interface_path, &DEVPKEY_Device_InstanceId, &property_type, NULL, &len, 0);
if (cr == CR_BUFFER_SMALL && property_type == DEVPROP_TYPE_STRING) {
device_id = (wchar_t*)calloc(len, sizeof(BYTE));
cr = CM_Get_Device_Interface_PropertyW(interface_path, &DEVPKEY_Device_InstanceId, &property_type, (PBYTE)device_id, &len, 0);
}
if (cr != CR_SUCCESS)
goto end;
cr = CM_Locate_DevNodeW(&dev_node, (DEVINSTID_W)device_id, CM_LOCATE_DEVNODE_NORMAL);
if (cr != CR_SUCCESS)
goto end;
cr = CM_Get_Parent(&dev_node, dev_node, 0);
if (cr != CR_SUCCESS)
goto end;
len = 0;
cr = CM_Get_DevNode_PropertyW(dev_node, &DEVPKEY_Device_CompatibleIds, &property_type, NULL, &len, 0);
if (cr == CR_BUFFER_SMALL && property_type == DEVPROP_TYPE_STRING_LIST) {
compatible_ids = (wchar_t*)calloc(len, sizeof(BYTE));
cr = CM_Get_DevNode_PropertyW(dev_node, &DEVPKEY_Device_CompatibleIds, &property_type, (PBYTE)compatible_ids, &len, 0);
}
if (cr != CR_SUCCESS)
goto end;
for (wchar_t* compatible_id = compatible_ids; *compatible_id; compatible_id += wcslen(compatible_id) + 1) {
for (wchar_t* p = compatible_id; *p; ++p) *p = towupper(*p);
if (wcsstr(compatible_id, L"BTHLEDEVICE") != NULL) {
hid_internal_get_ble_info(dev, dev_node);
break;
}
}
end:
free(interface_path);
free(device_id);
free(compatible_ids);
}
static struct hid_device_info *hid_get_device_info(const char *path, HANDLE handle)
{
struct hid_device_info *dev = NULL;
BOOL res;
HIDD_ATTRIBUTES attrib;
PHIDP_PREPARSED_DATA pp_data = NULL;
HIDP_CAPS caps;
#define WSTR_LEN 512
wchar_t wstr[WSTR_LEN];
dev = (struct hid_device_info*)calloc(1, sizeof(struct hid_device_info));
dev->next = NULL;
if (path) {
size_t len = strlen(path);
dev->path = (char*)calloc(len + 1, sizeof(char));
memcpy(dev->path, path, len + 1);
}
else
dev->path = NULL;
attrib.Size = sizeof(HIDD_ATTRIBUTES);
res = HidD_GetAttributes(handle, &attrib);
if (res) {
dev->vendor_id = attrib.VendorID;
dev->product_id = attrib.ProductID;
dev->release_number = attrib.VersionNumber;
}
res = HidD_GetPreparsedData(handle, &pp_data);
if (res) {
NTSTATUS nt_res = HidP_GetCaps(pp_data, &caps);
if (nt_res == HIDP_STATUS_SUCCESS) {
dev->usage_page = caps.UsagePage;
dev->usage = caps.Usage;
}
HidD_FreePreparsedData(pp_data);
}
wstr[0] = L'\0';
res = HidD_GetSerialNumberString(handle, wstr, sizeof(wstr));
wstr[WSTR_LEN - 1] = L'\0';
dev->serial_number = _wcsdup(wstr);
wstr[0] = L'\0';
res = HidD_GetManufacturerString(handle, wstr, sizeof(wstr));
wstr[WSTR_LEN - 1] = L'\0';
dev->manufacturer_string = _wcsdup(wstr);
wstr[0] = L'\0';
res = HidD_GetProductString(handle, wstr, sizeof(wstr));
wstr[WSTR_LEN - 1] = L'\0';
dev->product_string = _wcsdup(wstr);
dev->interface_number = -1;
if (dev->path) {
char* interface_component = strstr(dev->path, "&mi_");
if (interface_component) {
char* hex_str = interface_component + 4;
char* endptr = NULL;
dev->interface_number = strtol(hex_str, &endptr, 16);
if (endptr == hex_str) {
dev->interface_number = -1;
}
}
}
hid_internal_get_info(dev);
return dev;
}
struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id)
{
BOOL res;
struct hid_device_info *root = NULL;
struct hid_device_info *cur_dev = NULL;
GUID interface_class_guid;
SP_DEVINFO_DATA devinfo_data;
SP_DEVICE_INTERFACE_DATA device_interface_data;
SP_DEVICE_INTERFACE_DETAIL_DATA_A *device_interface_detail_data = NULL;
HDEVINFO device_info_set = INVALID_HANDLE_VALUE;
char driver_name[256];
int device_index = 0;
if (hid_init() < 0)
return NULL;
HidD_GetHidGuid(&interface_class_guid);
memset(&devinfo_data, 0x0, sizeof(devinfo_data));
devinfo_data.cbSize = sizeof(SP_DEVINFO_DATA);
device_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
device_info_set = SetupDiGetClassDevsA(&interface_class_guid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
for (;;) {
HANDLE read_handle = INVALID_HANDLE_VALUE;
DWORD required_size = 0;
HIDD_ATTRIBUTES attrib;
res = SetupDiEnumDeviceInterfaces(device_info_set,
NULL,
&interface_class_guid,
device_index,
&device_interface_data);
if (!res) {
break;
}
res = SetupDiGetDeviceInterfaceDetailA(device_info_set,
&device_interface_data,
NULL,
0,
&required_size,
NULL);
device_interface_detail_data = (SP_DEVICE_INTERFACE_DETAIL_DATA_A*) malloc(required_size);
device_interface_detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A);
res = SetupDiGetDeviceInterfaceDetailA(device_info_set,
&device_interface_data,
device_interface_detail_data,
required_size,
NULL,
NULL);
if (!res) {
goto cont;
}
res = SetupDiEnumDeviceInfo(device_info_set, device_index, &devinfo_data);
if (!res)
goto cont;
res = SetupDiGetDeviceRegistryPropertyA(device_info_set, &devinfo_data,
SPDRP_DRIVER, NULL, (PBYTE)driver_name, sizeof(driver_name), NULL);
if (!res)
goto cont;
read_handle = open_device(device_interface_detail_data->DevicePath, FALSE);
if (read_handle == INVALID_HANDLE_VALUE) {
goto cont;
}
attrib.Size = sizeof(HIDD_ATTRIBUTES);
HidD_GetAttributes(read_handle, &attrib);
if ((vendor_id == 0x0 || attrib.VendorID == vendor_id) &&
(product_id == 0x0 || attrib.ProductID == product_id)) {
struct hid_device_info *tmp = hid_get_device_info(device_interface_detail_data->DevicePath, read_handle);
if (tmp == NULL) {
goto cont_close;
}
if (cur_dev) {
cur_dev->next = tmp;
}
else {
root = tmp;
}
cur_dev = tmp;
}
cont_close:
CloseHandle(read_handle);
cont:
free(device_interface_detail_data);
device_index++;
}
SetupDiDestroyDeviceInfoList(device_info_set);
return root;
}
void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs)
{
struct hid_device_info *d = devs;
while (d) {
struct hid_device_info *next = d->next;
free(d->path);
free(d->serial_number);
free(d->manufacturer_string);
free(d->product_string);
free(d);
d = next;
}
}
HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number)
{
struct hid_device_info *devs, *cur_dev;
const char *path_to_open = NULL;
hid_device *handle = NULL;
devs = hid_enumerate(vendor_id, product_id);
cur_dev = devs;
while (cur_dev) {
if (cur_dev->vendor_id == vendor_id &&
cur_dev->product_id == product_id) {
if (serial_number) {
if (cur_dev->serial_number && wcscmp(serial_number, cur_dev->serial_number) == 0) {
path_to_open = cur_dev->path;
break;
}
}
else {
path_to_open = cur_dev->path;
break;
}
}
cur_dev = cur_dev->next;
}
if (path_to_open) {
handle = hid_open_path(path_to_open);
}
hid_free_enumeration(devs);
return handle;
}
HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path)
{
hid_device *dev;
HIDP_CAPS caps;
PHIDP_PREPARSED_DATA pp_data = NULL;
BOOLEAN res;
NTSTATUS nt_res;
if (hid_init() < 0) {
return NULL;
}
dev = new_hid_device();
dev->device_handle = open_device(path, TRUE);
if (dev->device_handle == INVALID_HANDLE_VALUE) {
dev->device_handle = open_device(path, FALSE);
if (dev->device_handle == INVALID_HANDLE_VALUE) {
register_error(dev, "CreateFile");
goto err;
}
}
res = HidD_SetNumInputBuffers(dev->device_handle, 64);
if (!res) {
register_error(dev, "HidD_SetNumInputBuffers");
goto err;
}
res = HidD_GetPreparsedData(dev->device_handle, &pp_data);
if (!res) {
register_error(dev, "HidD_GetPreparsedData");
goto err;
}
nt_res = HidP_GetCaps(pp_data, &caps);
if (nt_res != HIDP_STATUS_SUCCESS) {
register_error(dev, "HidP_GetCaps");
goto err_pp_data;
}
dev->output_report_length = caps.OutputReportByteLength;
dev->input_report_length = caps.InputReportByteLength;
dev->feature_report_length = caps.FeatureReportByteLength;
HidD_FreePreparsedData(pp_data);
dev->read_buf = (char*) malloc(dev->input_report_length);
dev->device_info = hid_get_device_info(path, dev->device_handle);
return dev;
err_pp_data:
HidD_FreePreparsedData(pp_data);
err:
free_hid_device(dev);
return NULL;
}
int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char *data, size_t length)
{
DWORD bytes_written = 0;
int function_result = -1;
BOOL res;
BOOL overlapped = FALSE;
unsigned char *buf;
if (!data || (length==0)) {
register_error(dev, "Zero length buffer");
return function_result;
}
if (length >= dev->output_report_length) {
buf = (unsigned char *) data;
} else {
if (dev->write_buf == NULL)
dev->write_buf = (unsigned char *) malloc(dev->output_report_length);
buf = dev->write_buf;
memcpy(buf, data, length);
memset(buf + length, 0, dev->output_report_length - length);
length = dev->output_report_length;
}
res = WriteFile(dev->device_handle, buf, (DWORD) length, NULL, &dev->write_ol);
if (!res) {
if (GetLastError() != ERROR_IO_PENDING) {
register_error(dev, "WriteFile");
goto end_of_function;
}
overlapped = TRUE;
}
if (overlapped) {
res = WaitForSingleObject(dev->write_ol.hEvent, 1000);
if (res != WAIT_OBJECT_0) {
register_error(dev, "WriteFile/WaitForSingleObject Timeout");
goto end_of_function;
}
res = GetOverlappedResult(dev->device_handle, &dev->write_ol, &bytes_written, FALSE);
if (res) {
function_result = bytes_written;
}
else {
register_error(dev, "WriteFile");
goto end_of_function;
}
}
end_of_function:
return function_result;
}
int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds)
{
DWORD bytes_read = 0;
size_t copy_len = 0;
BOOL res = FALSE;
BOOL overlapped = FALSE;
HANDLE ev = dev->ol.hEvent;
if (!dev->read_pending) {
dev->read_pending = TRUE;
memset(dev->read_buf, 0, dev->input_report_length);
ResetEvent(ev);
res = ReadFile(dev->device_handle, dev->read_buf, (DWORD) dev->input_report_length, &bytes_read, &dev->ol);
if (!res) {
if (GetLastError() != ERROR_IO_PENDING) {
CancelIo(dev->device_handle);
dev->read_pending = FALSE;
goto end_of_function;
}
overlapped = TRUE;
}
}
else {
overlapped = TRUE;
}
if (overlapped) {
if (milliseconds >= 0) {
res = WaitForSingleObject(ev, milliseconds);
if (res != WAIT_OBJECT_0) {
return 0;
}
}
res = GetOverlappedResult(dev->device_handle, &dev->ol, &bytes_read, TRUE);
}
dev->read_pending = FALSE;
if (res && bytes_read > 0) {
if (dev->read_buf[0] == 0x0) {
bytes_read--;
copy_len = length > bytes_read ? bytes_read : length;
memcpy(data, dev->read_buf+1, copy_len);
}
else {
copy_len = length > bytes_read ? bytes_read : length;
memcpy(data, dev->read_buf, copy_len);
}
}
end_of_function:
if (!res) {
register_error(dev, "GetOverlappedResult");
return -1;
}
return (int) copy_len;
}
int HID_API_EXPORT HID_API_CALL hid_read(hid_device *dev, unsigned char *data, size_t length)
{
return hid_read_timeout(dev, data, length, (dev->blocking)? -1: 0);
}
int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *dev, int nonblock)
{
dev->blocking = !nonblock;
return 0;
}
int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length)
{
BOOL res = FALSE;
unsigned char *buf;
size_t length_to_send;
if (length >= dev->feature_report_length) {
buf = (unsigned char *) data;
length_to_send = length;
} else {
if (dev->feature_buf == NULL)
dev->feature_buf = (unsigned char *) malloc(dev->feature_report_length);
buf = dev->feature_buf;
memcpy(buf, data, length);
memset(buf + length, 0, dev->feature_report_length - length);
length_to_send = dev->feature_report_length;
}
res = HidD_SetFeature(dev->device_handle, (PVOID)buf, (DWORD) length_to_send);
if (!res) {
register_error(dev, "HidD_SetFeature");
return -1;
}
return (int) length;
}
static int hid_get_report(hid_device *dev, DWORD report_type, unsigned char *data, size_t length)
{
BOOL res;
DWORD bytes_returned = 0;
OVERLAPPED ol;
memset(&ol, 0, sizeof(ol));
res = DeviceIoControl(dev->device_handle,
report_type,
data, (DWORD) length,
data, (DWORD) length,
&bytes_returned, &ol);
if (!res) {
if (GetLastError() != ERROR_IO_PENDING) {
register_error(dev, "Get Input/Feature Report DeviceIoControl");
return -1;
}
}
res = GetOverlappedResult(dev->device_handle, &ol, &bytes_returned, TRUE);
if (!res) {
register_error(dev, "Get Input/Feature Report GetOverLappedResult");
return -1;
}
if (data[0] == 0x0) {
bytes_returned++;
}
return bytes_returned;
}
int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length)
{
return hid_get_report(dev, IOCTL_HID_GET_FEATURE, data, length);
}
int HID_API_EXPORT HID_API_CALL hid_get_input_report(hid_device *dev, unsigned char *data, size_t length)
{
return hid_get_report(dev, IOCTL_HID_GET_INPUT_REPORT, data, length);
}
void HID_API_EXPORT HID_API_CALL hid_close(hid_device *dev)
{
if (!dev)
return;
CancelIo(dev->device_handle);
free_hid_device(dev);
}
int HID_API_EXPORT_CALL HID_API_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen)
{
if (!dev->device_info || !string || !maxlen)
return -1;
wcsncpy(string, dev->device_info->manufacturer_string, maxlen);
string[maxlen] = L'\0';
return 0;
}
int HID_API_EXPORT_CALL HID_API_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen)
{
if (!dev->device_info || !string || !maxlen)
return -1;
wcsncpy(string, dev->device_info->product_string, maxlen);
string[maxlen] = L'\0';
return 0;
}
int HID_API_EXPORT_CALL HID_API_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen)
{
if (!dev->device_info || !string || !maxlen)
return -1;
wcsncpy(string, dev->device_info->serial_number, maxlen);
string[maxlen] = L'\0';
return 0;
}
int HID_API_EXPORT_CALL HID_API_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen)
{
BOOL res;
res = HidD_GetIndexedString(dev->device_handle, string_index, string, sizeof(wchar_t) * (DWORD) MIN(maxlen, MAX_STRING_WCHARS));
if (!res) {
register_error(dev, "HidD_GetIndexedString");
return -1;
}
return 0;
}
HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev)
{
if (dev) {
if (dev->last_error_str == NULL)
return L"Success";
return (wchar_t*)dev->last_error_str;
}
return L"hid_error for global errors is not implemented yet";
}
#define P32
#ifdef S11
unsigned short VendorID = 0xa0a0;
unsigned short ProductID = 0x0001;
#endif
#ifdef P32
unsigned short VendorID = 0x04d8;
unsigned short ProductID = 0x3f;
#endif
#ifdef PICPGM
unsigned short VendorID = 0x04d8;
unsigned short ProductID = 0x0033;
#endif
#if 0#endif
#ifdef __cplusplus
}
#endif