From d6db1ec8ca6495ab55c9be9e184bcfb11cb96b6d Mon Sep 17 00:00:00 2001 From: Alan Ott Date: Sat, 3 Jul 2010 15:31:21 -0400 Subject: [PATCH] Worked around a problem in Linux kernels prior to 2.6.34. Fixed non-blocking operation. --- linux/hid.c | 130 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 128 insertions(+), 2 deletions(-) diff --git a/linux/hid.c b/linux/hid.c index 5050254..0ab0c54 100644 --- a/linux/hid.c +++ b/linux/hid.c @@ -13,18 +13,25 @@ long as this copyright notice remains intact. ********************************************************/ +/* C */ #include #include #include #include +#include /* Unix */ #include #include #include +#include +#include #include -#include "libudev.h" +/* Linux */ +#include +#include +#include #include "hidapi.h" @@ -32,12 +39,14 @@ struct Device { int valid; int device_handle; int blocking; + int uses_numbered_reports; }; #define MAX_DEVICES 64 static struct Device devices[MAX_DEVICES]; static int devices_initialized = 0; +static __u32 kernel_version = 0; static void register_error(struct Device *device, const char *op) { @@ -62,6 +71,68 @@ static wchar_t *copy_udev_string(struct udev_device *dev, const char *udev_name) 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 devices 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; +} + struct hid_device HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short product_id) { struct udev *udev; @@ -239,6 +310,7 @@ int HID_API_EXPORT hid_open_path(const char *path) devices[i].valid = 0; devices[i].device_handle = -1; devices[i].blocking = 1; + devices[i].uses_numbered_reports = 0; } devices_initialized = 1; } @@ -247,6 +319,9 @@ int HID_API_EXPORT hid_open_path(const char *path) for (i = 0; i < MAX_DEVICES; i++) { if (!devices[i].valid) { devices[i].valid = 1; + devices[i].device_handle = -1; + devices[i].blocking = 1; + devices[i].uses_numbered_reports = 0; handle = i; dev = &devices[i]; break; @@ -256,12 +331,54 @@ int HID_API_EXPORT hid_open_path(const char *path) if (handle < 0) { return -1; } + + 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 i, 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"); + else + printf("Report Descriptor Size: %d\n", desc_size); + + + /* 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 handle; } else { @@ -269,7 +386,6 @@ int HID_API_EXPORT hid_open_path(const char *path) dev->valid = 0; return -1; } - } @@ -304,6 +420,16 @@ int HID_API_EXPORT hid_read(int device, unsigned char *data, size_t length) dev = &devices[device]; 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; }