Index: third_party/hidapi/hidapi_win.c |
diff --git a/third_party/hidapi/hidapi_win.c b/third_party/hidapi/hidapi_win.c |
new file mode 100644 |
index 0000000000000000000000000000000000000000..1d3768fd377bd1cdd668300dfbdf352d573c9473 |
--- /dev/null |
+++ b/third_party/hidapi/hidapi_win.c |
@@ -0,0 +1,923 @@ |
+/******************************************************* |
+ HIDAPI - Multi-Platform library for |
+ communication with HID devices. |
+ |
+ Alan Ott |
+ Signal 11 Software |
+ |
+ 8/22/2009 |
+ |
+ Copyright 2009, All Rights Reserved. |
+ |
+ At the discretion of the user of this library, |
+ this software may be licensed under the terms of the |
+ GNU General Public License v3, a BSD-Style license, or the |
+ original HIDAPI license as outlined in the LICENSE.txt, |
+ LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt |
+ files located at the root of the source distribution. |
+ These files may also be found in the public source |
+ code repository located at: |
+ http://github.com/signal11/hidapi . |
+********************************************************/ |
+ |
+#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 HIDAPI_USE_DDK*/ |
+ |
+#ifdef __cplusplus |
+extern "C" { |
+#endif |
+ #include <setupapi.h> |
+ #include <winioctl.h> |
+ #ifdef HIDAPI_USE_DDK |
+ #include <hidsdi.h> |
+ #endif |
+ |
+ /* Copied from inc/ddk/hidclass.h, part of the Windows DDK. */ |
+ #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) |
+ |
+#ifdef __cplusplus |
+} /* extern "C" */ |
+#endif |
+ |
+#include <stdio.h> |
+#include <stdlib.h> |
+ |
+ |
+#include "hidapi.h" |
+ |
+#ifdef _MSC_VER |
+ /* Thanks Microsoft, but I know how to use strncpy(). */ |
+ #pragma warning(disable:4996) |
+#endif |
+ |
+#ifdef __cplusplus |
+extern "C" { |
+#endif |
+ |
+#ifndef HIDAPI_USE_DDK |
+ /* Since we're not building with the DDK, and the HID header |
+ files aren't part of the SDK, we have to define all this |
+ stuff here. In lookup_functions(), the function pointers |
+ defined below are set. */ |
+ 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 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_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); |
+ |
+ 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_GetIndexedString_ HidD_GetIndexedString; |
+ static HidD_GetPreparsedData_ HidD_GetPreparsedData; |
+ static HidD_FreePreparsedData_ HidD_FreePreparsedData; |
+ static HidP_GetCaps_ HidP_GetCaps; |
+ |
+ static HMODULE lib_handle = NULL; |
+ static BOOLEAN initialized = FALSE; |
+#endif /* HIDAPI_USE_DDK */ |
+ |
+struct hid_device_ { |
+ HANDLE device_handle; |
+ BOOL blocking; |
+ USHORT output_report_length; |
+ size_t input_report_length; |
+ void *last_error_str; |
+ DWORD last_error_num; |
+ BOOL read_pending; |
+ char *read_buf; |
+ OVERLAPPED ol; |
+}; |
+ |
+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->input_report_length = 0; |
+ 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 /*inital state f=nonsignaled*/, NULL); |
+ |
+ return dev; |
+} |
+ |
+static void free_hid_device(hid_device *dev) |
+{ |
+ CloseHandle(dev->ol.hEvent); |
+ CloseHandle(dev->device_handle); |
+ LocalFree(dev->last_error_str); |
+ free(dev->read_buf); |
+ free(dev); |
+} |
+ |
+static void register_error(hid_device *device, const char *op) |
+{ |
+ WCHAR *ptr, *msg; |
+ |
+ FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | |
+ FORMAT_MESSAGE_FROM_SYSTEM | |
+ FORMAT_MESSAGE_IGNORE_INSERTS, |
+ NULL, |
+ GetLastError(), |
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), |
+ (LPVOID)&msg, 0/*sz*/, |
+ NULL); |
+ |
+ /* Get rid of the CR and LF that FormatMessage() sticks at the |
+ end of the message. Thanks Microsoft! */ |
+ ptr = msg; |
+ while (*ptr) { |
+ if (*ptr == '\r') { |
+ *ptr = 0x0000; |
+ break; |
+ } |
+ ptr++; |
+ } |
+ |
+ /* Store the message off in the Device entry so that |
+ the hid_error() function can pick it up. */ |
+ LocalFree(device->last_error_str); |
+ device->last_error_str = msg; |
+} |
+ |
+#ifndef HIDAPI_USE_DDK |
+static int lookup_functions() |
+{ |
+ lib_handle = LoadLibraryA("hid.dll"); |
+ if (lib_handle) { |
+#define RESOLVE(x) x = (x##_)GetProcAddress(lib_handle, #x); if (!x) return -1; |
+ RESOLVE(HidD_GetAttributes); |
+ RESOLVE(HidD_GetSerialNumberString); |
+ RESOLVE(HidD_GetManufacturerString); |
+ RESOLVE(HidD_GetProductString); |
+ RESOLVE(HidD_SetFeature); |
+ RESOLVE(HidD_GetFeature); |
+ RESOLVE(HidD_GetIndexedString); |
+ RESOLVE(HidD_GetPreparsedData); |
+ RESOLVE(HidD_FreePreparsedData); |
+ RESOLVE(HidP_GetCaps); |
+#undef RESOLVE |
+ } |
+ else |
+ return -1; |
+ |
+ return 0; |
+} |
+#endif |
+ |
+static HANDLE open_device(const char *path, BOOL enumerate) |
+{ |
+ HANDLE handle; |
+ DWORD desired_access = (enumerate)? 0: (GENERIC_WRITE | GENERIC_READ); |
+ DWORD share_mode = (enumerate)? |
+ FILE_SHARE_READ|FILE_SHARE_WRITE: |
+ FILE_SHARE_READ; |
+ |
+ handle = CreateFileA(path, |
+ desired_access, |
+ share_mode, |
+ NULL, |
+ OPEN_EXISTING, |
+ FILE_FLAG_OVERLAPPED,/*FILE_ATTRIBUTE_NORMAL,*/ |
+ 0); |
+ |
+ return handle; |
+} |
+ |
+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; |
+ initialized = FALSE; |
+#endif |
+ return 0; |
+} |
+ |
+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; /* return object */ |
+ struct hid_device_info *cur_dev = NULL; |
+ |
+ /* Windows objects for interacting with the driver. */ |
+ GUID InterfaceClassGuid = {0x4d1e55b2, 0xf16f, 0x11cf, {0x88, 0xcb, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30} }; |
+ 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; |
+ int device_index = 0; |
+ int i; |
+ |
+ if (hid_init() < 0) |
+ return NULL; |
+ |
+ /* Initialize the Windows objects. */ |
+ memset(&devinfo_data, 0x0, sizeof(devinfo_data)); |
+ devinfo_data.cbSize = sizeof(SP_DEVINFO_DATA); |
+ device_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); |
+ |
+ /* Get information for all the devices belonging to the HID class. */ |
+ device_info_set = SetupDiGetClassDevsA(&InterfaceClassGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); |
+ |
+ /* Iterate over each device in the HID class, looking for the right one. */ |
+ |
+ for (;;) { |
+ HANDLE write_handle = INVALID_HANDLE_VALUE; |
+ DWORD required_size = 0; |
+ HIDD_ATTRIBUTES attrib; |
+ |
+ res = SetupDiEnumDeviceInterfaces(device_info_set, |
+ NULL, |
+ &InterfaceClassGuid, |
+ device_index, |
+ &device_interface_data); |
+ |
+ if (!res) { |
+ /* A return of FALSE from this function means that |
+ there are no more devices. */ |
+ break; |
+ } |
+ |
+ /* Call with 0-sized detail size, and let the function |
+ tell us how long the detail struct needs to be. The |
+ size is put in &required_size. */ |
+ res = SetupDiGetDeviceInterfaceDetailA(device_info_set, |
+ &device_interface_data, |
+ NULL, |
+ 0, |
+ &required_size, |
+ NULL); |
+ |
+ /* Allocate a long enough structure for device_interface_detail_data. */ |
+ 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); |
+ |
+ /* Get the detailed data for this device. The detail data gives us |
+ the device path for this device, which is then passed into |
+ CreateFile() to get a handle to the device. */ |
+ res = SetupDiGetDeviceInterfaceDetailA(device_info_set, |
+ &device_interface_data, |
+ device_interface_detail_data, |
+ required_size, |
+ NULL, |
+ NULL); |
+ |
+ if (!res) { |
+ /* register_error(dev, "Unable to call SetupDiGetDeviceInterfaceDetail"); |
+ Continue to the next device. */ |
+ goto cont; |
+ } |
+ |
+ /* Make sure this device is of Setup Class "HIDClass" and has a |
+ driver bound to it. */ |
+ for (i = 0; ; i++) { |
+ char driver_name[256]; |
+ |
+ /* Populate devinfo_data. This function will return failure |
+ when there are no more interfaces left. */ |
+ res = SetupDiEnumDeviceInfo(device_info_set, i, &devinfo_data); |
+ if (!res) |
+ goto cont; |
+ |
+ res = SetupDiGetDeviceRegistryPropertyA(device_info_set, &devinfo_data, |
+ SPDRP_CLASS, NULL, (PBYTE)driver_name, sizeof(driver_name), NULL); |
+ if (!res) |
+ goto cont; |
+ |
+ if (strcmp(driver_name, "HIDClass") == 0) { |
+ /* See if there's a driver bound. */ |
+ res = SetupDiGetDeviceRegistryPropertyA(device_info_set, &devinfo_data, |
+ SPDRP_DRIVER, NULL, (PBYTE)driver_name, sizeof(driver_name), NULL); |
+ if (res) |
+ break; |
+ } |
+ } |
+ |
+ //wprintf(L"HandleName: %s\n", device_interface_detail_data->DevicePath); |
+ |
+ /* Open a handle to the device */ |
+ write_handle = open_device(device_interface_detail_data->DevicePath, TRUE); |
+ |
+ /* Check validity of write_handle. */ |
+ if (write_handle == INVALID_HANDLE_VALUE) { |
+ /* Unable to open the device. */ |
+ //register_error(dev, "CreateFile"); |
+ goto cont_close; |
+ } |
+ |
+ |
+ /* Get the Vendor ID and Product ID for this device. */ |
+ attrib.Size = sizeof(HIDD_ATTRIBUTES); |
+ HidD_GetAttributes(write_handle, &attrib); |
+ //wprintf(L"Product/Vendor: %x %x\n", attrib.ProductID, attrib.VendorID); |
+ |
+ /* Check the VID/PID to see if we should add this |
+ device to the enumeration list. */ |
+ if ((vendor_id == 0x0 || attrib.VendorID == vendor_id) && |
+ (product_id == 0x0 || attrib.ProductID == product_id)) { |
+ |
+ #define WSTR_LEN 512 |
+ const char *str; |
+ struct hid_device_info *tmp; |
+ PHIDP_PREPARSED_DATA pp_data = NULL; |
+ HIDP_CAPS caps; |
+ BOOLEAN res; |
+ NTSTATUS nt_res; |
+ wchar_t wstr[WSTR_LEN]; /* TODO: Determine Size */ |
+ size_t len; |
+ |
+ /* VID/PID match. Create the record. */ |
+ tmp = (struct hid_device_info*) calloc(1, sizeof(struct hid_device_info)); |
+ if (cur_dev) { |
+ cur_dev->next = tmp; |
+ } |
+ else { |
+ root = tmp; |
+ } |
+ cur_dev = tmp; |
+ |
+ /* Get the Usage Page and Usage for this device. */ |
+ res = HidD_GetPreparsedData(write_handle, &pp_data); |
+ if (res) { |
+ nt_res = HidP_GetCaps(pp_data, &caps); |
+ if (nt_res == HIDP_STATUS_SUCCESS) { |
+ cur_dev->usage_page = caps.UsagePage; |
+ cur_dev->usage = caps.Usage; |
+ } |
+ |
+ HidD_FreePreparsedData(pp_data); |
+ } |
+ |
+ /* Fill out the record */ |
+ cur_dev->next = NULL; |
+ str = device_interface_detail_data->DevicePath; |
+ if (str) { |
+ len = strlen(str); |
+ cur_dev->path = (char*) calloc(len+1, sizeof(char)); |
+ strncpy(cur_dev->path, str, len+1); |
+ cur_dev->path[len] = '\0'; |
+ } |
+ else |
+ cur_dev->path = NULL; |
+ |
+ /* Serial Number */ |
+ res = HidD_GetSerialNumberString(write_handle, wstr, sizeof(wstr)); |
+ wstr[WSTR_LEN-1] = 0x0000; |
+ if (res) { |
+ cur_dev->serial_number = _wcsdup(wstr); |
+ } |
+ |
+ /* Manufacturer String */ |
+ res = HidD_GetManufacturerString(write_handle, wstr, sizeof(wstr)); |
+ wstr[WSTR_LEN-1] = 0x0000; |
+ if (res) { |
+ cur_dev->manufacturer_string = _wcsdup(wstr); |
+ } |
+ |
+ /* Product String */ |
+ res = HidD_GetProductString(write_handle, wstr, sizeof(wstr)); |
+ wstr[WSTR_LEN-1] = 0x0000; |
+ if (res) { |
+ cur_dev->product_string = _wcsdup(wstr); |
+ } |
+ |
+ /* VID/PID */ |
+ cur_dev->vendor_id = attrib.VendorID; |
+ cur_dev->product_id = attrib.ProductID; |
+ |
+ /* Release Number */ |
+ cur_dev->release_number = attrib.VersionNumber; |
+ |
+ /* Interface Number. It can sometimes be parsed out of the path |
+ on Windows if a device has multiple interfaces. See |
+ http://msdn.microsoft.com/en-us/windows/hardware/gg487473 or |
+ search for "Hardware IDs for HID Devices" at MSDN. If it's not |
+ in the path, it's set to -1. */ |
+ cur_dev->interface_number = -1; |
+ if (cur_dev->path) { |
+ char *interface_component = strstr(cur_dev->path, "&mi_"); |
+ if (interface_component) { |
+ char *hex_str = interface_component + 4; |
+ char *endptr = NULL; |
+ cur_dev->interface_number = strtol(hex_str, &endptr, 16); |
+ if (endptr == hex_str) { |
+ /* The parsing failed. Set interface_number to -1. */ |
+ cur_dev->interface_number = -1; |
+ } |
+ } |
+ } |
+ } |
+ |
+cont_close: |
+ CloseHandle(write_handle); |
+cont: |
+ /* We no longer need the detail data. It can be freed */ |
+ free(device_interface_detail_data); |
+ |
+ device_index++; |
+ |
+ } |
+ |
+ /* Close the device information handle. */ |
+ SetupDiDestroyDeviceInfoList(device_info_set); |
+ |
+ return root; |
+ |
+} |
+ |
+void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs) |
+{ |
+ /* TODO: Merge this with the Linux version. This function is platform-independent. */ |
+ 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) |
+{ |
+ /* TODO: Merge this functions with the Linux version. This function should be platform independent. */ |
+ 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 (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) { |
+ /* Open the device */ |
+ 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(); |
+ |
+ /* Open a handle to the device */ |
+ dev->device_handle = open_device(path, FALSE); |
+ |
+ /* Check validity of write_handle. */ |
+ if (dev->device_handle == INVALID_HANDLE_VALUE) { |
+ /* Unable to open the device. */ |
+ register_error(dev, "CreateFile"); |
+ goto err; |
+ } |
+ |
+ /* Get the Input Report length for the device. */ |
+ 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; |
+ HidD_FreePreparsedData(pp_data); |
+ |
+ dev->read_buf = (char*) malloc(dev->input_report_length); |
+ |
+ 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; |
+ BOOL res; |
+ |
+ OVERLAPPED ol; |
+ unsigned char *buf; |
+ memset(&ol, 0, sizeof(ol)); |
+ |
+ /* Make sure the right number of bytes are passed to WriteFile. Windows |
+ expects the number of bytes which are in the _longest_ report (plus |
+ one for the report number) bytes even if the data is a report |
+ which is shorter than that. Windows gives us this value in |
+ caps.OutputReportByteLength. If a user passes in fewer bytes than this, |
+ create a temporary buffer which is the proper size. */ |
+ if (length >= dev->output_report_length) { |
+ /* The user passed the right number of bytes. Use the buffer as-is. */ |
+ buf = (unsigned char *) data; |
+ } else { |
+ /* Create a temporary buffer and copy the user's data |
+ into it, padding the rest with zeros. */ |
+ buf = (unsigned char *) malloc(dev->output_report_length); |
+ memcpy(buf, data, length); |
+ memset(buf + length, 0, dev->output_report_length - length); |
+ length = dev->output_report_length; |
+ } |
+ |
+ res = WriteFile(dev->device_handle, buf, length, NULL, &ol); |
+ |
+ if (!res) { |
+ if (GetLastError() != ERROR_IO_PENDING) { |
+ /* WriteFile() failed. Return error. */ |
+ register_error(dev, "WriteFile"); |
+ bytes_written = -1; |
+ goto end_of_function; |
+ } |
+ } |
+ |
+ /* Wait here until the write is done. This makes |
+ hid_write() synchronous. */ |
+ res = GetOverlappedResult(dev->device_handle, &ol, &bytes_written, TRUE/*wait*/); |
+ if (!res) { |
+ /* The Write operation failed. */ |
+ register_error(dev, "WriteFile"); |
+ bytes_written = -1; |
+ goto end_of_function; |
+ } |
+ |
+end_of_function: |
+ if (buf != data) |
+ free(buf); |
+ |
+ return bytes_written; |
+} |
+ |
+ |
+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; |
+ BOOL res; |
+ |
+ /* Copy the handle for convenience. */ |
+ HANDLE ev = dev->ol.hEvent; |
+ |
+ if (!dev->read_pending) { |
+ /* Start an Overlapped I/O read. */ |
+ dev->read_pending = TRUE; |
+ memset(dev->read_buf, 0, dev->input_report_length); |
+ ResetEvent(ev); |
+ res = ReadFile(dev->device_handle, dev->read_buf, dev->input_report_length, &bytes_read, &dev->ol); |
+ |
+ if (!res) { |
+ if (GetLastError() != ERROR_IO_PENDING) { |
+ /* ReadFile() has failed. |
+ Clean up and return error. */ |
+ CancelIo(dev->device_handle); |
+ dev->read_pending = FALSE; |
+ goto end_of_function; |
+ } |
+ } |
+ } |
+ |
+ if (milliseconds >= 0) { |
+ /* See if there is any data yet. */ |
+ res = WaitForSingleObject(ev, milliseconds); |
+ if (res != WAIT_OBJECT_0) { |
+ /* There was no data this time. Return zero bytes available, |
+ but leave the Overlapped I/O running. */ |
+ return 0; |
+ } |
+ } |
+ |
+ /* Either WaitForSingleObject() told us that ReadFile has completed, or |
+ we are in non-blocking mode. Get the number of bytes read. The actual |
+ data has been copied to the data[] array which was passed to ReadFile(). */ |
+ res = GetOverlappedResult(dev->device_handle, &dev->ol, &bytes_read, TRUE/*wait*/); |
+ |
+ /* Set pending back to false, even if GetOverlappedResult() returned error. */ |
+ dev->read_pending = FALSE; |
+ |
+ if (res && bytes_read > 0) { |
+ if (dev->read_buf[0] == 0x0) { |
+ /* If report numbers aren't being used, but Windows sticks a report |
+ number (0x0) on the beginning of the report anyway. To make this |
+ work like the other platforms, and to make it work more like the |
+ HID spec, we'll skip over this byte. */ |
+ size_t copy_len; |
+ bytes_read--; |
+ copy_len = length > bytes_read ? bytes_read : length; |
+ memcpy(data, dev->read_buf+1, copy_len); |
+ } |
+ else { |
+ /* Copy the whole buffer, report number and all. */ |
+ size_t 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 bytes_read; |
+} |
+ |
+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; /* Success */ |
+} |
+ |
+int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length) |
+{ |
+ BOOL res = HidD_SetFeature(dev->device_handle, (PVOID)data, length); |
+ if (!res) { |
+ register_error(dev, "HidD_SetFeature"); |
+ return -1; |
+ } |
+ |
+ return length; |
+} |
+ |
+ |
+int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length) |
+{ |
+ BOOL res; |
+#if 0 |
+ res = HidD_GetFeature(dev->device_handle, data, length); |
+ if (!res) { |
+ register_error(dev, "HidD_GetFeature"); |
+ return -1; |
+ } |
+ return 0; /* HidD_GetFeature() doesn't give us an actual length, unfortunately */ |
+#else |
+ DWORD bytes_returned; |
+ |
+ OVERLAPPED ol; |
+ memset(&ol, 0, sizeof(ol)); |
+ |
+ res = DeviceIoControl(dev->device_handle, |
+ IOCTL_HID_GET_FEATURE, |
+ data, length, |
+ data, length, |
+ &bytes_returned, &ol); |
+ |
+ if (!res) { |
+ if (GetLastError() != ERROR_IO_PENDING) { |
+ /* DeviceIoControl() failed. Return error. */ |
+ register_error(dev, "Send Feature Report DeviceIoControl"); |
+ return -1; |
+ } |
+ } |
+ |
+ /* Wait here until the write is done. This makes |
+ hid_get_feature_report() synchronous. */ |
+ res = GetOverlappedResult(dev->device_handle, &ol, &bytes_returned, TRUE/*wait*/); |
+ if (!res) { |
+ /* The operation failed. */ |
+ register_error(dev, "Send Feature Report GetOverLappedResult"); |
+ return -1; |
+ } |
+ return bytes_returned; |
+#endif |
+} |
+ |
+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) |
+{ |
+ BOOL res; |
+ |
+ res = HidD_GetManufacturerString(dev->device_handle, string, sizeof(wchar_t) * maxlen); |
+ if (!res) { |
+ register_error(dev, "HidD_GetManufacturerString"); |
+ return -1; |
+ } |
+ |
+ return 0; |
+} |
+ |
+int HID_API_EXPORT_CALL HID_API_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen) |
+{ |
+ BOOL res; |
+ |
+ res = HidD_GetProductString(dev->device_handle, string, sizeof(wchar_t) * maxlen); |
+ if (!res) { |
+ register_error(dev, "HidD_GetProductString"); |
+ return -1; |
+ } |
+ |
+ return 0; |
+} |
+ |
+int HID_API_EXPORT_CALL HID_API_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen) |
+{ |
+ BOOL res; |
+ |
+ res = HidD_GetSerialNumberString(dev->device_handle, string, sizeof(wchar_t) * maxlen); |
+ if (!res) { |
+ register_error(dev, "HidD_GetSerialNumberString"); |
+ return -1; |
+ } |
+ |
+ 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) * maxlen); |
+ 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) |
+{ |
+ return (wchar_t*)dev->last_error_str; |
+} |
+ |
+ |
+/*#define PICPGM*/ |
+/*#define S11*/ |
+#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 |
+int __cdecl main(int argc, char* argv[]) |
+{ |
+ int res; |
+ unsigned char buf[65]; |
+ |
+ UNREFERENCED_PARAMETER(argc); |
+ UNREFERENCED_PARAMETER(argv); |
+ |
+ /* Set up the command buffer. */ |
+ memset(buf,0x00,sizeof(buf)); |
+ buf[0] = 0; |
+ buf[1] = 0x81; |
+ |
+ |
+ /* Open the device. */ |
+ int handle = open(VendorID, ProductID, L"12345"); |
+ if (handle < 0) |
+ printf("unable to open device\n"); |
+ |
+ |
+ /* Toggle LED (cmd 0x80) */ |
+ buf[1] = 0x80; |
+ res = write(handle, buf, 65); |
+ if (res < 0) |
+ printf("Unable to write()\n"); |
+ |
+ /* Request state (cmd 0x81) */ |
+ buf[1] = 0x81; |
+ write(handle, buf, 65); |
+ if (res < 0) |
+ printf("Unable to write() (2)\n"); |
+ |
+ /* Read requested state */ |
+ read(handle, buf, 65); |
+ if (res < 0) |
+ printf("Unable to read()\n"); |
+ |
+ /* Print out the returned buffer. */ |
+ for (int i = 0; i < 4; i++) |
+ printf("buf[%d]: %d\n", i, buf[i]); |
+ |
+ return 0; |
+} |
+#endif |
+ |
+#ifdef __cplusplus |
+} /* extern "C" */ |
+#endif |