diff options
author | dl1ksv <dl1ksv@gmx.de> | 2013-07-15 22:05:01 +0200 |
---|---|---|
committer | Johnathan Corgan <johnathan@corganlabs.com> | 2013-07-17 15:46:38 -0700 |
commit | c0e969a3fb644c4919b8fe621c2ff9f7d2329fe3 (patch) | |
tree | 3982a4389214894dff685d06e0e71f500138f6df /gr-fcd/lib | |
parent | 212e41e8500348e984dd15082c3727c12d19f20f (diff) |
Applying hidapi patch from Hans de Goede
Diffstat (limited to 'gr-fcd/lib')
-rw-r--r-- | gr-fcd/lib/hid/hid-libusb.c | 193 |
1 files changed, 117 insertions, 76 deletions
diff --git a/gr-fcd/lib/hid/hid-libusb.c b/gr-fcd/lib/hid/hid-libusb.c index 82cbc91512..097f87215d 100644 --- a/gr-fcd/lib/hid/hid-libusb.c +++ b/gr-fcd/lib/hid/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. @@ -22,7 +23,7 @@ http://github.com/signal11/hidapi . ********************************************************/ -#define _GNU_SOURCE // needed for wcsdup() before glibc 2.10 +#define _GNU_SOURCE /* needed for wcsdup() before glibc 2.10 */ /* C */ #include <stdio.h> @@ -58,11 +59,15 @@ extern "C" { #define LOG(...) do {} while (0) #endif +#ifndef __FreeBSD__ +#define DETACH_KERNEL_DRIVER +#endif /* Uncomment to enable the retrieval of Usage and Usage Page in -hid_enumerate(). Warning, this is very invasive as it requires the detach +hid_enumerate(). Warning, on platforms different from FreeBSD +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*/ @@ -100,13 +105,14 @@ 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. */ struct input_report *input_reports; }; -static int initialized = 0; +static libusb_context *usb_context = NULL; uint16_t get_usb_code_for_current_locale(void); static int return_data(hid_device *dev, unsigned char *data, size_t length); @@ -114,18 +120,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 +141,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) { @@ -187,7 +182,7 @@ static uint32_t get_bytes(uint8_t *rpt, size_t len, size_t num_bytes, size_t cur static int get_usage(uint8_t *report_descriptor, size_t size, unsigned short *usage_page, unsigned short *usage) { - int i = 0; + unsigned int i = 0; int size_code; int data_len, key_size; int usage_found = 0, usage_page_found = 0; @@ -253,7 +248,28 @@ static int get_usage(uint8_t *report_descriptor, size_t size, return -1; /* failure */ } -#endif // INVASIVE_GET_USAGE +#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 @@ -272,7 +288,7 @@ static uint16_t get_first_language(libusb_device_handle *dev) if (len < 4) return 0x0; - return buf[1]; // First two bytes are len and descriptor type. + return buf[1]; /* First two bytes are len and descriptor type. */ } static int is_language_supported(libusb_device_handle *dev, uint16_t lang) @@ -308,7 +324,7 @@ static int is_language_supported(libusb_device_handle *dev, uint16_t lang) static wchar_t *get_usb_string(libusb_device_handle *dev, uint8_t idx) { char buf[512]; - unsigned int len; + int len; wchar_t *str = NULL; wchar_t wbuf[256]; @@ -317,7 +333,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 +355,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 +383,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); @@ -384,10 +406,17 @@ static char *make_path(libusb_device *dev, int interface_number) int HID_API_EXPORT hid_init(void) { - if (!initialized) { - if (libusb_init(NULL)) + if (!usb_context) { + const char *locale; + + /* Init Libusb */ + if (libusb_init(&usb_context)) return -1; - initialized = 1; + + /* Set the locale if it's not set. */ + locale = setlocale(LC_CTYPE, NULL); + if (!locale) + setlocale(LC_CTYPE, ""); } return 0; @@ -395,9 +424,9 @@ int HID_API_EXPORT hid_init(void) int HID_API_EXPORT hid_exit(void) { - if (initialized) { - libusb_exit(NULL); - initialized = 0; + if (usb_context) { + libusb_exit(usb_context); + usb_context = NULL; } return 0; @@ -411,15 +440,13 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, ssize_t num_devs; int i = 0; - struct hid_device_info *root = NULL; // return object + struct hid_device_info *root = NULL; /* return object */ struct hid_device_info *cur_dev = NULL; - setlocale(LC_ALL,""); - - if (!initialized) - hid_init(); + if(hid_init() < 0) + return NULL; - num_devs = libusb_get_device_list(NULL, &devs); + num_devs = libusb_get_device_list(usb_context, &devs); if (num_devs < 0) return NULL; while ((dev = devs[i++]) != NULL) { @@ -432,10 +459,6 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short dev_vid = desc.idVendor; unsigned short dev_pid = desc.idProduct; - /* HID's are defined at the interface level. */ - if (desc.bDeviceClass != LIBUSB_CLASS_PER_INTERFACE) - continue; - res = libusb_get_active_config_descriptor(dev, &conf_desc); if (res < 0) libusb_get_config_descriptor(dev, 0, &conf_desc); @@ -449,8 +472,8 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, interface_num = intf_desc->bInterfaceNumber; /* 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; /* VID/PID match. Create the record. */ @@ -484,6 +507,7 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, get_usb_string(handle, desc.iProduct); #ifdef INVASIVE_GET_USAGE +{ /* This section is removed because it is too invasive on the system. Getting a Usage Page @@ -499,9 +523,9 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, #if 0. For composite devices, use the interface field in the hid_device_info struct to distinguish between interfaces. */ - int detached = 0; unsigned char data[256]; - +#ifdef DETACH_KERNEL_DRIVER + int detached = 0; /* Usage Page and Usage */ res = libusb_kernel_driver_active(handle, interface_num); if (res == 1) { @@ -511,6 +535,7 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, else detached = 1; } +#endif res = libusb_claim_interface(handle, interface_num); if (res >= 0) { /* Get the HID Report Descriptor. */ @@ -533,14 +558,16 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, } else LOG("Can't claim interface %d\n", res); - +#ifdef DETACH_KERNEL_DRIVER /* Re-attach kernel driver if necessary. */ if (detached) { res = libusb_attach_kernel_driver(handle, interface_num); if (res < 0) LOG("Couldn't re-attach kernel driver.\n"); } -#endif /*******************/ +#endif +} +#endif /* INVASIVE_GET_USAGE */ libusb_close(handle); } @@ -618,6 +645,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) { @@ -656,10 +684,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) { @@ -670,7 +700,12 @@ 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; + dev->cancelled = 1; + } } @@ -696,25 +731,33 @@ static void *read_thread(void *param) from inside read_callback() */ libusb_submit_transfer(dev->transfer); - // Notify the main thread that the read thread is up and running. + /* Notify the main thread that the read thread is up and running. */ pthread_barrier_wait(&dev->barrier); /* Handle all the events. */ while (!dev->shutdown_thread) { int res; - res = libusb_handle_events(NULL); + 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; + } } } /* 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(NULL); - } + 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 @@ -741,21 +784,18 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path) { hid_device *dev = NULL; - dev = new_hid_device(); - libusb_device **devs; libusb_device *usb_dev; - ssize_t num_devs; int res; int d = 0; int good_open = 0; - setlocale(LC_ALL,""); + dev = new_hid_device(); - if (!initialized) - hid_init(); + if(hid_init() < 0) + return NULL; - num_devs = libusb_get_device_list(NULL, &devs); + libusb_get_device_list(usb_context, &devs); while ((usb_dev = devs[d++]) != NULL) { struct libusb_device_descriptor desc; struct libusb_config_descriptor *conf_desc = NULL; @@ -774,15 +814,15 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path) if (!strcmp(dev_path, path)) { /* Matched Paths. Open this device */ - // OPEN HERE // + /* OPEN HERE */ res = libusb_open(usb_dev, &dev->device_handle); if (res < 0) { LOG("can't open device\n"); free(dev_path); - break; + break; } good_open = 1; - +#ifdef DETACH_KERNEL_DRIVER /* Detach the kernel driver, but only if the device is managed by the kernel */ if (libusb_kernel_driver_active(dev->device_handle, intf_desc->bInterfaceNumber) == 1) { @@ -795,7 +835,7 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path) break; } } - +#endif res = libusb_claim_interface(dev->device_handle, intf_desc->bInterfaceNumber); if (res < 0) { LOG("can't claim interface %d: %d\n", intf_desc->bInterfaceNumber, res); @@ -847,7 +887,7 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path) pthread_create(&dev->thread, NULL, read_thread, dev); - // Wait here for the read thread to be initialized. + /* Wait here for the read thread to be initialized. */ pthread_barrier_wait(&dev->barrier); } @@ -861,12 +901,12 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path) libusb_free_device_list(devs, 1); - // If we have a good handle, return it. + /* If we have a good handle, return it. */ if (good_open) { return dev; } else { - // Unable to open any devices. + /* Unable to open any devices. */ free_hid_device(dev); return NULL; } @@ -1324,6 +1364,7 @@ uint16_t get_usb_code_for_current_locale(void) char *locale; char search_string[64]; char *ptr; + struct lang_map_entry *lang; /* Get the current locale. */ locale = setlocale(0, NULL); @@ -1346,7 +1387,7 @@ uint16_t get_usb_code_for_current_locale(void) } /* Find the entry which matches the string code of our locale. */ - struct lang_map_entry *lang = lang_map; + lang = lang_map; while (lang->string_code) { if (!strcmp(lang->string_code, search_string)) { return lang->usb_code; @@ -1366,7 +1407,7 @@ uint16_t get_usb_code_for_current_locale(void) ptr++; } -#if 0 // TODO: Do we need this? +#if 0 /* TODO: Do we need this? */ /* Find the entry which matches the string code of our language. */ lang = lang_map; while (lang->string_code) { |