From c28473c57c90991f73036c16c2dd1faa356941bd Mon Sep 17 00:00:00 2001 From: Alexandru Csete Date: Mon, 28 May 2012 13:55:26 +0200 Subject: [PATCH] Update HID API and switch to using the hidraw driver. --- gqrx.pro | 6 +- input/fcdctl/hid-libusb.c | 94 ++++-- input/fcdctl/hidapi.h | 2 +- input/fcdctl/hidmac.c | 12 +- input/fcdctl/hidraw.c | 601 ++++++++++++++++++++++++++++++++++++++ input/fcdctl/hidwin.c | 16 +- 6 files changed, 683 insertions(+), 48 deletions(-) create mode 100644 input/fcdctl/hidraw.c diff --git a/gqrx.pro b/gqrx.pro index f27581d0..14cd10f8 100644 --- a/gqrx.pro +++ b/gqrx.pro @@ -53,7 +53,8 @@ SOURCES += \ input/rx_source_base.cpp \ input/rx_source_fcd.cpp \ input/fcdctl/fcd.c \ - input/fcdctl/hid-libusb.c \ # FIXME: Linux only + input/fcdctl/hidraw.c \ # FIXME: Linux only +# input/fcdctl/hid-libusb.c \ # input/fcdctl/hidwin.c \ # input/fcdctl/hidmac.c \ pulseaudio/pa_device_list.cc \ # FIXME: Linux only @@ -145,8 +146,9 @@ unix { CONFIG += link_pkgconfig PKGCONFIG += gnuradio-core gnuradio-audio PKGCONFIG += libpulse libpulse-simple - PKGCONFIG += libusb-1.0 +# PKGCONFIG += libusb-1.0 PKGCONFIG += gnuradio-osmosdr gnuradio-uhd gnuradio-fcd + LIBS += -ludev } macx-g++ { diff --git a/input/fcdctl/hid-libusb.c b/input/fcdctl/hid-libusb.c index 9ec3cbc2..0da24c53 100644 --- a/input/fcdctl/hid-libusb.c +++ b/input/fcdctl/hid-libusb.c @@ -8,6 +8,7 @@ 8/22/2009 Linux Version - 6/2/2010 Libusb Version - 8/13/2010 + FreeBSD Version - 11/1/2011 Copyright 2009, All Rights Reserved. @@ -62,7 +63,7 @@ extern "C" { /* Uncomment to enable the retrieval of Usage and Usage Page in hid_enumerate(). Warning, this is very invasive as it requires the detach and re-attach of the kernel driver. See comments inside hid_enumerate(). -Linux/libusb HIDAPI programs are encouraged to use the interface number +libusb HIDAPI programs are encouraged to use the interface number instead to differentiate between interfaces on a composite HID device. */ /*#define INVASIVE_GET_USAGE*/ @@ -114,18 +115,7 @@ static int return_data(hid_device *dev, unsigned char *data, size_t length); static hid_device *new_hid_device(void) { hid_device *dev = calloc(1, sizeof(hid_device)); - dev->device_handle = NULL; - dev->input_endpoint = 0; - dev->output_endpoint = 0; - dev->input_ep_max_packet_size = 0; - dev->interface = 0; - dev->manufacturer_index = 0; - dev->product_index = 0; - dev->serial_index = 0; dev->blocking = 1; - dev->shutdown_thread = 0; - dev->transfer = NULL; - dev->input_reports = NULL; pthread_mutex_init(&dev->mutex, NULL); pthread_cond_init(&dev->condition, NULL); @@ -146,7 +136,7 @@ static void free_hid_device(hid_device *dev) } #if 0 -//TODO: Implement this funciton on Linux. +//TODO: Implement this funciton on hidapi/libusb.. static void register_error(hid_device *device, const char *op) { @@ -255,6 +245,27 @@ 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. + + Note that the data parameter is Unicode in UTF-16LE encoding. + Return value is the number of bytes in data, or LIBUSB_ERROR_*. + */ +static inline int libusb_get_string_descriptor(libusb_device_handle *dev, + uint8_t descriptor_index, uint16_t lang_id, + unsigned char *data, int length) +{ + return libusb_control_transfer(dev, + LIBUSB_ENDPOINT_IN | 0x0, /* Endpoint 0 IN */ + LIBUSB_REQUEST_GET_DESCRIPTOR, + (LIBUSB_DT_STRING << 8) | descriptor_index, + lang_id, data, (uint16_t) length, 1000); +} + +#endif + /* Get the first language the device says it reports. This comes from USB string #0. */ @@ -317,7 +328,11 @@ static wchar_t *get_usb_string(libusb_device_handle *dev, uint8_t idx) size_t inbytes; size_t outbytes; size_t res; +#ifdef __FreeBSD__ + const char *inptr; +#else char *inptr; +#endif char *outptr; /* Determine which language to use. */ @@ -335,25 +350,27 @@ static wchar_t *get_usb_string(libusb_device_handle *dev, uint8_t idx) if (len < 0) return NULL; - buf[sizeof(buf)-1] = '\0'; - - if (len+1 < sizeof(buf)) - buf[len+1] = '\0'; + /* buf does not need to be explicitly NULL-terminated because + it is only passed into iconv() which does not need it. */ /* Initialize iconv. */ - ic = iconv_open("UTF-32", "UTF-16"); - if (ic == (iconv_t)-1) + ic = iconv_open("WCHAR_T", "UTF-16LE"); + if (ic == (iconv_t)-1) { + LOG("iconv_open() failed\n"); return NULL; + } - /* Convert to UTF-32 (wchar_t on glibc systems). + /* Convert to native wchar_t (UTF-32 on glibc/BSD systems). Skip the first character (2-bytes). */ inptr = buf+2; inbytes = len-2; outptr = (char*) wbuf; outbytes = sizeof(wbuf); res = iconv(ic, &inptr, &inbytes, &outptr, &outbytes); - if (res == (size_t)-1) + if (res == (size_t)-1) { + LOG("iconv() failed\n"); goto err; + } /* Write the terminating NULL. */ wbuf[sizeof(wbuf)/sizeof(wbuf[0])-1] = 0x00000000; @@ -361,7 +378,7 @@ static wchar_t *get_usb_string(libusb_device_handle *dev, uint8_t idx) *((wchar_t*)outptr) = 0x00000000; /* Allocate and copy the string. */ - str = wcsdup(wbuf+1); + str = wcsdup(wbuf); err: iconv_close(ic); @@ -385,8 +402,16 @@ static char *make_path(libusb_device *dev, int interface_number) int HID_API_EXPORT hid_init(void) { if (!usb_context) { + const char *locale; + + /* Init Libusb */ if (libusb_init(&usb_context)) return -1; + + /* Set the locale if it's not set. */ + locale = setlocale(LC_CTYPE, NULL); + if (!locale) + setlocale(LC_CTYPE, ""); } return 0; @@ -413,8 +438,6 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, struct hid_device_info *root = NULL; // return object struct hid_device_info *cur_dev = NULL; - setlocale(LC_ALL,""); - hid_init(); num_devs = libusb_get_device_list(usb_context, &devs); @@ -578,7 +601,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; @@ -616,6 +639,7 @@ hid_device * hid_open(unsigned short vendor_id, unsigned short product_id, wchar static void read_callback(struct libusb_transfer *transfer) { hid_device *dev = transfer->user_data; + int res; if (transfer->status == LIBUSB_TRANSFER_COMPLETED) { @@ -668,7 +692,11 @@ static void read_callback(struct libusb_transfer *transfer) } /* Re-submit the transfer object. */ - libusb_submit_transfer(transfer); + res = libusb_submit_transfer(transfer); + if (res != 0) { + LOG("Unable to submit URB. libusb error code: %d\n", res); + dev->shutdown_thread = 1; + } } @@ -702,8 +730,16 @@ static void *read_thread(void *param) int res; res = libusb_handle_events(usb_context); if (res < 0) { - /* There was an error. Break out of this loop. */ - break; + /* There was an error. */ + LOG("read_thread(): libusb reports error # %d\n", res); + + /* Break out of this loop only on fatal error.*/ + if (res != LIBUSB_ERROR_BUSY && + res != LIBUSB_ERROR_TIMEOUT && + res != LIBUSB_ERROR_OVERFLOW && + res != LIBUSB_ERROR_INTERRUPTED) { + break; + } } } @@ -748,8 +784,6 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path) int d = 0; int good_open = 0; - setlocale(LC_ALL,""); - hid_init(); num_devs = libusb_get_device_list(usb_context, &devs); diff --git a/input/fcdctl/hidapi.h b/input/fcdctl/hidapi.h index 6c8c4839..2c26d508 100644 --- a/input/fcdctl/hidapi.h +++ b/input/fcdctl/hidapi.h @@ -155,7 +155,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. diff --git a/input/fcdctl/hidmac.c b/input/fcdctl/hidmac.c index b7d738e9..3a6200f7 100644 --- a/input/fcdctl/hidmac.c +++ b/input/fcdctl/hidmac.c @@ -291,10 +291,10 @@ static int get_string_property(IOHIDDeviceRef device, CFStringRef prop, wchar_t &used_buf_len); buf[chars_copied] = 0; - return chars_copied; + return 0; } else - return 0; + return -1; } @@ -442,8 +442,6 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, CFIndex num_devices; int i; - setlocale(LC_ALL,""); - /* Set up the HID Manager if it hasn't been done */ if (hid_init() < 0) return NULL; @@ -543,7 +541,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; @@ -752,6 +750,7 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path) char str[32]; free(device_array); + CFRetain(os_dev); CFRelease(device_set); dev->device_handle = os_dev; @@ -1044,6 +1043,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); } @@ -1128,8 +1128,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/input/fcdctl/hidraw.c b/input/fcdctl/hidraw.c new file mode 100644 index 00000000..bd299f7a --- /dev/null +++ b/input/fcdctl/hidraw.c @@ -0,0 +1,601 @@ +/******************************************************* + HIDAPI - Multi-Platform library for + communication with HID devices. + + Alan Ott + Signal 11 Software + + 8/22/2009 + Linux Version - 6/2/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 + 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 . +********************************************************/ + +/* C */ +#include +#include +#include +#include +#include + +/* Unix */ +#include +#include +#include +#include +#include +#include +#include + +/* Linux */ +#include +#include +#include + +#include "hidapi.h" + +/* Definitions from linux/hidraw.h. Since these are new, some distros + may not have header files which contain them. */ +#ifndef HIDIOCSFEATURE +#define HIDIOCSFEATURE(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x06, len) +#endif +#ifndef HIDIOCGFEATURE +#define HIDIOCGFEATURE(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x07, len) +#endif + +struct hid_device_ { + int device_handle; + int blocking; + int uses_numbered_reports; +}; + + +static __u32 kernel_version = 0; + +hid_device *new_hid_device() +{ + hid_device *dev = calloc(1, sizeof(hid_device)); + dev->device_handle = -1; + dev->blocking = 1; + dev->uses_numbered_reports = 0; + + return dev; +} + +static void register_error(hid_device *device, const char *op) +{ + +} + +/* Get an attribute value from a udev_device and return it as a whar_t + string. The returned string must be freed with free() when done.*/ +static wchar_t *copy_udev_string(struct udev_device *dev, const char *udev_name) +{ + const char *str; + wchar_t *ret = NULL; + str = udev_device_get_sysattr_value(dev, udev_name); + if (str) { + /* Convert the string from UTF-8 to wchar_t */ + size_t wlen = mbstowcs(NULL, str, 0); + ret = calloc(wlen+1, sizeof(wchar_t)); + mbstowcs(ret, str, wlen+1); + ret[wlen] = 0x0000; + } + + return ret; +} + +/* uses_numbered_reports() returns 1 if report_descriptor describes a device + which contains numbered reports. */ +static int uses_numbered_reports(__u8 *report_descriptor, __u32 size) { + int i = 0; + int size_code; + int data_len, key_size; + + while (i < size) { + int key = report_descriptor[i]; + + /* Check for the Report ID key */ + if (key == 0x85/*Report ID*/) { + /* This device has a Report ID, which means it uses + numbered reports. */ + return 1; + } + + //printf("key: %02hhx\n", key); + + if ((key & 0xf0) == 0xf0) { + /* This is a Long Item. The next byte contains the + length of the data section (value) for this key. + See the HID specification, version 1.11, section + 6.2.2.3, titled "Long Items." */ + if (i+1 < size) + data_len = report_descriptor[i+1]; + else + data_len = 0; /* malformed report */ + key_size = 3; + } + else { + /* This is a Short Item. The bottom two bits of the + key contain the size code for the data section + (value) for this key. Refer to the HID + specification, version 1.11, section 6.2.2.2, + titled "Short Items." */ + size_code = key & 0x3; + switch (size_code) { + case 0: + case 1: + case 2: + data_len = size_code; + break; + case 3: + data_len = 4; + break; + default: + /* Can't ever happen since size_code is & 0x3 */ + data_len = 0; + break; + }; + key_size = 1; + } + + /* Skip over this key and it's associated data */ + i += data_len + key_size; + } + + /* Didn't find a Report ID key. Device doesn't use numbered reports. */ + return 0; +} + +static int get_device_string(hid_device *dev, const char *key, wchar_t *string, size_t maxlen) +{ + struct udev *udev; + struct udev_device *udev_dev, *parent; + struct stat s; + int ret = -1; + + /* Create the udev object */ + udev = udev_new(); + if (!udev) { + printf("Can't create udev\n"); + return -1; + } + + /* Get the dev_t (major/minor numbers) from the file handle. */ + fstat(dev->device_handle, &s); + /* Open a udev device from the dev_t. 'c' means character device. */ + udev_dev = udev_device_new_from_devnum(udev, 'c', s.st_rdev); + if (udev_dev) { + const char *str; + /* Find the parent USB Device */ + parent = udev_device_get_parent_with_subsystem_devtype( + udev_dev, + "usb", + "usb_device"); + if (parent) { + str = udev_device_get_sysattr_value(parent, key); + if (str) { + /* Convert the string from UTF-8 to wchar_t */ + ret = (mbstowcs(string, str, maxlen) < 0)? -1: 0; + goto end; + } + } + } + +end: + udev_device_unref(udev_dev); + // parent doesn't need to be (and can't be) unref'd. + // I'm not sure why, but it'll throw double-free() errors. + udev_unref(udev); + + return ret; +} + +int HID_API_EXPORT hid_init(void) +{ + const char *locale; + + /* Set the locale if it's not set. */ + locale = setlocale(LC_CTYPE, NULL); + if (!locale) + setlocale(LC_CTYPE, ""); + + return 0; +} + +int HID_API_EXPORT hid_exit(void) +{ + /* Nothing to do for this in the Linux/hidraw implementation. */ + return 0; +} + +struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short product_id) +{ + struct udev *udev; + struct udev_enumerate *enumerate; + struct udev_list_entry *devices, *dev_list_entry; + + struct hid_device_info *root = NULL; // return object + struct hid_device_info *cur_dev = NULL; + + hid_init(); + + /* Create the udev object */ + udev = udev_new(); + if (!udev) { + printf("Can't create udev\n"); + return NULL; + } + + /* Create a list of the devices in the 'hidraw' subsystem. */ + enumerate = udev_enumerate_new(udev); + udev_enumerate_add_match_subsystem(enumerate, "hidraw"); + udev_enumerate_scan_devices(enumerate); + devices = udev_enumerate_get_list_entry(enumerate); + /* For each item, see if it matches the vid/pid, and if so + create a udev_device record for it */ + udev_list_entry_foreach(dev_list_entry, devices) { + const char *sysfs_path; + const char *dev_path; + const char *str; + struct udev_device *hid_dev; // The device's HID udev node. + struct udev_device *dev; // The actual hardware device. + struct udev_device *intf_dev; // The device's interface (in the USB sense). + unsigned short dev_vid; + unsigned short dev_pid; + + /* Get the filename of the /sys entry for the device + and create a udev_device object (dev) representing it */ + sysfs_path = udev_list_entry_get_name(dev_list_entry); + hid_dev = udev_device_new_from_syspath(udev, sysfs_path); + dev_path = udev_device_get_devnode(hid_dev); + + /* The device pointed to by hid_dev contains information about + the hidraw device. In order to get information about the + USB device, get the parent device with the + subsystem/devtype pair of "usb"/"usb_device". This will + be several levels up the tree, but the function will find + it.*/ + dev = udev_device_get_parent_with_subsystem_devtype( + hid_dev, + "usb", + "usb_device"); + if (!dev) { + /* Unable to find parent usb device. */ + goto next; + } + + /* Get the VID/PID of the device */ + str = udev_device_get_sysattr_value(dev,"idVendor"); + dev_vid = (str)? strtol(str, NULL, 16): 0x0; + str = udev_device_get_sysattr_value(dev, "idProduct"); + dev_pid = (str)? strtol(str, NULL, 16): 0x0; + + /* Check the VID/PID against the arguments */ + if ((vendor_id == 0x0 && product_id == 0x0) || + (vendor_id == dev_vid && product_id == dev_pid)) { + struct hid_device_info *tmp; + size_t len; + + /* VID/PID match. Create the record. */ + tmp = malloc(sizeof(struct hid_device_info)); + if (cur_dev) { + cur_dev->next = tmp; + } + else { + root = tmp; + } + cur_dev = tmp; + + /* Fill out the record */ + cur_dev->next = NULL; + str = dev_path; + if (str) { + len = strlen(str); + cur_dev->path = calloc(len+1, sizeof(char)); + strncpy(cur_dev->path, str, len+1); + cur_dev->path[len] = '\0'; + } + else + cur_dev->path = NULL; + + /* Serial Number */ + cur_dev->serial_number + = copy_udev_string(dev, "serial"); + + /* Manufacturer and Product strings */ + cur_dev->manufacturer_string + = copy_udev_string(dev, "manufacturer"); + cur_dev->product_string + = copy_udev_string(dev, "product"); + + /* VID/PID */ + cur_dev->vendor_id = dev_vid; + cur_dev->product_id = dev_pid; + + /* Release Number */ + str = udev_device_get_sysattr_value(dev, "bcdDevice"); + cur_dev->release_number = (str)? strtol(str, NULL, 16): 0x0; + + /* Interface Number */ + cur_dev->interface_number = -1; + /* Get a handle to the interface's udev node. */ + intf_dev = udev_device_get_parent_with_subsystem_devtype( + hid_dev, + "usb", + "usb_interface"); + if (intf_dev) { + str = udev_device_get_sysattr_value(intf_dev, "bInterfaceNumber"); + cur_dev->interface_number = (str)? strtol(str, NULL, 16): -1; + } + } + else + goto next; + + next: + udev_device_unref(hid_dev); + /* dev and intf_dev don't need to be (and can't be) + unref()d. It will cause a double-free() error. I'm not + sure why. */ + } + /* Free the enumerator and udev objects. */ + udev_enumerate_unref(enumerate); + udev_unref(udev); + + return root; +} + +void HID_API_EXPORT hid_free_enumeration(struct hid_device_info *devs) +{ + struct hid_device_info *d = devs; + while (d) { + struct hid_device_info *next = d->next; + free(d->path); + free(d->serial_number); + free(d->manufacturer_string); + free(d->product_string); + free(d); + d = next; + } +} + +hid_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; + 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_device * HID_API_EXPORT hid_open_path(const char *path) +{ + hid_device *dev = NULL; + + hid_init(); + + 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); + + // If we have a good handle, return it. + if (dev->device_handle > 0) { + + /* Get the report descriptor */ + int res, desc_size = 0; + struct hidraw_report_descriptor rpt_desc; + + memset(&rpt_desc, 0x0, sizeof(rpt_desc)); + + /* Get Report Descriptor Size */ + res = ioctl(dev->device_handle, HIDIOCGRDESCSIZE, &desc_size); + if (res < 0) + perror("HIDIOCGRDESCSIZE"); + + + /* Get Report Descriptor */ + rpt_desc.size = desc_size; + res = ioctl(dev->device_handle, HIDIOCGRDESC, &rpt_desc); + if (res < 0) { + perror("HIDIOCGRDESC"); + } else { + /* Determine if this device uses numbered reports. */ + dev->uses_numbered_reports = + uses_numbered_reports(rpt_desc.value, + rpt_desc.size); + } + + return dev; + } + else { + // Unable to open any devices. + free(dev); + return NULL; + } +} + + +int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t length) +{ + int bytes_written; + + bytes_written = write(dev->device_handle, data, length); + + return bytes_written; +} + + +int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds) +{ + int bytes_read; + + if (milliseconds != 0) { + /* milliseconds is -1 or > 0. In both cases, we want to + call poll() and wait for data to arrive. -1 means + INFINITE. */ + int ret; + struct pollfd fds; + + fds.fd = dev->device_handle; + fds.events = POLLIN; + fds.revents = 0; + ret = poll(&fds, 1, milliseconds); + if (ret == -1 || ret == 0) + /* Error or timeout */ + return ret; + } + + bytes_read = read(dev->device_handle, data, length); + if (bytes_read < 0 && errno == EAGAIN) + bytes_read = 0; + + if (bytes_read >= 0 && + kernel_version < KERNEL_VERSION(2,6,34) && + dev->uses_numbered_reports) { + /* Work around a kernel bug. Chop off the first byte. */ + memmove(data, data+1, bytes_read); + bytes_read--; + } + + return bytes_read; +} + +int HID_API_EXPORT 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_set_nonblocking(hid_device *dev, int nonblock) +{ + int flags, res; + + flags = fcntl(dev->device_handle, F_GETFL, 0); + if (flags >= 0) { + if (nonblock) + res = fcntl(dev->device_handle, F_SETFL, flags | O_NONBLOCK); + else + res = fcntl(dev->device_handle, F_SETFL, flags & ~O_NONBLOCK); + } + else + return -1; + + if (res < 0) { + return -1; + } + else { + dev->blocking = !nonblock; + return 0; /* Success */ + } +} + + +int HID_API_EXPORT hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length) +{ + int res; + + res = ioctl(dev->device_handle, HIDIOCSFEATURE(length), data); + if (res < 0) + perror("ioctl (SFEATURE)"); + + return res; +} + +int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length) +{ + int res; + + res = ioctl(dev->device_handle, HIDIOCGFEATURE(length), data); + if (res < 0) + perror("ioctl (GFEATURE)"); + + + return res; +} + + +void HID_API_EXPORT hid_close(hid_device *dev) +{ + if (!dev) + return; + close(dev->device_handle); + free(dev); +} + + +int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + return get_device_string(dev, "manufacturer", string, maxlen); +} + +int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + return get_device_string(dev, "product", string, maxlen); +} + +int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen) +{ + return get_device_string(dev, "serial", string, maxlen); +} + +int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen) +{ + return -1; +} + + +HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev) +{ + return NULL; +} diff --git a/input/fcdctl/hidwin.c b/input/fcdctl/hidwin.c index 77fa93cd..5d795e1c 100644 --- a/input/fcdctl/hidwin.c +++ b/input/fcdctl/hidwin.c @@ -93,8 +93,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 +103,9 @@ 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); static HidD_GetAttributes_ HidD_GetAttributes; static HidD_GetSerialNumberString_ HidD_GetSerialNumberString; @@ -350,7 +350,7 @@ struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned shor #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; @@ -472,7 +472,7 @@ 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. struct hid_device_info *devs, *cur_dev; @@ -512,7 +512,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;