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