diff options
author | Alexandru Csete <oz9aec@gmail.com> | 2016-09-23 13:53:25 +0200 |
---|---|---|
committer | Alexandru Csete <oz9aec@gmail.com> | 2016-09-23 13:54:51 +0200 |
commit | 67baea5e32de0daef510dfebfa67bf94c0b88f68 (patch) | |
tree | f2cacd82d1ec07057a7404e3f239204e2692baf5 /gr-fcd/lib | |
parent | 7566a55d929eab238c9066572b865164042b2aa0 (diff) |
fcd: Update hidapi to latest HEAD.
This contains necessary fixes for Mac OS X.
Diffstat (limited to 'gr-fcd/lib')
-rw-r--r-- | gr-fcd/lib/hid/hid-libusb.c | 117 | ||||
-rw-r--r-- | gr-fcd/lib/hid/hidapi.h | 32 | ||||
-rw-r--r-- | gr-fcd/lib/hid/hidmac.c | 406 | ||||
-rw-r--r-- | gr-fcd/lib/hid/hidwin.c | 355 |
4 files changed, 532 insertions, 378 deletions
diff --git a/gr-fcd/lib/hid/hid-libusb.c b/gr-fcd/lib/hid/hid-libusb.c index 097f87215d..3c6d877f88 100644 --- a/gr-fcd/lib/hid/hid-libusb.c +++ b/gr-fcd/lib/hid/hid-libusb.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 <wchar.h> /* GNU / LibUSB */ -#include "libusb.h" -#include "iconv.h" +#include <libusb.h> +#ifndef __ANDROID__ +#include <iconv.h> +#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 @@ -250,10 +313,10 @@ 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 - resemblence to that one, because there's about one way to code it. +#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 + resemblance to that one, because there's about one way to code it. Note that the data parameter is Unicode in UTF-16LE encoding. Return value is the number of bytes in data, or LIBUSB_ERROR_*. @@ -326,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; @@ -339,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; @@ -355,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. */ @@ -388,6 +472,8 @@ static wchar_t *get_usb_string(libusb_device_handle *dev, uint8_t idx) err: iconv_close(ic); +#endif + return str; } @@ -607,7 +693,7 @@ void HID_API_EXPORT hid_free_enumeration(struct hid_device_info *devs) } } -hid_device * hid_open(unsigned short vendor_id, unsigned short product_id, wchar_t *serial_number) +hid_device * 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; @@ -619,7 +705,8 @@ hid_device * hid_open(unsigned short vendor_id, unsigned short product_id, wchar 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; } @@ -762,7 +849,7 @@ static void *read_thread(void *param) /* 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 make sure that a thread which is about to go to sleep waiting on - the condition acutally will go to sleep before the condition is + the condition actually will go to sleep before the condition is signaled. */ pthread_mutex_lock(&dev->mutex); pthread_cond_broadcast(&dev->condition); @@ -790,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; @@ -871,7 +958,7 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path) (ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_IN; - /* Decide whether to use it for intput or output. */ + /* Decide whether to use it for input or output. */ if (dev->input_endpoint == 0 && is_interrupt && is_input) { /* Use this endpoint for INPUT */ @@ -927,7 +1014,7 @@ int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t if (dev->output_endpoint <= 0) { - /* No interrput out endpoint. Use the Control Endpoint */ + /* No interrupt out endpoint. Use the Control Endpoint */ res = libusb_control_transfer(dev->device_handle, LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE|LIBUSB_ENDPOINT_OUT, 0x09/*HID Set_Report*/, diff --git a/gr-fcd/lib/hid/hidapi.h b/gr-fcd/lib/hid/hidapi.h index 8e55c84742..e5bc2dc40a 100644 --- a/gr-fcd/lib/hid/hidapi.h +++ b/gr-fcd/lib/hid/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. @@ -87,7 +87,7 @@ extern "C" { needed. This function should be called at the beginning of execution however, if there is a chance of HIDAPI handles being opened by different threads simultaneously. - + @ingroup API @returns @@ -112,6 +112,8 @@ extern "C" { This function returns a linked list of all the HID devices attached to the system which match vendor_id and product_id. + If @p vendor_id is set to 0 then any vendor matches. + If @p product_id is set to 0 then any product matches. If @p vendor_id and @p product_id are both set to 0, then all HID devices will be returned. @@ -155,7 +157,7 @@ extern "C" { This function returns a pointer to a #hid_device object on success or NULL on failure. */ - HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, wchar_t *serial_number); + HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number); /** @brief Open a HID device by its path name. @@ -207,7 +209,7 @@ extern "C" { contain the Report number if the device uses numbered reports. @ingroup API - @param dev A device handle returned from hid_open(). + @param device A device handle returned from hid_open(). @param data A buffer to put the read data into. @param length The number of bytes to read. For devices with multiple reports, make sure to read an extra byte for @@ -216,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); @@ -235,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); @@ -289,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/gr-fcd/lib/hid/hidmac.c b/gr-fcd/lib/hid/hidmac.c index d8c69a8e52..e0756a1588 100644 --- a/gr-fcd/lib/hid/hidmac.c +++ b/gr-fcd/lib/hid/hidmac.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,12 +24,14 @@ #include <IOKit/hid/IOHIDManager.h> #include <IOKit/hid/IOHIDKeys.h> +#include <IOKit/IOKitLib.h> #include <CoreFoundation/CoreFoundation.h> #include <wchar.h> #include <locale.h> #include <pthread.h> #include <sys/time.h> #include <unistd.h> +#include <dlfcn.h> #include "hidapi.h" @@ -118,16 +120,8 @@ struct hid_device_ { pthread_barrier_t barrier; /* Ensures correct startup sequence */ pthread_barrier_t shutdown_barrier; /* Ensures correct shutdown sequence */ int shutdown_thread; - - hid_device *next; }; -/* Static list of all the devices open. This way when a device gets - disconnected, its hid_device structure can be marked as disconnected - from hid_device_removal_callback(). */ -static hid_device *device_list = NULL; -static pthread_mutex_t device_list_mutex = PTHREAD_MUTEX_INITIALIZER; - static hid_device *new_hid_device(void) { hid_device *dev = calloc(1, sizeof(hid_device)); @@ -141,7 +135,6 @@ static hid_device *new_hid_device(void) dev->input_report_buf = NULL; dev->input_reports = NULL; dev->shutdown_thread = 0; - dev->next = NULL; /* Thread objects */ pthread_mutex_init(&dev->mutex, NULL); @@ -149,22 +142,6 @@ static hid_device *new_hid_device(void) pthread_barrier_init(&dev->barrier, NULL, 2); pthread_barrier_init(&dev->shutdown_barrier, NULL, 2); - /* Add the new record to the device_list. */ - pthread_mutex_lock(&device_list_mutex); - if (!device_list) - device_list = dev; - else { - hid_device *d = device_list; - while (d) { - if (!d->next) { - d->next = dev; - break; - } - d = d->next; - } - } - pthread_mutex_unlock(&device_list_mutex); - return dev; } @@ -197,29 +174,11 @@ static void free_hid_device(hid_device *dev) pthread_cond_destroy(&dev->condition); pthread_mutex_destroy(&dev->mutex); - /* Remove it from the device list. */ - pthread_mutex_lock(&device_list_mutex); - hid_device *d = device_list; - if (d == dev) { - device_list = d->next; - } - else { - while (d) { - if (d->next == dev) { - d->next = d->next->next; - break; - } - - d = d->next; - } - } - pthread_mutex_unlock(&device_list_mutex); - /* Free the structure itself. */ free(dev); } -static IOHIDManagerRef hid_mgr = 0x0; +static IOHIDManagerRef hid_mgr = 0x0; #if 0 @@ -255,7 +214,6 @@ static unsigned short get_product_id(IOHIDDeviceRef device) return get_int_property(device, CFSTR(kIOHIDProductIDKey)); } - static int32_t get_max_report_length(IOHIDDeviceRef device) { return get_int_property(device, CFSTR(kIOHIDMaxInputReportSizeKey)); @@ -263,59 +221,46 @@ static int32_t get_max_report_length(IOHIDDeviceRef device) static int get_string_property(IOHIDDeviceRef device, CFStringRef prop, wchar_t *buf, size_t len) { - CFStringRef str = IOHIDDeviceGetProperty(device, prop); + CFStringRef str; - buf[0] = 0x0000; - - if (str) { - CFRange range; - range.location = 0; - range.length = len; - CFIndex used_buf_len; - CFStringGetBytes(str, - range, - kCFStringEncodingUTF32LE, - (char)'?', - FALSE, - (UInt8*)buf, - len, - &used_buf_len); - buf[len-1] = 0x00000000; - return used_buf_len; - } - else + if (!len) return 0; -} - -static int get_string_property_utf8(IOHIDDeviceRef device, CFStringRef prop, char *buf, size_t len) -{ - CFStringRef str = IOHIDDeviceGetProperty(device, prop); + str = IOHIDDeviceGetProperty(device, prop); - buf[0] = 0x0000; + buf[0] = 0; if (str) { + CFIndex str_len = CFStringGetLength(str); CFRange range; - range.location = 0; - range.length = len; CFIndex used_buf_len; - CFStringGetBytes(str, + CFIndex chars_copied; + + len --; + + range.location = 0; + range.length = ((size_t)str_len > len)? len: (size_t)str_len; + chars_copied = CFStringGetBytes(str, range, - kCFStringEncodingUTF8, + kCFStringEncodingUTF32LE, (char)'?', FALSE, (UInt8*)buf, - len, + len * sizeof(wchar_t), &used_buf_len); - buf[len-1] = 0x00000000; - return used_buf_len; + + if (chars_copied == len) + buf[len] = 0; /* len is decremented above */ + else + buf[chars_copied] = 0; + + return 0; } else - return 0; + return -1; } - static int get_serial_number(IOHIDDeviceRef device, wchar_t *buf, size_t len) { return get_string_property(device, CFSTR(kIOHIDSerialNumberKey), buf, len); @@ -342,53 +287,87 @@ static wchar_t *dup_wcs(const wchar_t *s) return ret; } - -static int make_path(IOHIDDeviceRef device, char *buf, size_t len) +/* 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) { - int res; - unsigned short vid, pid; - char transport[32]; - - buf[0] = '\0'; - - res = get_string_property_utf8( - device, CFSTR(kIOHIDTransportKey), - transport, sizeof(transport)); - - if (!res) - return -1; - - vid = get_vendor_id(device); - pid = get_product_id(device); - - res = snprintf(buf, len, "%s_%04hx_%04hx_%p", - transport, vid, pid, device); + static void *iokit_framework = NULL; + static io_service_t (*dynamic_IOHIDDeviceGetService)(IOHIDDeviceRef device) = NULL; + + /* Use dlopen()/dlsym() to get a pointer to IOHIDDeviceGetService() if it exists. + * If any of these steps fail, dynamic_IOHIDDeviceGetService will be left NULL + * and the fallback method will be used. + */ + if (iokit_framework == NULL) { + iokit_framework = dlopen("/System/Library/IOKit.framework/IOKit", RTLD_LAZY); + + if (iokit_framework != NULL) + dynamic_IOHIDDeviceGetService = dlsym(iokit_framework, "IOHIDDeviceGetService"); + } + if (dynamic_IOHIDDeviceGetService != NULL) { + /* Running on OS X 10.6 and above: IOHIDDeviceGetService() exists */ + return dynamic_IOHIDDeviceGetService(device); + } + else + { + /* Running on OS X 10.5: IOHIDDeviceGetService() doesn't exist. + * + * Be naughty and pull the service out of the IOHIDDevice. + * IOHIDDevice is an opaque struct not exposed to applications, but its + * layout is stable through all available versions of OS X. + * 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. + */ + 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; - buf[len-1] = '\0'; - return res+1; + return tmp->service; + } } +/* Initialize the IOHIDManager. Return 0 for success and -1 for failure. */ static int init_hid_manager(void) { - IOReturn res; - /* Initialize all the HID Manager Objects */ hid_mgr = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); - IOHIDManagerSetDeviceMatching(hid_mgr, NULL); - IOHIDManagerScheduleWithRunLoop(hid_mgr, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); - res = IOHIDManagerOpen(hid_mgr, kIOHIDOptionsTypeNone); - return (res == kIOReturnSuccess)? 0: -1; + if (hid_mgr) { + IOHIDManagerSetDeviceMatching(hid_mgr, NULL); + IOHIDManagerScheduleWithRunLoop(hid_mgr, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + return 0; + } + + return -1; } +/* Initialize the IOHIDManager if necessary. This is the public function, and + it is safe to call this function repeatedly. Return 0 for success and -1 + for failure. */ int HID_API_EXPORT hid_init(void) { if (!hid_mgr) { - if (init_hid_manager() < 0) { - hid_exit(); - return -1; - } + return init_hid_manager(); } + + /* Already initialized. */ return 0; } @@ -404,19 +383,29 @@ int HID_API_EXPORT hid_exit(void) return 0; } +static void process_pending_events(void) { + SInt32 res; + do { + res = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.001, FALSE); + } while(res != kCFRunLoopRunFinished && res != kCFRunLoopRunTimedOut); +} + struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short product_id) { - struct hid_device_info *root = NULL; // return object + struct hid_device_info *root = NULL; /* return object */ struct hid_device_info *cur_dev = NULL; CFIndex num_devices; int i; - setlocale(LC_ALL,""); - /* Set up the HID Manager if it hasn't been done */ - hid_init(); + if (hid_init() < 0) + return NULL; + + /* give the IOHIDManager a chance to update itself */ + 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. */ @@ -430,7 +419,6 @@ 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]; @@ -441,12 +429,14 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, dev_pid = get_product_id(dev); /* Check the VID/PID against the arguments */ - if ((vendor_id == 0x0 && product_id == 0x0) || - (vendor_id == dev_vid && product_id == dev_pid)) { + if ((vendor_id == 0x0 || vendor_id == dev_vid) && + (product_id == 0x0 || product_id == dev_pid)) { struct hid_device_info *tmp; - size_t len; + io_object_t iokit_dev; + kern_return_t res; + io_string_t path; - /* VID/PID match. Create the record. */ + /* VID/PID match. Create the record. */ tmp = malloc(sizeof(struct hid_device_info)); if (cur_dev) { cur_dev->next = tmp; @@ -456,14 +446,20 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, } cur_dev = tmp; - // Get the Usage Page and Usage for this device. + /* Get the Usage Page and Usage for this device. */ cur_dev->usage_page = get_int_property(dev, CFSTR(kIOHIDPrimaryUsagePageKey)); cur_dev->usage = get_int_property(dev, CFSTR(kIOHIDPrimaryUsageKey)); /* Fill out the record */ cur_dev->next = NULL; - len = make_path(dev, cbuf, sizeof(cbuf)); - cur_dev->path = strdup(cbuf); + + /* 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(""); /* Serial Number */ get_serial_number(dev, buf, BUF_LEN); @@ -508,7 +504,7 @@ void HID_API_EXPORT hid_free_enumeration(struct hid_device_info *devs) } } -hid_device * HID_API_EXPORT hid_open(unsigned short vendor_id, unsigned short product_id, wchar_t *serial_number) +hid_device * HID_API_EXPORT hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number) { /* This function is identical to the Linux version. Platform independent. */ struct hid_device_info *devs, *cur_dev; @@ -545,20 +541,13 @@ hid_device * HID_API_EXPORT hid_open(unsigned short vendor_id, unsigned short pr } static void hid_device_removal_callback(void *context, IOReturn result, - void *sender, IOHIDDeviceRef dev_ref) + void *sender) { /* Stop the Run Loop for this device. */ - pthread_mutex_lock(&device_list_mutex); - hid_device *d = device_list; - while (d) { - if (d->device_handle == dev_ref) { - d->disconnected = 1; - CFRunLoopStop(d->run_loop); - } + hid_device *d = context; - d = d->next; - } - pthread_mutex_unlock(&device_list_mutex); + d->disconnected = 1; + CFRunLoopStop(d->run_loop); } /* The Run Loop calls this function for each input report received. @@ -612,17 +601,18 @@ static void hid_report_callback(void *context, IOReturn result, void *sender, } -/* This gets called when the read_thred's run loop gets signaled by +/* This gets called when the read_thread's run loop gets signaled by hid_close(), and serves to stop the read_thread's run loop. */ static void perform_signal_callback(void *context) { hid_device *dev = context; - CFRunLoopStop(dev->run_loop); //TODO: CFRunLoopGetCurrent() + CFRunLoopStop(dev->run_loop); /*TODO: CFRunLoopGetCurrent()*/ } static void *read_thread(void *param) { hid_device *dev = param; + SInt32 code; /* Move the device's run loop to this thread. */ IOHIDDeviceScheduleWithRunLoop(dev->device_handle, CFRunLoopGetCurrent(), dev->run_loop_mode); @@ -646,7 +636,6 @@ static void *read_thread(void *param) /* Run the Event Loop. CFRunLoopRunInMode() will dispatch HID input reports into the hid_report_callback(). */ - SInt32 code; while (!dev->shutdown_thread && !dev->disconnected) { code = CFRunLoopRunInMode(dev->run_loop_mode, 1000/*sec*/, FALSE); /* Return if the device has been disconnected */ @@ -670,19 +659,12 @@ static void *read_thread(void *param) /* 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 make sure that a thread which is about to go to sleep waiting on - the condition acutally will go to sleep before the condition is + the condition actually will go to sleep before the condition is signaled. */ pthread_mutex_lock(&dev->mutex); pthread_cond_broadcast(&dev->condition); pthread_mutex_unlock(&dev->mutex); - /* Close the OS handle to the device, but only if it's not - been unplugged. If it's been unplugged, then calling - IOHIDDeviceClose() will crash. */ - if (!dev->disconnected) { - IOHIDDeviceClose(dev->device_handle, kIOHIDOptionsTypeNone); - } - /* Wait here until hid_close() is called and makes it past the call to CFRunLoopWakeUp(). This thread still needs to be valid when that function is called on the other thread. */ @@ -691,71 +673,77 @@ 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; - CFIndex num_devices; + io_registry_entry_t entry = MACH_PORT_NULL; dev = new_hid_device(); /* Set up the HID Manager if it hasn't been done */ - hid_init(); + 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; + } - CFSetRef device_set = IOHIDManagerCopyDevices(hid_mgr); + /* 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; + } - 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]; - - len = make_path(os_dev, cbuf, sizeof(cbuf)); - if (!strcmp(cbuf, path)) { - // Matched Paths. Open this Device. - IOReturn ret = IOHIDDeviceOpen(os_dev, kIOHIDOptionsTypeNone); - if (ret == kIOReturnSuccess) { - char str[32]; - - free(device_array); - CFRelease(device_set); - dev->device_handle = os_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)); - - /* 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); - IOHIDManagerRegisterDeviceRemovalCallback(hid_mgr, hid_device_removal_callback, NULL); - - /* 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; - } - } + /* Open the IOHIDDevice */ + IOReturn ret = IOHIDDeviceOpen(dev->device_handle, kIOHIDOptionsTypeSeizeDevice); + if (ret == kIOReturnSuccess) { + char str[32]; + + /* 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)); + + /* 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); + + /* 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); + + IOObjectRelease(entry); + return dev; + } + else { + goto return_error; } return_error: - free(device_array); - CFRelease(device_set); + if (dev->device_handle != NULL) + CFRelease(dev->device_handle); + + if (entry != MACH_PORT_NULL) + IOObjectRelease(entry); + free_hid_device(dev); return NULL; } @@ -767,8 +755,8 @@ static int set_report(hid_device *dev, IOHIDReportType type, const unsigned char IOReturn res; /* Return if the device has been disconnected. */ - if (dev->disconnected) - return -1; + if (dev->disconnected) + return -1; if (data[0] == 0x0) { /* Not using numbered Reports. @@ -981,7 +969,7 @@ void HID_API_EXPORT hid_close(hid_device *dev) IOHIDDeviceRegisterInputReportCallback( dev->device_handle, dev->input_report_buf, dev->max_input_report_len, NULL, dev); - IOHIDManagerRegisterDeviceRemovalCallback(hid_mgr, NULL, dev); + IOHIDDeviceRegisterRemovalCallback(dev->device_handle, NULL, dev); IOHIDDeviceUnscheduleFromRunLoop(dev->device_handle, dev->run_loop, dev->run_loop_mode); IOHIDDeviceScheduleWithRunLoop(dev->device_handle, CFRunLoopGetMain(), kCFRunLoopDefaultMode); } @@ -1003,7 +991,7 @@ void HID_API_EXPORT hid_close(hid_device *dev) been unplugged. If it's been unplugged, then calling IOHIDDeviceClose() will crash. */ if (!dev->disconnected) { - IOHIDDeviceClose(dev->device_handle, kIOHIDOptionsTypeNone); + IOHIDDeviceClose(dev->device_handle, kIOHIDOptionsTypeSeizeDevice); } /* Clear out the queue of received reports. */ @@ -1012,6 +1000,7 @@ void HID_API_EXPORT hid_close(hid_device *dev) return_data(dev, NULL, 0); } pthread_mutex_unlock(&dev->mutex); + CFRelease(dev->device_handle); free_hid_device(dev); } @@ -1033,7 +1022,7 @@ int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *s int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen) { - // TODO: + /* TODO: */ return 0; } @@ -1041,7 +1030,7 @@ 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) { - // TODO: + /* TODO: */ return NULL; } @@ -1051,6 +1040,7 @@ HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev) + #if 0 static int32_t get_location_id(IOHIDDeviceRef device) { @@ -1096,8 +1086,6 @@ int main(void) IOHIDDeviceRef *device_array = calloc(num_devices, sizeof(IOHIDDeviceRef)); CFSetGetValues(device_set, (const void **) device_array); - setlocale(LC_ALL, ""); - for (i = 0; i < num_devices; i++) { IOHIDDeviceRef dev = device_array[i]; printf("Device: %p\n", dev); diff --git a/gr-fcd/lib/hid/hidwin.c b/gr-fcd/lib/hid/hidwin.c index 5d34aadf48..86810d7e56 100644 --- a/gr-fcd/lib/hid/hidwin.c +++ b/gr-fcd/lib/hid/hidwin.c @@ -8,10 +8,10 @@ 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 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. @@ -36,7 +36,11 @@ typedef LONG NTSTATUS; #define _wcsdup wcsdup #endif -//#define HIDAPI_USE_DDK +/* The maximum number of characters that can be passed into the + HidD_Get*String() functions without it failing.*/ +#define MAX_STRING_WCHARS 0xFFF + +/*#define HIDAPI_USE_DDK*/ #ifdef __cplusplus extern "C" { @@ -47,13 +51,13 @@ extern "C" { #include <hidsdi.h> #endif - // Copied from inc/ddk/hidclass.h, part of the Windows DDK. + /* 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" +} /* extern "C" */ #endif #include <stdio.h> @@ -62,8 +66,11 @@ extern "C" { #include "hidapi.h" +#undef MIN +#define MIN(x,y) ((x) < (y)? (x): (y)) + #ifdef _MSC_VER - // Thanks Microsoft, but I know how to use strncpy(). + /* Thanks Microsoft, but I know how to use strncpy(). */ #pragma warning(disable:4996) #endif @@ -72,10 +79,10 @@ 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. + /* 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; @@ -93,8 +100,8 @@ extern "C" { USHORT Reserved[17]; USHORT fields_not_used_by_hidapi[10]; } HIDP_CAPS, *PHIDP_CAPS; - typedef char* HIDP_PREPARSED_DATA; - #define HIDP_STATUS_SUCCESS 0x0 + 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); @@ -103,9 +110,10 @@ extern "C" { 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, HIDP_PREPARSED_DATA **preparsed_data); - typedef BOOLEAN (__stdcall *HidD_FreePreparsedData_)(HIDP_PREPARSED_DATA *preparsed_data); - typedef BOOLEAN (__stdcall *HidP_GetCaps_)(HIDP_PREPARSED_DATA *preparsed_data, HIDP_CAPS *caps); + 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,14 +125,16 @@ 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; -#endif // HIDAPI_USE_DDK +#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; @@ -138,17 +148,26 @@ 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); + dev->ol.hEvent = CreateEvent(NULL, FALSE, FALSE /*initial 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) { @@ -160,11 +179,11 @@ static void register_error(hid_device *device, const char *op) NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPWSTR)&msg, 0/*sz*/, + (LPVOID)&msg, 0/*sz*/, NULL); - - // Get rid of the CR and LF that FormatMessage() sticks at the - // end of the message. Thanks Microsoft! + + /* 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') { @@ -174,8 +193,8 @@ static void register_error(hid_device *device, const char *op) ptr++; } - // Store the message off in the Device entry so that - // the hid_error() function can pick it up. + /* 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; } @@ -196,6 +215,7 @@ static int lookup_functions() RESOLVE(HidD_GetPreparsedData); RESOLVE(HidD_FreePreparsedData); RESOLVE(HidP_GetCaps); + RESOLVE(HidD_SetNumInputBuffers); #undef RESOLVE } else @@ -205,34 +225,20 @@ static int lookup_functions() } #endif -static HANDLE open_device(const char *path) +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; - /* First, try to open with sharing mode turned off. This will make it so - that a HID device can only be opened once. This is to be consistent - with the behavior on the other platforms. */ handle = CreateFileA(path, - GENERIC_WRITE |GENERIC_READ, - 0, /*share mode*/ + desired_access, + share_mode, NULL, OPEN_EXISTING, - FILE_FLAG_OVERLAPPED,//FILE_ATTRIBUTE_NORMAL, + FILE_FLAG_OVERLAPPED,/*FILE_ATTRIBUTE_NORMAL,*/ 0); - if (handle == INVALID_HANDLE_VALUE) { - /* Couldn't open the device. Some devices must be opened - with sharing enabled (even though they are only opened once), - so try it here. */ - handle = CreateFileA(path, - GENERIC_WRITE |GENERIC_READ, - FILE_SHARE_READ|FILE_SHARE_WRITE, /*share mode*/ - NULL, - OPEN_EXISTING, - FILE_FLAG_OVERLAPPED,//FILE_ATTRIBUTE_NORMAL, - 0); - } - return handle; } @@ -264,29 +270,31 @@ int HID_API_EXPORT hid_exit(void) 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 *root = NULL; /* return object */ struct hid_device_info *cur_dev = NULL; - // Windows objects for interacting with the driver. + /* 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. + /* 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. + /* 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. - + + /* Iterate over each device in the HID class, looking for the right one. */ + for (;;) { HANDLE write_handle = INVALID_HANDLE_VALUE; DWORD required_size = 0; @@ -297,16 +305,16 @@ struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned shor &InterfaceClassGuid, device_index, &device_interface_data); - + if (!res) { - // A return of FALSE from this function means that - // there are no more devices. + /* 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. + /* 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, @@ -314,13 +322,13 @@ struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned shor &required_size, NULL); - // Allocate a long enough structure for device_interface_detail_data. + /* 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. + /* 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, @@ -329,42 +337,67 @@ struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned shor NULL); if (!res) { - //register_error(dev, "Unable to call SetupDiGetDeviceInterfaceDetail"); - // Continue to the next device. + /* 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); + /* Open a handle to the device */ + write_handle = open_device(device_interface_detail_data->DevicePath, TRUE); - // Check validity of write_handle. + /* Check validity of write_handle. */ if (write_handle == INVALID_HANDLE_VALUE) { - // Unable to open the device. + /* Unable to open the device. */ //register_error(dev, "CreateFile"); goto cont_close; - } + } - // Get the Vendor ID and Product ID for this device. + /* 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 && product_id == 0x0) || - (attrib.VendorID == vendor_id && attrib.ProductID == product_id)) { + /* 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; - HIDP_PREPARSED_DATA *pp_data = NULL; + PHIDP_PREPARSED_DATA pp_data = NULL; HIDP_CAPS caps; BOOLEAN res; NTSTATUS nt_res; - wchar_t wstr[WSTR_LEN]; // TODO: Determine Size + wchar_t wstr[WSTR_LEN]; /* TODO: Determine Size */ size_t len; /* VID/PID match. Create the record. */ @@ -377,7 +410,7 @@ struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned shor } cur_dev = tmp; - // Get the Usage Page and Usage for this device. + /* 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); @@ -388,7 +421,7 @@ struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned shor HidD_FreePreparsedData(pp_data); } - + /* Fill out the record */ cur_dev->next = NULL; str = device_interface_detail_data->DevicePath; @@ -452,14 +485,14 @@ struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned shor cont_close: CloseHandle(write_handle); cont: - // We no longer need the detail data. It can be freed + /* We no longer need the detail data. It can be freed */ free(device_interface_detail_data); device_index++; } - // Close the device information handle. + /* Close the device information handle. */ SetupDiDestroyDeviceInfoList(device_info_set); return root; @@ -468,7 +501,7 @@ cont: 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. + /* 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; @@ -482,13 +515,13 @@ void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *d } -HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, wchar_t *serial_number) +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. + /* 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) { @@ -514,7 +547,7 @@ HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsi } hid_free_enumeration(devs); - + return handle; } @@ -522,7 +555,7 @@ HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path) { hid_device *dev; HIDP_CAPS caps; - HIDP_PREPARSED_DATA *pp_data = NULL; + PHIDP_PREPARSED_DATA pp_data = NULL; BOOLEAN res; NTSTATUS nt_res; @@ -532,17 +565,24 @@ HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path) dev = new_hid_device(); - // Open a handle to the device - dev->device_handle = open_device(path); + /* Open a handle to the device */ + dev->device_handle = open_device(path, FALSE); - // Check validity of write_handle. + /* Check validity of write_handle. */ if (dev->device_handle == INVALID_HANDLE_VALUE) { - // Unable to open the device. + /* Unable to open the device. */ register_error(dev, "CreateFile"); goto err; } - // Get the Input Report length for the device. + /* 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) { register_error(dev, "HidD_GetPreparsedData"); @@ -550,9 +590,10 @@ HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path) } nt_res = HidP_GetCaps(pp_data, &caps); if (nt_res != HIDP_STATUS_SUCCESS) { - register_error(dev, "HidP_GetCaps"); + 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); @@ -562,9 +603,8 @@ HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path) err_pp_data: HidD_FreePreparsedData(pp_data); -err: - CloseHandle(dev->device_handle); - free(dev); +err: + free_hid_device(dev); return NULL; } @@ -574,27 +614,52 @@ int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char * BOOL res; OVERLAPPED ol; + unsigned char *buf; memset(&ol, 0, sizeof(ol)); - res = WriteFile(dev->device_handle, data, length, NULL, &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. + /* WriteFile() failed. Return error. */ register_error(dev, "WriteFile"); - return -1; + bytes_written = -1; + goto end_of_function; } } - // Wait here until the write is done. This makes - // hid_write() synchronous. + /* 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. + /* The Write operation failed. */ register_error(dev, "WriteFile"); - return -1; + bytes_written = -1; + goto end_of_function; } +end_of_function: + if (buf != data) + free(buf); + return bytes_written; } @@ -602,21 +667,23 @@ int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char * 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. + /* Copy the handle for convenience. */ HANDLE ev = dev->ol.hEvent; if (!dev->read_pending) { - // Start an Overlapped I/O read. + /* 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. + /* ReadFile() has failed. + Clean up and return error. */ CancelIo(dev->device_handle); dev->read_pending = FALSE; goto end_of_function; @@ -625,21 +692,21 @@ int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char } if (milliseconds >= 0) { - // See if there is any data yet. + /* 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. + /* 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(). + /* 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. + + /* Set pending back to false, even if GetOverlappedResult() returned error. */ dev->read_pending = FALSE; if (res && bytes_read > 0) { @@ -649,21 +716,23 @@ int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char work like the other platforms, and to make it work more like the HID spec, we'll skip over this byte. */ bytes_read--; - memcpy(data, dev->read_buf+1, length); + 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. */ - memcpy(data, dev->read_buf, length); + 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; + + return copy_len; } int HID_API_EXPORT HID_API_CALL hid_read(hid_device *dev, unsigned char *data, size_t length) @@ -713,20 +782,26 @@ int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *dev, unsigned if (!res) { if (GetLastError() != ERROR_IO_PENDING) { - // DeviceIoControl() failed. Return error. + /* 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. + /* 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. + /* The operation failed. */ 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 } @@ -736,18 +811,14 @@ void HID_API_EXPORT HID_API_CALL hid_close(hid_device *dev) if (!dev) return; CancelIo(dev->device_handle); - CloseHandle(dev->ol.hEvent); - CloseHandle(dev->device_handle); - LocalFree(dev->last_error_str); - free(dev->read_buf); - free(dev); + 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, 2 * maxlen); + res = HidD_GetManufacturerString(dev->device_handle, string, sizeof(wchar_t) * MIN(maxlen, MAX_STRING_WCHARS)); if (!res) { register_error(dev, "HidD_GetManufacturerString"); return -1; @@ -760,7 +831,7 @@ int HID_API_EXPORT_CALL HID_API_CALL hid_get_product_string(hid_device *dev, wch { BOOL res; - res = HidD_GetProductString(dev->device_handle, string, 2 * maxlen); + res = HidD_GetProductString(dev->device_handle, string, sizeof(wchar_t) * MIN(maxlen, MAX_STRING_WCHARS)); if (!res) { register_error(dev, "HidD_GetProductString"); return -1; @@ -773,7 +844,7 @@ int HID_API_EXPORT_CALL HID_API_CALL hid_get_serial_number_string(hid_device *de { BOOL res; - res = HidD_GetSerialNumberString(dev->device_handle, string, 2 * maxlen); + res = HidD_GetSerialNumberString(dev->device_handle, string, sizeof(wchar_t) * MIN(maxlen, MAX_STRING_WCHARS)); if (!res) { register_error(dev, "HidD_GetSerialNumberString"); return -1; @@ -786,7 +857,7 @@ int HID_API_EXPORT_CALL HID_API_CALL hid_get_indexed_string(hid_device *dev, int { BOOL res; - res = HidD_GetIndexedString(dev->device_handle, string_index, string, 2 * maxlen); + res = HidD_GetIndexedString(dev->device_handle, string_index, string, sizeof(wchar_t) * MIN(maxlen, MAX_STRING_WCHARS)); if (!res) { register_error(dev, "HidD_GetIndexedString"); return -1; @@ -802,10 +873,10 @@ HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev) } -//#define PICPGM -//#define S11 +/*#define PICPGM*/ +/*#define S11*/ #define P32 -#ifdef S11 +#ifdef S11 unsigned short VendorID = 0xa0a0; unsigned short ProductID = 0x0001; #endif @@ -831,36 +902,36 @@ int __cdecl main(int argc, char* argv[]) UNREFERENCED_PARAMETER(argc); UNREFERENCED_PARAMETER(argv); - // Set up the command buffer. + /* Set up the command buffer. */ memset(buf,0x00,sizeof(buf)); buf[0] = 0; buf[1] = 0x81; + - - // Open the device. + /* Open the device. */ int handle = open(VendorID, ProductID, L"12345"); if (handle < 0) printf("unable to open device\n"); - // Toggle LED (cmd 0x80) + /* Toggle LED (cmd 0x80) */ buf[1] = 0x80; res = write(handle, buf, 65); if (res < 0) printf("Unable to write()\n"); - // Request state (cmd 0x81) + /* Request state (cmd 0x81) */ buf[1] = 0x81; write(handle, buf, 65); if (res < 0) printf("Unable to write() (2)\n"); - // Read requested state + /* Read requested state */ read(handle, buf, 65); if (res < 0) printf("Unable to read()\n"); - // Print out the returned buffer. + /* Print out the returned buffer. */ for (int i = 0; i < 4; i++) printf("buf[%d]: %d\n", i, buf[i]); @@ -869,5 +940,5 @@ int __cdecl main(int argc, char* argv[]) #endif #ifdef __cplusplus -} // extern "C" +} /* extern "C" */ #endif |