diff --git a/ground/openpilotgcs/src/plugins/ophid/hidapi/hidapi.h b/ground/openpilotgcs/src/plugins/ophid/hidapi/hidapi.h index e58e8a484..e5bc2dc40 100644 --- a/ground/openpilotgcs/src/plugins/ophid/hidapi/hidapi.h +++ b/ground/openpilotgcs/src/plugins/ophid/hidapi/hidapi.h @@ -11,7 +11,7 @@ At the discretion of the user of this library, this software may be licensed under the terms of the - GNU Public License v3, a BSD-Style license, or 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. @@ -218,7 +218,8 @@ extern "C" { @returns This function returns the actual number of bytes read and - -1 on error. + -1 on error. If no packet was available to be read within + the timeout period, this function returns 0. */ int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds); @@ -237,7 +238,8 @@ extern "C" { @returns This function returns the actual number of bytes read and - -1 on error. + -1 on error. If no packet was available to be read and + the handle is in non-blocking mode, this function returns 0. */ int HID_API_EXPORT HID_API_CALL hid_read(hid_device *device, unsigned char *data, size_t length); @@ -291,22 +293,26 @@ extern "C" { /** @brief Get a feature report from a HID device. - Make sure to set the first byte of @p data[] to the Report - ID of the report to be read. Make sure to allow space for - this extra byte in @p data[]. + Set the first byte of @p data[] to the Report ID of the + report to be read. Make sure to allow space for this + extra byte in @p data[]. Upon return, the first byte will + still contain the Report ID, and the report data will + start in data[1]. @ingroup API @param device A device handle returned from hid_open(). @param data A buffer to put the read data into, including the Report ID. Set the first byte of @p data[] to the - Report ID of the report to be read. + Report ID of the report to be read, or set it to zero + if your device does not use numbered reports. @param length The number of bytes to read, including an extra byte for the report ID. The buffer can be longer than the actual report. @returns - This function returns the number of bytes read and - -1 on error. + This function returns the number of bytes read plus + one for the report ID (which is still in the first + byte), or -1 on error. */ int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *device, unsigned char *data, size_t length); diff --git a/ground/openpilotgcs/src/plugins/ophid/hidapi/libusb/hid.c b/ground/openpilotgcs/src/plugins/ophid/hidapi/libusb/hid.c index 13a4e5aa4..b066dde5f 100644 --- a/ground/openpilotgcs/src/plugins/ophid/hidapi/libusb/hid.c +++ b/ground/openpilotgcs/src/plugins/ophid/hidapi/libusb/hid.c @@ -14,7 +14,7 @@ At the discretion of the user of this library, this software may be licensed under the terms of the - GNU Public License v3, a BSD-Style license, or 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. @@ -44,11 +44,74 @@ #include /* GNU / LibUSB */ -#include "libusb.h" -#include "iconv.h" +#include +#ifndef __ANDROID__ +#include +#endif #include "../hidapi.h" +#ifdef __ANDROID__ + +/* Barrier implementation because Android/Bionic don't have pthread_barrier. + This implementation came from Brent Priddy and was posted on + StackOverflow. It is used with his permission. */ +typedef int pthread_barrierattr_t; +typedef struct pthread_barrier { + pthread_mutex_t mutex; + pthread_cond_t cond; + int count; + int trip_count; +} pthread_barrier_t; + +static int pthread_barrier_init(pthread_barrier_t *barrier, const pthread_barrierattr_t *attr, unsigned int count) +{ + if(count == 0) { + errno = EINVAL; + return -1; + } + + if(pthread_mutex_init(&barrier->mutex, 0) < 0) { + return -1; + } + if(pthread_cond_init(&barrier->cond, 0) < 0) { + pthread_mutex_destroy(&barrier->mutex); + return -1; + } + barrier->trip_count = count; + barrier->count = 0; + + return 0; +} + +static int pthread_barrier_destroy(pthread_barrier_t *barrier) +{ + pthread_cond_destroy(&barrier->cond); + pthread_mutex_destroy(&barrier->mutex); + return 0; +} + +static int pthread_barrier_wait(pthread_barrier_t *barrier) +{ + pthread_mutex_lock(&barrier->mutex); + ++(barrier->count); + if(barrier->count >= barrier->trip_count) + { + barrier->count = 0; + pthread_cond_broadcast(&barrier->cond); + pthread_mutex_unlock(&barrier->mutex); + return 1; + } + else + { + pthread_cond_wait(&barrier->cond, &(barrier->mutex)); + pthread_mutex_unlock(&barrier->mutex); + return 0; + } +} + +#endif + #ifdef __cplusplus extern "C" { #endif @@ -105,6 +168,7 @@ struct hid_device_ { pthread_cond_t condition; pthread_barrier_t barrier; /* Ensures correct startup sequence */ int shutdown_thread; + int cancelled; struct libusb_transfer *transfer; /* List of received input reports. */ @@ -249,9 +313,9 @@ static int get_usage(uint8_t *report_descriptor, size_t size, } #endif /* INVASIVE_GET_USAGE */ -#ifdef __FreeBSD__ -/* The FreeBSD version of libusb doesn't have this funciton. In mainline - libusb, it's inlined in libusb.h. This function will bear a striking +#if defined(__FreeBSD__) && __FreeBSD__ < 10 +/* The libusb version included in FreeBSD < 10 doesn't have this function. In + mainline libusb, it's inlined in libusb.h. This function will bear a striking resemblence to that one, because there's about one way to code it. Note that the data parameter is Unicode in UTF-16LE encoding. @@ -325,8 +389,9 @@ static wchar_t *get_usb_string(libusb_device_handle *dev, uint8_t idx) char buf[512]; int len; wchar_t *str = NULL; - wchar_t wbuf[256]; +#ifndef __ANDROID__ /* we don't use iconv on Android */ + wchar_t wbuf[256]; /* iconv variables */ iconv_t ic; size_t inbytes; @@ -338,6 +403,7 @@ static wchar_t *get_usb_string(libusb_device_handle *dev, uint8_t idx) char *inptr; #endif char *outptr; +#endif /* Determine which language to use. */ uint16_t lang; @@ -354,6 +420,25 @@ static wchar_t *get_usb_string(libusb_device_handle *dev, uint8_t idx) if (len < 0) return NULL; +#ifdef __ANDROID__ + + /* Bionic does not have iconv support nor wcsdup() function, so it + has to be done manually. The following code will only work for + code points that can be represented as a single UTF-16 character, + and will incorrectly convert any code points which require more + than one UTF-16 character. + + Skip over the first character (2-bytes). */ + len -= 2; + str = malloc((len / 2 + 1) * sizeof(wchar_t)); + int i; + for (i = 0; i < len / 2; i++) { + str[i] = buf[i * 2 + 2] | (buf[i * 2 + 3] << 8); + } + str[len / 2] = 0x00000000; + +#else + /* buf does not need to be explicitly NULL-terminated because it is only passed into iconv() which does not need it. */ @@ -387,6 +472,8 @@ static wchar_t *get_usb_string(libusb_device_handle *dev, uint8_t idx) err: iconv_close(ic); +#endif + return str; } @@ -618,7 +705,8 @@ hid_device * hid_open(unsigned short vendor_id, unsigned short product_id, const 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) { + if (cur_dev->serial_number && + wcscmp(serial_number, cur_dev->serial_number) == 0) { path_to_open = cur_dev->path; break; } @@ -683,10 +771,12 @@ static void read_callback(struct libusb_transfer *transfer) } else if (transfer->status == LIBUSB_TRANSFER_CANCELLED) { dev->shutdown_thread = 1; + dev->cancelled = 1; return; } else if (transfer->status == LIBUSB_TRANSFER_NO_DEVICE) { dev->shutdown_thread = 1; + dev->cancelled = 1; return; } else if (transfer->status == LIBUSB_TRANSFER_TIMED_OUT) { @@ -701,6 +791,7 @@ static void read_callback(struct libusb_transfer *transfer) if (res != 0) { LOG("Unable to submit URB. libusb error code: %d\n", res); dev->shutdown_thread = 1; + dev->cancelled = 1; } } @@ -750,10 +841,10 @@ static void *read_thread(void *param) /* Cancel any transfer that may be pending. This call will fail if no transfers are pending, but that's OK. */ - if (libusb_cancel_transfer(dev->transfer) == 0) { - /* The transfer was cancelled, so wait for its completion. */ - libusb_handle_events(usb_context); - } + libusb_cancel_transfer(dev->transfer); + + while (!dev->cancelled) + libusb_handle_events_completed(usb_context, &dev->cancelled); /* Now that the read thread is stopping, Wake any threads which are waiting on data (in hid_read_timeout()). Do this under a mutex to @@ -786,11 +877,11 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path) int d = 0; int good_open = 0; - dev = new_hid_device(); - if(hid_init() < 0) return NULL; + dev = new_hid_device(); + libusb_get_device_list(usb_context, &devs); while ((usb_dev = devs[d++]) != NULL) { struct libusb_device_descriptor desc; @@ -1205,9 +1296,9 @@ int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index } -HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev __attribute__ ((unused))) +HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev) { - return NULL; + return NULL; } diff --git a/ground/openpilotgcs/src/plugins/ophid/hidapi/linux/hid.c b/ground/openpilotgcs/src/plugins/ophid/hidapi/linux/hid.c index a54543047..63501d685 100644 --- a/ground/openpilotgcs/src/plugins/ophid/hidapi/linux/hid.c +++ b/ground/openpilotgcs/src/plugins/ophid/hidapi/linux/hid.c @@ -12,7 +12,7 @@ At the discretion of the user of this library, this software may be licensed under the terms of the - GNU Public License v3, a BSD-Style license, or 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. @@ -80,6 +80,27 @@ struct hid_device_ { static __u32 kernel_version = 0; +static __u32 detect_kernel_version(void) +{ + struct utsname name; + int major, minor, release; + int ret; + + uname(&name); + ret = sscanf(name.release, "%d.%d.%d", &major, &minor, &release); + if (ret == 3) { + return KERNEL_VERSION(major, minor, release); + } + + ret = sscanf(name.release, "%d.%d", &major, &minor); + if (ret == 2) { + return KERNEL_VERSION(major, minor, 0); + } + + printf("Couldn't determine kernel version from version string \"%s\"\n", name.release); + return 0; +} + static hid_device *new_hid_device(void) { hid_device *dev = calloc(1, sizeof(hid_device)); @@ -345,6 +366,8 @@ int HID_API_EXPORT hid_init(void) if (!locale) setlocale(LC_CTYPE, ""); + kernel_version = detect_kernel_version(); + return 0; } @@ -600,21 +623,6 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path) dev = new_hid_device(); - if (kernel_version == 0) { - struct utsname name; - int major, minor, release; - int ret; - uname(&name); - ret = sscanf(name.release, "%d.%d.%d", &major, &minor, &release); - if (ret == 3) { - kernel_version = major << 16 | minor << 8 | release; - //printf("Kernel Version: %d\n", kernel_version); - } - else { - printf("Couldn't sscanf() version string %s\n", name.release); - } - } - /* OPEN HERE */ dev->device_handle = open(path, O_RDWR); @@ -700,6 +708,7 @@ int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t bytes_read = 0; if (bytes_read >= 0 && + kernel_version != 0 && kernel_version < KERNEL_VERSION(2,6,34) && dev->uses_numbered_reports) { /* Work around a kernel bug. Chop off the first byte. */ diff --git a/ground/openpilotgcs/src/plugins/ophid/hidapi/mac/hid.c b/ground/openpilotgcs/src/plugins/ophid/hidapi/mac/hid.c index 2d473253f..b6fa8383a 100644 --- a/ground/openpilotgcs/src/plugins/ophid/hidapi/mac/hid.c +++ b/ground/openpilotgcs/src/plugins/ophid/hidapi/mac/hid.c @@ -11,7 +11,7 @@ At the discretion of the user of this library, this software may be licensed under the terms of the - GNU Public License v3, a BSD-Style license, or 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. @@ -24,7 +24,6 @@ #include #include -#include #include #include #include @@ -265,6 +264,46 @@ static int get_string_property(IOHIDDeviceRef device, CFStringRef prop, wchar_t } +static int get_string_property_utf8(IOHIDDeviceRef device, CFStringRef prop, char *buf, size_t len) +{ + CFStringRef str; + if (!len) + return 0; + + str = IOHIDDeviceGetProperty(device, prop); + + buf[0] = 0; + + if (str) { + len--; + + CFIndex str_len = CFStringGetLength(str); + CFRange range; + range.location = 0; + range.length = str_len; + CFIndex used_buf_len; + CFIndex chars_copied; + chars_copied = CFStringGetBytes(str, + range, + kCFStringEncodingUTF8, + (char)'?', + FALSE, + (UInt8*)buf, + len, + &used_buf_len); + + if (used_buf_len == len) + buf[len] = 0; /* len is decremented above */ + else + buf[used_buf_len] = 0; + + return used_buf_len; + } + else + return 0; +} + + static int get_serial_number(IOHIDDeviceRef device, wchar_t *buf, size_t len) { return get_string_property(device, CFSTR(kIOHIDSerialNumberKey), buf, len); @@ -291,54 +330,33 @@ static wchar_t *dup_wcs(const wchar_t *s) return ret; } -#if MAC_OS_X_VERSION_MIN_REQUIRED != MAC_OS_X_VERSION_10_5 -#warning "hidapi must be compiled/linked with -mmacosx-version-min=10.5 if you want OS X 10.5 compatibility" -#endif -/* hidapi_IOHIDDeviceGetService() - * - * Return the io_service_t corresponding to a given IOHIDDeviceRef, either by: - * - on OS X 10.6 and above, calling IOHIDDeviceGetService() - * - on OS X 10.5, extract it from the IOHIDDevice struct - */ -static io_service_t hidapi_IOHIDDeviceGetService(IOHIDDeviceRef device) +static int make_path(IOHIDDeviceRef device, char *buf, size_t len) { - /* When compiled with '-mmacosx-version-min=10.5', IOHIDDeviceGetService() - * is weakly linked in so we can decide to use it (or not) at runtime. - */ -#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 - if (IOHIDDeviceGetService != NULL) { - /* OS X 10.6 and above: IOHIDDeviceGetService() exists */ - return IOHIDDeviceGetService(device); - } - else -#endif - { - /* OS X 10.5: IOHIDDeviceGetService() doesn't exist. - * Be naughty and pull the service out of the IOHIDDevice. - * Tested and working on OS X 10.5.8 i386, x86_64, and ppc - */ - struct IOHIDDevice_internal { - /* The first field of the IOHIDDevice struct is a - * CFRuntimeBase (which is a private CF struct). - * - * a, b, and c are the 3 fields that make up a CFRuntimeBase. - * See http://opensource.apple.com/source/CF/CF-476.18/CFRuntime.h - * - * The second field of the IOHIDDevice is the io_service_t we're looking for. - */ - //CFRuntimeBase base; - uintptr_t a; - uint8_t b[4]; -#if __LP64__ - uint32_t c; -#endif - io_service_t service; - }; - struct IOHIDDevice_internal *tmp = (struct IOHIDDevice_internal *)device; + int res; + unsigned short vid, pid; + char transport[32]; + int32_t location; - return tmp->service; - } + buf[0] = '\0'; + + res = get_string_property_utf8( + device, CFSTR(kIOHIDTransportKey), + transport, sizeof(transport)); + + if (!res) + return -1; + + location = get_location_id(device); + vid = get_vendor_id(device); + pid = get_product_id(device); + + res = snprintf(buf, len, "%s_%04hx_%04hx_%x", + transport, vid, pid, location); + + + buf[len-1] = '\0'; + return res+1; } /* Initialize the IOHIDManager. Return 0 for success and -1 for failure. */ @@ -402,6 +420,7 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, process_pending_events(); /* Get a list of the Devices */ + IOHIDManagerSetDeviceMatching(hid_mgr, NULL); CFSetRef device_set = IOHIDManagerCopyDevices(hid_mgr); /* Convert the list into a C array so we can iterate easily. */ @@ -415,6 +434,7 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short dev_pid; #define BUF_LEN 256 wchar_t buf[BUF_LEN]; + char cbuf[BUF_LEN]; IOHIDDeviceRef dev = device_array[i]; @@ -428,9 +448,7 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, if ((vendor_id == 0x0 || vendor_id == dev_vid) && (product_id == 0x0 || product_id == dev_pid)) { struct hid_device_info *tmp; - io_object_t iokit_dev; - kern_return_t res; - io_string_t path; + size_t len; /* VID/PID match. Create the record. */ tmp = malloc(sizeof(struct hid_device_info)); @@ -448,14 +466,8 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, /* Fill out the record */ cur_dev->next = NULL; - - /* Fill in the path (IOService plane) */ - iokit_dev = hidapi_IOHIDDeviceGetService(dev); - res = IORegistryEntryGetPath(iokit_dev, kIOServicePlane, path); - if (res == KERN_SUCCESS) - cur_dev->path = strdup(path); - else - cur_dev->path = strdup(""); + len = make_path(dev, cbuf, sizeof(cbuf)); + cur_dev->path = strdup(cbuf); /* Serial Number */ get_serial_number(dev, buf, BUF_LEN); @@ -669,77 +681,76 @@ static void *read_thread(void *param) return NULL; } -/* hid_open_path() - * - * path must be a valid path to an IOHIDDevice in the IOService plane - * Example: "IOService:/AppleACPIPlatformExpert/PCI0@0/AppleACPIPCI/EHC1@1D,7/AppleUSBEHCI/PLAYSTATION(R)3 Controller@fd120000/IOUSBInterface@0/IOUSBHIDDriver" - */ hid_device * HID_API_EXPORT hid_open_path(const char *path) { + int i; hid_device *dev = NULL; - io_registry_entry_t entry = MACH_PORT_NULL; - + CFIndex num_devices; + dev = new_hid_device(); /* Set up the HID Manager if it hasn't been done */ if (hid_init() < 0) return NULL; - /* Get the IORegistry entry for the given path */ - entry = IORegistryEntryFromPath(kIOMasterPortDefault, path); - if (entry == MACH_PORT_NULL) { - /* Path wasn't valid (maybe device was removed?) */ - goto return_error; - } + /* give the IOHIDManager a chance to update itself */ + process_pending_events(); - /* Create an IOHIDDevice for the entry */ - dev->device_handle = IOHIDDeviceCreate(kCFAllocatorDefault, entry); - if (dev->device_handle == NULL) { - /* Error creating the HID device */ - goto return_error; - } + CFSetRef device_set = IOHIDManagerCopyDevices(hid_mgr); - /* Open the IOHIDDevice */ - IOReturn ret = IOHIDDeviceOpen(dev->device_handle, kIOHIDOptionsTypeSeizeDevice); - if (ret == kIOReturnSuccess) { - char str[32]; + num_devices = CFSetGetCount(device_set); + IOHIDDeviceRef *device_array = calloc(num_devices, sizeof(IOHIDDeviceRef)); + CFSetGetValues(device_set, (const void **) device_array); + for (i = 0; i < num_devices; i++) { + char cbuf[BUF_LEN]; + size_t len; + IOHIDDeviceRef os_dev = device_array[i]; - /* Create the buffers for receiving data */ - dev->max_input_report_len = (CFIndex) get_max_report_length(dev->device_handle); - dev->input_report_buf = calloc(dev->max_input_report_len, sizeof(uint8_t)); + len = make_path(os_dev, cbuf, sizeof(cbuf)); + if (!strcmp(cbuf, path)) { + /* Matched Paths. Open this Device. */ + IOReturn ret = IOHIDDeviceOpen(os_dev, kIOHIDOptionsTypeSeizeDevice); + if (ret == kIOReturnSuccess) { + char str[32]; - /* Create the Run Loop Mode for this device. - printing the reference seems to work. */ - sprintf(str, "HIDAPI_%p", dev->device_handle); - dev->run_loop_mode = - CFStringCreateWithCString(NULL, str, kCFStringEncodingASCII); - - /* Attach the device to a Run Loop */ - IOHIDDeviceRegisterInputReportCallback( - dev->device_handle, dev->input_report_buf, dev->max_input_report_len, - &hid_report_callback, dev); - IOHIDDeviceRegisterRemovalCallback(dev->device_handle, hid_device_removal_callback, dev); + free(device_array); + CFRetain(os_dev); + CFRelease(device_set); + dev->device_handle = os_dev; - /* Start the read thread */ - pthread_create(&dev->thread, NULL, read_thread, dev); + /* Create the buffers for receiving data */ + dev->max_input_report_len = (CFIndex) get_max_report_length(os_dev); + dev->input_report_buf = calloc(dev->max_input_report_len, sizeof(uint8_t)); - /* Wait here for the read thread to be initialized. */ - pthread_barrier_wait(&dev->barrier); - - IOObjectRelease(entry); - return dev; - } - else { - goto return_error; + /* Create the Run Loop Mode for this device. + printing the reference seems to work. */ + sprintf(str, "HIDAPI_%p", os_dev); + dev->run_loop_mode = + CFStringCreateWithCString(NULL, str, kCFStringEncodingASCII); + + /* Attach the device to a Run Loop */ + IOHIDDeviceRegisterInputReportCallback( + os_dev, dev->input_report_buf, dev->max_input_report_len, + &hid_report_callback, dev); + IOHIDDeviceRegisterRemovalCallback(dev->device_handle, hid_device_removal_callback, dev); + + /* Start the read thread */ + pthread_create(&dev->thread, NULL, read_thread, dev); + + /* Wait here for the read thread to be initialized. */ + pthread_barrier_wait(&dev->barrier); + + return dev; + } + else { + goto return_error; + } + } } return_error: - if (dev->device_handle != NULL) - CFRelease(dev->device_handle); - - if (entry != MACH_PORT_NULL) - IOObjectRelease(entry); - + free(device_array); + CFRelease(device_set); free_hid_device(dev); return NULL; } diff --git a/ground/openpilotgcs/src/plugins/ophid/hidapi/windows/hid.c b/ground/openpilotgcs/src/plugins/ophid/hidapi/windows/hid.c index 9286b1ff6..ae1a07da7 100644 --- a/ground/openpilotgcs/src/plugins/ophid/hidapi/windows/hid.c +++ b/ground/openpilotgcs/src/plugins/ophid/hidapi/windows/hid.c @@ -11,7 +11,7 @@ At the discretion of the user of this library, this software may be licensed under the terms of the - GNU Public License v3, a BSD-Style license, or 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. @@ -106,6 +106,7 @@ extern "C" { 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_GetAttributes_ HidD_GetAttributes; static HidD_GetSerialNumberString_ HidD_GetSerialNumberString; @@ -117,6 +118,7 @@ extern "C" { 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; @@ -131,8 +133,7 @@ struct hid_device_ { DWORD last_error_num; BOOL read_pending; char *read_buf; - OVERLAPPED rx_ol; - OVERLAPPED tx_ol; + OVERLAPPED ol; }; static hid_device *new_hid_device() @@ -146,18 +147,15 @@ static hid_device *new_hid_device() dev->last_error_num = 0; dev->read_pending = FALSE; dev->read_buf = NULL; - memset(&dev->rx_ol, 0, sizeof(dev->rx_ol)); - memset(&dev->tx_ol, 0, sizeof(dev->tx_ol)); - dev->rx_ol.hEvent = CreateEvent(NULL, FALSE, FALSE /*inital state f=nonsignaled*/, NULL); - dev->tx_ol.hEvent = CreateEvent(NULL, FALSE, FALSE /*inital state f=nonsignaled*/, 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->rx_ol.hEvent); - CloseHandle(dev->tx_ol.hEvent); + CloseHandle(dev->ol.hEvent); CloseHandle(dev->device_handle); LocalFree(dev->last_error_str); free(dev->read_buf); @@ -210,6 +208,7 @@ static int lookup_functions() RESOLVE(HidD_GetPreparsedData); RESOLVE(HidD_FreePreparsedData); RESOLVE(HidP_GetCaps); + RESOLVE(HidD_SetNumInputBuffers); #undef RESOLVE } else @@ -223,7 +222,9 @@ static HANDLE open_device(const char *path, BOOL enumerate) { HANDLE handle; DWORD desired_access = (enumerate)? 0: (GENERIC_WRITE | GENERIC_READ); - DWORD share_mode = FILE_SHARE_READ | FILE_SHARE_WRITE; + DWORD share_mode = (enumerate)? + FILE_SHARE_READ|FILE_SHARE_WRITE: + FILE_SHARE_READ; handle = CreateFileA(path, desired_access, @@ -232,7 +233,7 @@ static HANDLE open_device(const char *path, BOOL enumerate) OPEN_EXISTING, FILE_FLAG_OVERLAPPED,/*FILE_ATTRIBUTE_NORMAL,*/ 0); - DWORD error = GetLastError(); + return handle; } @@ -569,6 +570,13 @@ HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path) goto err; } + /* Set the Input Report buffer size to 64 reports. */ + res = HidD_SetNumInputBuffers(dev->device_handle, 64); + if (!res) { + register_error(dev, "HidD_SetNumInputBuffers"); + goto err; + } + /* Get the Input Report length for the device. */ res = HidD_GetPreparsedData(dev->device_handle, &pp_data); if (!res) { @@ -600,7 +608,9 @@ int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char * 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 @@ -620,8 +630,7 @@ int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char * length = dev->output_report_length; } - ResetEvent(dev->tx_ol.hEvent); - res = WriteFile(dev->device_handle, buf, length, NULL, &dev->tx_ol); + res = WriteFile(dev->device_handle, buf, length, NULL, &ol); if (!res) { if (GetLastError() != ERROR_IO_PENDING) { @@ -634,7 +643,7 @@ int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char * /* Wait here until the write is done. This makes hid_write() synchronous. */ - res = GetOverlappedResult(dev->device_handle, &dev->tx_ol, &bytes_written, TRUE/*wait*/); + res = GetOverlappedResult(dev->device_handle, &ol, &bytes_written, TRUE/*wait*/); if (!res) { /* The Write operation failed. */ register_error(dev, "WriteFile"); @@ -653,17 +662,18 @@ end_of_function: 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; /* Copy the handle for convenience. */ - HANDLE ev = dev->rx_ol.hEvent; + 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->rx_ol); + res = ReadFile(dev->device_handle, dev->read_buf, dev->input_report_length, &bytes_read, &dev->ol); if (!res) { if (GetLastError() != ERROR_IO_PENDING) { @@ -689,7 +699,7 @@ int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char /* 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->rx_ol, &bytes_read, TRUE/*wait*/); + 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; @@ -700,14 +710,13 @@ int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char 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; + copy_len = length > bytes_read ? bytes_read : length; memcpy(data, dev->read_buf, copy_len); } } @@ -718,7 +727,7 @@ end_of_function: return -1; } - return bytes_read; + return copy_len; } int HID_API_EXPORT HID_API_CALL hid_read(hid_device *dev, unsigned char *data, size_t length) @@ -782,6 +791,12 @@ int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *dev, unsigned register_error(dev, "Send Feature Report GetOverLappedResult"); return -1; } + + /* bytes_returned does not include the first byte which contains the + report ID. The data buffer actually contains one more byte than + bytes_returned. */ + bytes_returned++; + return bytes_returned; #endif }