mirror of
https://github.com/Fishwaldo/open-zwave.git
synced 2025-07-06 21:18:26 +00:00
hidapi update to version 2011-09-10
This commit is contained in:
parent
5098867bda
commit
4363dd6ab4
21 changed files with 1020 additions and 292 deletions
|
@ -3,11 +3,42 @@ HID API for Windows, Linux, and Mac OS X
|
|||
About
|
||||
------
|
||||
|
||||
HIDAPI is a multi-platform library which allows an application to interface with USB and Bluetooth HID-Class devices on Windows, Linux, and Mac OS X. On Windows, a DLL is built. On other platforms (and optionally on Windows), the single source file can simply be dropped into a target application.
|
||||
HIDAPI is a multi-platform library which allows an application to interface
|
||||
with USB and Bluetooth HID-Class devices on Windows, Linux, and Mac OS X.
|
||||
On Windows, a DLL is built. On other platforms (and optionally on Windows),
|
||||
the single source file can simply be dropped into a target application.
|
||||
|
||||
HIDAPI has four back-ends:
|
||||
* Windows (using hid.dll)
|
||||
* Linux/hidraw (using the Kernel's hidraw driver)
|
||||
* Linux/libusb (using libusb-1.0)
|
||||
* Mac (using IOHidManager)
|
||||
|
||||
On Linux, either the hidraw or the libusb back-end can be used. There are
|
||||
tradeoffs, and the functionality supported is slightly different.
|
||||
|
||||
Linux/hidraw (linux/hid.c):
|
||||
This back-end uses the hidraw interface in the Linux kernel. While this
|
||||
back-end will support both USB and Bluetooth, it has some limitations on
|
||||
kernels prior to 2.6.39, including the inability to send or receive feature
|
||||
reports. In addition, it will only communicate with devices which have
|
||||
hidraw nodes associated with them. Keyboards, mice, and some other devices
|
||||
which are blacklisted from having hidraw nodes will not work. Fortunately,
|
||||
for nearly all the uses of hidraw, this is not a problem.
|
||||
|
||||
Linux/libusb (linux/hid-libusb.c):
|
||||
This back-end uses libusb-1.0 to communicate directly to a USB device. This
|
||||
back-end will of course not work with Bluetooth devices.
|
||||
|
||||
What Does the API Look Like?
|
||||
-----------------------------
|
||||
The API wraps the functionality of the most commonly used of the hid.dll functions. The sample program, which communicates with the USB Generic HID sample which is part of the Microchip Application Library (in folder "Microchip Solutions\USB Device - HID - Custom Demos\Generic HID - Firmware" when the Microchip Application Framework is installed), looks like this (with error checking removed for simplicity):
|
||||
The API provides the the most commonly used HID functions including sending
|
||||
and receiving of input, output, and feature reports. The sample program,
|
||||
which communicates with a heavily modified version the USB Generic HID
|
||||
sample which is part of the Microchip Application Library (in folder
|
||||
"Microchip Solutions\USB Device - HID - Custom Demos\Generic HID - Firmware"
|
||||
when the Microchip Application Framework is installed), looks like this
|
||||
(with error checking removed for simplicity):
|
||||
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
|
@ -66,7 +97,7 @@ int main(int argc, char* argv[])
|
|||
|
||||
License
|
||||
--------
|
||||
HIDAPI may be used by anyone for any reason so long as the coypright header in the source code remains intact.
|
||||
HIDAPI may be used by one of three licenses as outlined in LICENSE.txt.
|
||||
|
||||
Download
|
||||
---------
|
||||
|
@ -75,26 +106,48 @@ It can be downloaded from github
|
|||
|
||||
Build Instructions
|
||||
-------------------
|
||||
Windows:
|
||||
Build the .sln file in the windows/ directory.
|
||||
Linux:
|
||||
cd to the linux/ directory and run make.
|
||||
Mac OS X:
|
||||
cd to the mac/ directory and run make.
|
||||
To build the console test program:
|
||||
Windows:
|
||||
Build the .sln file in the windows/ directory.
|
||||
Linux:
|
||||
cd to the linux/ directory and run make.
|
||||
Mac OS X:
|
||||
cd to the mac/ directory and run make.
|
||||
|
||||
To build the Test GUI:
|
||||
On Windows, build the .sln file in the hidtest/ directory.
|
||||
On Linux and Mac, run make from the hidtest/ directory.
|
||||
The test GUI uses Fox toolkit, available from www.fox-toolkit.org.
|
||||
On Debian-based systems such as Ubuntu, install Fox using the following:
|
||||
sudo apt-get install libfox-1.6-dev
|
||||
On Mac OSX, install Fox from ports:
|
||||
sudo port install fox
|
||||
On Windows, download the hidapi-externals.zip file from the main download
|
||||
site and extract it just outside of hidapi, so that hidapi-externals and
|
||||
hidapi are on the same level, as shown:
|
||||
|
||||
Parent_Folder
|
||||
|
|
||||
+hidapi
|
||||
+hidapi-externals
|
||||
|
||||
Then to build:
|
||||
On Windows, build the .sln file in the testgui/ directory.
|
||||
On Linux and Mac, run make from the testgui/ directory.
|
||||
|
||||
To build using the DDK (old method):
|
||||
|
||||
1. Install the Windows Driver Kit (WDK) from Microsoft.
|
||||
2. From the Start menu, in the Windows Driver Kits folder, select Build Environments, then your operating system, then the x86 Free Build Environment (or one that is appropriate for your system).
|
||||
3. From the console, change directory to the windows/ddk_build/ directory, which is part of the HIDAPI distribution.
|
||||
2. From the Start menu, in the Windows Driver Kits folder, select Build
|
||||
Environments, then your operating system, then the x86 Free Build
|
||||
Environment (or one that is appropriate for your system).
|
||||
3. From the console, change directory to the windows/ddk_build/ directory,
|
||||
which is part of the HIDAPI distribution.
|
||||
4. Type build.
|
||||
5. You can find the output files (DLL and LIB) in a subdirectory created by the build system which is appropriate for your environment. On Windows XP, this directory is objfre_wxp_x86/i386.
|
||||
5. You can find the output files (DLL and LIB) in a subdirectory created
|
||||
by the build system which is appropriate for your environment. On
|
||||
Windows XP, this directory is objfre_wxp_x86/i386.
|
||||
|
||||
--------------------------------
|
||||
|
||||
Signal 11 Software - 2010-04-11
|
||||
2010-07-28
|
||||
2011-09-10
|
||||
|
|
|
@ -69,7 +69,9 @@ extern "C" {
|
|||
(Windows/Mac only).*/
|
||||
unsigned short usage;
|
||||
/** The USB interface which this logical device
|
||||
represents (Linux/libusb implementation only). */
|
||||
represents. Valid on both Linux implementations
|
||||
in all cases, and valid on the Windows implementation
|
||||
only if the device contains more than one interface. */
|
||||
int interface_number;
|
||||
|
||||
/** Pointer to the next device */
|
||||
|
@ -77,6 +79,35 @@ extern "C" {
|
|||
};
|
||||
|
||||
|
||||
/** @brief Initialize the HIDAPI library.
|
||||
|
||||
This function initializes the HIDAPI library. Calling it is not
|
||||
strictly necessary, as it will be called automatically by
|
||||
hid_enumerate() and any of the hid_open_*() functions if it is
|
||||
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
|
||||
This function returns 0 on success and -1 on error.
|
||||
*/
|
||||
int HID_API_EXPORT HID_API_CALL hid_init(void);
|
||||
|
||||
/** @brief Finalize the HIDAPI library.
|
||||
|
||||
This function frees all of the static data associated with
|
||||
HIDAPI. It should be called at the end of execution to avoid
|
||||
memory leaks.
|
||||
|
||||
@ingroup API
|
||||
|
||||
@returns
|
||||
This function returns 0 on success and -1 on error.
|
||||
*/
|
||||
int HID_API_EXPORT HID_API_CALL hid_exit(void);
|
||||
|
||||
/** @brief Enumerate the HID Devices.
|
||||
|
||||
This function returns a linked list of all the HID devices
|
||||
|
@ -169,6 +200,26 @@ extern "C" {
|
|||
*/
|
||||
int HID_API_EXPORT HID_API_CALL hid_write(hid_device *device, const unsigned char *data, size_t length);
|
||||
|
||||
/** @brief Read an Input report from a HID device with timeout.
|
||||
|
||||
Input reports are returned
|
||||
to the host through the INTERRUPT IN endpoint. The first byte will
|
||||
contain the Report number if the device uses numbered reports.
|
||||
|
||||
@ingroup API
|
||||
@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
|
||||
the report number.
|
||||
@param milliseconds timeout in milliseconds or -1 for blocking wait.
|
||||
|
||||
@returns
|
||||
This function returns the actual number of bytes read and
|
||||
-1 on error.
|
||||
*/
|
||||
int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds);
|
||||
|
||||
/** @brief Read an Input report from a HID device.
|
||||
|
||||
Input reports are returned
|
||||
|
|
|
@ -178,6 +178,11 @@ int main(int argc, char* argv[])
|
|||
printf("%02hhx ", buf[i]);
|
||||
printf("\n");
|
||||
|
||||
hid_close(handle);
|
||||
|
||||
/* Free static HIDAPI objects. */
|
||||
hid_exit();
|
||||
|
||||
#ifdef WIN32
|
||||
system("pause");
|
||||
#endif
|
||||
|
|
|
@ -8,23 +8,27 @@
|
|||
|
||||
all: hidtest
|
||||
|
||||
CC=gcc
|
||||
CXX=g++
|
||||
COBJS=hid-libusb.o
|
||||
CPPOBJS=../hidtest/hidtest.o
|
||||
OBJS=$(COBJS) $(CPPOBJS)
|
||||
CFLAGS+=-I../hidapi -Wall -g -c `pkg-config libusb-1.0 --cflags`
|
||||
LIBS=`pkg-config libusb-1.0 libudev --libs`
|
||||
CC ?= gcc
|
||||
CFLAGS ?= -Wall -g
|
||||
|
||||
CXX ?= g++
|
||||
CXXFLAGS ?= -Wall -g
|
||||
|
||||
COBJS = hid-libusb.o
|
||||
CPPOBJS = ../hidtest/hidtest.o
|
||||
OBJS = $(COBJS) $(CPPOBJS)
|
||||
LIBS = `pkg-config libusb-1.0 libudev --libs`
|
||||
INCLUDES ?= -I../hidapi `pkg-config libusb-1.0 --cflags`
|
||||
|
||||
|
||||
hidtest: $(OBJS)
|
||||
g++ -Wall -g $^ $(LIBS) -o hidtest
|
||||
$(CXX) $(CXXFLAGS) $(LDFLAGS) $^ $(LIBS) -o hidtest
|
||||
|
||||
$(COBJS): %.o: %.c
|
||||
$(CC) $(CFLAGS) $< -o $@
|
||||
$(CC) $(CFLAGS) -c $(INCLUDES) $< -o $@
|
||||
|
||||
$(CPPOBJS): %.o: %.cpp
|
||||
$(CXX) $(CFLAGS) $< -o $@
|
||||
$(CXX) $(CXXFLAGS) -c $(INCLUDES) $< -o $@
|
||||
|
||||
clean:
|
||||
rm -f $(OBJS) hidtest
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
http://github.com/signal11/hidapi .
|
||||
********************************************************/
|
||||
|
||||
#define _GNU_SOURCE // needed for wcsdup() before glibc 2.10
|
||||
|
||||
/* C */
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
@ -38,6 +40,7 @@
|
|||
#include <sys/utsname.h>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <wchar.h>
|
||||
|
||||
/* GNU / LibUSB */
|
||||
#include "libusb.h"
|
||||
|
@ -103,7 +106,7 @@ struct hid_device_ {
|
|||
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);
|
||||
|
@ -244,7 +247,7 @@ static int get_usage(uint8_t *report_descriptor, size_t size,
|
|||
if (usage_page_found && usage_found)
|
||||
return 0; /* success */
|
||||
|
||||
/* Skip over this key and its associated data */
|
||||
/* Skip over this key and it's associated data */
|
||||
i += data_len + key_size;
|
||||
}
|
||||
|
||||
|
@ -378,6 +381,27 @@ static char *make_path(libusb_device *dev, int interface_number)
|
|||
return strdup(str);
|
||||
}
|
||||
|
||||
|
||||
int HID_API_EXPORT hid_init(void)
|
||||
{
|
||||
if (!usb_context) {
|
||||
if (libusb_init(&usb_context))
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int HID_API_EXPORT hid_exit(void)
|
||||
{
|
||||
if (usb_context) {
|
||||
libusb_exit(usb_context);
|
||||
usb_context = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short product_id)
|
||||
{
|
||||
libusb_device **devs;
|
||||
|
@ -391,12 +415,9 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id,
|
|||
|
||||
setlocale(LC_ALL,"");
|
||||
|
||||
if (!initialized) {
|
||||
libusb_init(NULL);
|
||||
initialized = 1;
|
||||
}
|
||||
|
||||
num_devs = libusb_get_device_list(NULL, &devs);
|
||||
hid_init();
|
||||
|
||||
num_devs = libusb_get_device_list(usb_context, &devs);
|
||||
if (num_devs < 0)
|
||||
return NULL;
|
||||
while ((dev = devs[i++]) != NULL) {
|
||||
|
@ -679,11 +700,7 @@ static void *read_thread(void *param)
|
|||
/* Handle all the events. */
|
||||
while (!dev->shutdown_thread) {
|
||||
int res;
|
||||
struct timeval tv;
|
||||
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 100; //TODO: Fix this value.
|
||||
res = libusb_handle_events_timeout(NULL, &tv);
|
||||
res = libusb_handle_events(usb_context);
|
||||
if (res < 0) {
|
||||
/* There was an error. Break out of this loop. */
|
||||
break;
|
||||
|
@ -694,8 +711,17 @@ static void *read_thread(void *param)
|
|||
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_handle_events(usb_context);
|
||||
}
|
||||
|
||||
/* 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
|
||||
signaled. */
|
||||
pthread_mutex_lock(&dev->mutex);
|
||||
pthread_cond_broadcast(&dev->condition);
|
||||
pthread_mutex_unlock(&dev->mutex);
|
||||
|
||||
/* The dev->transfer->buffer and dev->transfer objects are cleaned up
|
||||
in hid_close(). They are not cleaned up here because this thread
|
||||
|
@ -724,12 +750,9 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path)
|
|||
|
||||
setlocale(LC_ALL,"");
|
||||
|
||||
if (!initialized) {
|
||||
libusb_init(NULL);
|
||||
initialized = 1;
|
||||
}
|
||||
|
||||
num_devs = libusb_get_device_list(NULL, &devs);
|
||||
hid_init();
|
||||
|
||||
num_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;
|
||||
|
@ -752,6 +775,7 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path)
|
|||
res = libusb_open(usb_dev, &dev->device_handle);
|
||||
if (res < 0) {
|
||||
LOG("can't open device\n");
|
||||
free(dev_path);
|
||||
break;
|
||||
}
|
||||
good_open = 1;
|
||||
|
@ -763,6 +787,7 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path)
|
|||
if (res < 0) {
|
||||
libusb_close(dev->device_handle);
|
||||
LOG("Unable to detach Kernel Driver\n");
|
||||
free(dev_path);
|
||||
good_open = 0;
|
||||
break;
|
||||
}
|
||||
|
@ -771,6 +796,7 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path)
|
|||
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);
|
||||
free(dev_path);
|
||||
libusb_close(dev->device_handle);
|
||||
good_open = 0;
|
||||
break;
|
||||
|
@ -910,8 +936,14 @@ static int return_data(hid_device *dev, unsigned char *data, size_t length)
|
|||
return len;
|
||||
}
|
||||
|
||||
static void cleanup_mutex(void *param)
|
||||
{
|
||||
hid_device *dev = param;
|
||||
pthread_mutex_unlock(&dev->mutex);
|
||||
}
|
||||
|
||||
int HID_API_EXPORT hid_read(hid_device *dev, unsigned char *data, size_t length)
|
||||
|
||||
int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds)
|
||||
{
|
||||
int bytes_read = -1;
|
||||
|
||||
|
@ -923,6 +955,7 @@ int HID_API_EXPORT hid_read(hid_device *dev, unsigned char *data, size_t length)
|
|||
#endif
|
||||
|
||||
pthread_mutex_lock(&dev->mutex);
|
||||
pthread_cleanup_push(&cleanup_mutex, dev);
|
||||
|
||||
/* There's an input report queued up. Return it. */
|
||||
if (dev->input_reports) {
|
||||
|
@ -938,20 +971,68 @@ int HID_API_EXPORT hid_read(hid_device *dev, unsigned char *data, size_t length)
|
|||
goto ret;
|
||||
}
|
||||
|
||||
if (dev->blocking) {
|
||||
pthread_cond_wait(&dev->condition, &dev->mutex);
|
||||
bytes_read = return_data(dev, data, length);
|
||||
if (milliseconds == -1) {
|
||||
/* Blocking */
|
||||
while (!dev->input_reports && !dev->shutdown_thread) {
|
||||
pthread_cond_wait(&dev->condition, &dev->mutex);
|
||||
}
|
||||
if (dev->input_reports) {
|
||||
bytes_read = return_data(dev, data, length);
|
||||
}
|
||||
}
|
||||
else if (milliseconds > 0) {
|
||||
/* Non-blocking, but called with timeout. */
|
||||
int res;
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_REALTIME, &ts);
|
||||
ts.tv_sec += milliseconds / 1000;
|
||||
ts.tv_nsec += (milliseconds % 1000) * 1000000;
|
||||
if (ts.tv_nsec >= 1000000000L) {
|
||||
ts.tv_sec++;
|
||||
ts.tv_nsec -= 1000000000L;
|
||||
}
|
||||
|
||||
while (!dev->input_reports && !dev->shutdown_thread) {
|
||||
res = pthread_cond_timedwait(&dev->condition, &dev->mutex, &ts);
|
||||
if (res == 0) {
|
||||
if (dev->input_reports) {
|
||||
bytes_read = return_data(dev, data, length);
|
||||
break;
|
||||
}
|
||||
|
||||
/* If we're here, there was a spurious wake up
|
||||
or the read thread was shutdown. Run the
|
||||
loop again (ie: don't break). */
|
||||
}
|
||||
else if (res == ETIMEDOUT) {
|
||||
/* Timed out. */
|
||||
bytes_read = 0;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
/* Error. */
|
||||
bytes_read = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Purely non-blocking */
|
||||
bytes_read = 0;
|
||||
}
|
||||
|
||||
ret:
|
||||
pthread_mutex_unlock(&dev->mutex);
|
||||
pthread_cleanup_pop(0);
|
||||
|
||||
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)
|
||||
{
|
||||
dev->blocking = !nonblock;
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include <sys/ioctl.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
|
||||
/* Linux */
|
||||
#include <linux/hidraw.h>
|
||||
|
@ -43,12 +44,12 @@
|
|||
|
||||
#include "hidapi.h"
|
||||
|
||||
/*
|
||||
* Added here until linux/hidraw.h has them. These definitions require a version
|
||||
* 2.6.39 based kernel as well.
|
||||
*/
|
||||
/* 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
|
||||
|
||||
|
@ -71,6 +72,13 @@ hid_device *new_hid_device()
|
|||
return dev;
|
||||
}
|
||||
|
||||
#ifdef notdef
|
||||
static void register_error(hid_device *device, const char *op)
|
||||
{
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
/* 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)
|
||||
|
@ -143,7 +151,7 @@ static int uses_numbered_reports(__u8 *report_descriptor, __u32 size) {
|
|||
key_size = 1;
|
||||
}
|
||||
|
||||
/* Skip over this key and its associated data */
|
||||
/* Skip over this key and it's associated data */
|
||||
i += data_len + key_size;
|
||||
}
|
||||
|
||||
|
@ -154,7 +162,7 @@ static int uses_numbered_reports(__u8 *report_descriptor, __u32 size) {
|
|||
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 = NULL;
|
||||
struct udev_device *udev_dev, *parent;
|
||||
struct stat s;
|
||||
int ret = -1;
|
||||
|
||||
|
@ -189,21 +197,31 @@ static int get_device_string(hid_device *dev, const char *key, wchar_t *string,
|
|||
}
|
||||
|
||||
end:
|
||||
udev_device_unref(parent);
|
||||
// udev_dev doesn't need unref'd. Not sure why, but
|
||||
// it'll throw "double free" errors.
|
||||
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)
|
||||
{
|
||||
/* Nothing to do for this in the Linux/hidraw implementation. */
|
||||
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 udev_device *dev;
|
||||
|
||||
struct hid_device_info *root = NULL; // return object
|
||||
struct hid_device_info *cur_dev = NULL;
|
||||
|
@ -228,23 +246,26 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id,
|
|||
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);
|
||||
dev = udev_device_new_from_syspath(udev, sysfs_path);
|
||||
dev_path = udev_device_get_devnode(dev);
|
||||
hid_dev = udev_device_new_from_syspath(udev, sysfs_path);
|
||||
dev_path = udev_device_get_devnode(hid_dev);
|
||||
|
||||
/* The device pointed to by dev contains information about
|
||||
/* 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(
|
||||
dev,
|
||||
hid_dev,
|
||||
"usb",
|
||||
"usb_device");
|
||||
if (!dev) {
|
||||
|
@ -304,15 +325,26 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id,
|
|||
str = udev_device_get_sysattr_value(dev, "bcdDevice");
|
||||
cur_dev->release_number = (str)? strtol(str, NULL, 16): 0x0;
|
||||
|
||||
/* Interface Number (Unsupported on Linux/hidraw) */
|
||||
/* 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(dev);
|
||||
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);
|
||||
|
@ -441,10 +473,26 @@ int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t
|
|||
}
|
||||
|
||||
|
||||
int HID_API_EXPORT hid_read(hid_device *dev, unsigned char *data, size_t length)
|
||||
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;
|
||||
|
@ -460,6 +508,11 @@ int HID_API_EXPORT hid_read(hid_device *dev, unsigned char *data, size_t length)
|
|||
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;
|
||||
|
|
|
@ -20,15 +20,79 @@
|
|||
http://github.com/signal11/hidapi .
|
||||
********************************************************/
|
||||
|
||||
/* See Apple Technical Note TN2187 for details on IOHidManager. */
|
||||
|
||||
#include <IOKit/hid/IOHIDManager.h>
|
||||
#include <IOKit/hid/IOHIDKeys.h>
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <wchar.h>
|
||||
#include <locale.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "hidapi.h"
|
||||
|
||||
/* Barrier implementation because Mac OSX doesn't have pthread_barrier.
|
||||
It also doesn't have clock_gettime(). So much for POSIX and SUSv2.
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
static int return_data(hid_device *dev, unsigned char *data, size_t length);
|
||||
|
||||
/* Linked List of input reports received from the device. */
|
||||
struct input_report {
|
||||
uint8_t *data;
|
||||
|
@ -42,10 +106,18 @@ struct hid_device_ {
|
|||
int uses_numbered_reports;
|
||||
int disconnected;
|
||||
CFStringRef run_loop_mode;
|
||||
CFRunLoopRef runloopref;
|
||||
CFRunLoopRef run_loop;
|
||||
CFRunLoopSourceRef source;
|
||||
uint8_t *input_report_buf;
|
||||
CFIndex max_input_report_len;
|
||||
struct input_report *input_reports;
|
||||
pthread_mutex_t mutex;
|
||||
|
||||
pthread_t thread;
|
||||
pthread_mutex_t mutex; /* Protects input_reports */
|
||||
pthread_cond_t condition;
|
||||
pthread_barrier_t barrier; /* Ensures correct startup sequence */
|
||||
pthread_barrier_t shutdown_barrier; /* Ensures correct shutdown sequence */
|
||||
int shutdown_thread;
|
||||
|
||||
hid_device *next;
|
||||
};
|
||||
|
@ -54,6 +126,7 @@ struct hid_device_ {
|
|||
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)
|
||||
{
|
||||
|
@ -63,13 +136,21 @@ static hid_device *new_hid_device(void)
|
|||
dev->uses_numbered_reports = 0;
|
||||
dev->disconnected = 0;
|
||||
dev->run_loop_mode = NULL;
|
||||
dev->run_loop = NULL;
|
||||
dev->source = NULL;
|
||||
dev->input_report_buf = NULL;
|
||||
dev->input_reports = NULL;
|
||||
dev->shutdown_thread = 0;
|
||||
dev->next = NULL;
|
||||
|
||||
/* Thread objects */
|
||||
pthread_mutex_init(&dev->mutex, NULL);
|
||||
pthread_cond_init(&dev->condition, NULL);
|
||||
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 {
|
||||
|
@ -82,6 +163,7 @@ static hid_device *new_hid_device(void)
|
|||
d = d->next;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&device_list_mutex);
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
@ -105,11 +187,18 @@ static void free_hid_device(hid_device *dev)
|
|||
free() and others do. */
|
||||
if (dev->run_loop_mode)
|
||||
CFRelease(dev->run_loop_mode);
|
||||
if (dev->source)
|
||||
CFRelease(dev->source);
|
||||
free(dev->input_report_buf);
|
||||
|
||||
/* Clean up the thread objects */
|
||||
pthread_barrier_destroy(&dev->shutdown_barrier);
|
||||
pthread_barrier_destroy(&dev->barrier);
|
||||
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;
|
||||
|
@ -124,10 +213,10 @@ static void free_hid_device(hid_device *dev)
|
|||
d = d->next;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&device_list_mutex);
|
||||
|
||||
/* Free the structure itself. */
|
||||
free(dev);
|
||||
|
||||
}
|
||||
|
||||
static IOHIDManagerRef hid_mgr = 0x0;
|
||||
|
@ -174,16 +263,25 @@ 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;
|
||||
|
||||
if (!len)
|
||||
return 0;
|
||||
|
||||
buf[0] = 0x0000;
|
||||
str = IOHIDDeviceGetProperty(device, prop);
|
||||
|
||||
buf[0] = 0;
|
||||
|
||||
if (str) {
|
||||
len --;
|
||||
|
||||
CFIndex str_len = CFStringGetLength(str);
|
||||
CFRange range;
|
||||
range.location = 0;
|
||||
range.length = len;
|
||||
range.length = (str_len > len)? len: str_len;
|
||||
CFIndex used_buf_len;
|
||||
CFStringGetBytes(str,
|
||||
CFIndex chars_copied;
|
||||
chars_copied = CFStringGetBytes(str,
|
||||
range,
|
||||
kCFStringEncodingUTF32LE,
|
||||
(char)'?',
|
||||
|
@ -191,8 +289,9 @@ static int get_string_property(IOHIDDeviceRef device, CFStringRef prop, wchar_t
|
|||
(UInt8*)buf,
|
||||
len,
|
||||
&used_buf_len);
|
||||
buf[len-1] = 0x00000000;
|
||||
return used_buf_len;
|
||||
|
||||
buf[chars_copied] = 0;
|
||||
return chars_copied;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
|
@ -201,16 +300,24 @@ 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 = IOHIDDeviceGetProperty(device, prop);
|
||||
CFStringRef str;
|
||||
if (!len)
|
||||
return 0;
|
||||
|
||||
buf[0] = 0x0000;
|
||||
str = IOHIDDeviceGetProperty(device, prop);
|
||||
|
||||
buf[0] = 0;
|
||||
|
||||
if (str) {
|
||||
len--;
|
||||
|
||||
CFIndex str_len = CFStringGetLength(str);
|
||||
CFRange range;
|
||||
range.location = 0;
|
||||
range.length = len;
|
||||
range.length = (str_len > len)? len: str_len;
|
||||
CFIndex used_buf_len;
|
||||
CFStringGetBytes(str,
|
||||
CFIndex chars_copied;
|
||||
chars_copied = CFStringGetBytes(str,
|
||||
range,
|
||||
kCFStringEncodingUTF8,
|
||||
(char)'?',
|
||||
|
@ -218,12 +325,12 @@ static int get_string_property_utf8(IOHIDDeviceRef device, CFStringRef prop, cha
|
|||
(UInt8*)buf,
|
||||
len,
|
||||
&used_buf_len);
|
||||
buf[len-1] = 0x00000000;
|
||||
|
||||
buf[chars_copied] = 0;
|
||||
return used_buf_len;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -280,15 +387,53 @@ static int make_path(IOHIDDeviceRef device, char *buf, size_t len)
|
|||
return res+1;
|
||||
}
|
||||
|
||||
static void init_hid_manager(void)
|
||||
/* 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);
|
||||
IOHIDManagerOpen(hid_mgr, kIOHIDOptionsTypeNone);
|
||||
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) {
|
||||
return init_hid_manager();
|
||||
}
|
||||
|
||||
/* Already initialized. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
int HID_API_EXPORT hid_exit(void)
|
||||
{
|
||||
if (hid_mgr) {
|
||||
/* Close the HID manager. */
|
||||
IOHIDManagerClose(hid_mgr, kIOHIDOptionsTypeNone);
|
||||
CFRelease(hid_mgr);
|
||||
hid_mgr = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void process_pending_events() {
|
||||
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)
|
||||
{
|
||||
|
@ -300,9 +445,12 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id,
|
|||
setlocale(LC_ALL,"");
|
||||
|
||||
/* Set up the HID Manager if it hasn't been done */
|
||||
if (!hid_mgr)
|
||||
init_hid_manager();
|
||||
if (hid_init() < 0)
|
||||
return NULL;
|
||||
|
||||
/* give the IOHIDManager a chance to update itself */
|
||||
process_pending_events();
|
||||
|
||||
/* Get a list of the Devices */
|
||||
CFSetRef device_set = IOHIDManagerCopyDevices(hid_mgr);
|
||||
|
||||
|
@ -321,6 +469,9 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id,
|
|||
|
||||
IOHIDDeviceRef dev = device_array[i];
|
||||
|
||||
if (!dev) {
|
||||
continue;
|
||||
}
|
||||
dev_vid = get_vendor_id(dev);
|
||||
dev_pid = get_product_id(dev);
|
||||
|
||||
|
@ -431,14 +582,18 @@ 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)
|
||||
{
|
||||
/* 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);
|
||||
}
|
||||
|
||||
d = d->next;
|
||||
}
|
||||
pthread_mutex_unlock(&device_list_mutex);
|
||||
}
|
||||
|
||||
/* The Run Loop calls this function for each input report received.
|
||||
|
@ -450,13 +605,16 @@ static void hid_report_callback(void *context, IOReturn result, void *sender,
|
|||
{
|
||||
struct input_report *rpt;
|
||||
hid_device *dev = context;
|
||||
|
||||
|
||||
/* Make a new Input Report object */
|
||||
rpt = calloc(1, sizeof(struct input_report));
|
||||
rpt->data = calloc(1, report_length);
|
||||
memcpy(rpt->data, report, report_length);
|
||||
rpt->len = report_length;
|
||||
rpt->next = NULL;
|
||||
|
||||
/* Lock this section */
|
||||
pthread_mutex_lock(&dev->mutex);
|
||||
|
||||
/* Attach the new report object to the end of the list. */
|
||||
if (dev->input_reports == NULL) {
|
||||
|
@ -466,14 +624,99 @@ static void hid_report_callback(void *context, IOReturn result, void *sender,
|
|||
else {
|
||||
/* Find the end of the list and attach. */
|
||||
struct input_report *cur = dev->input_reports;
|
||||
while (cur->next != NULL)
|
||||
int num_queued = 0;
|
||||
while (cur->next != NULL) {
|
||||
cur = cur->next;
|
||||
num_queued++;
|
||||
}
|
||||
cur->next = rpt;
|
||||
|
||||
/* Pop one off if we've reached 30 in the queue. This
|
||||
way we don't grow forever if the user never reads
|
||||
anything from the device. */
|
||||
if (num_queued > 30) {
|
||||
return_data(dev, NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Signal a waiting thread that there is data. */
|
||||
pthread_cond_signal(&dev->condition);
|
||||
|
||||
/* Unlock */
|
||||
pthread_mutex_unlock(&dev->mutex);
|
||||
|
||||
}
|
||||
|
||||
/* This gets called when the read_thred'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()
|
||||
}
|
||||
|
||||
static void *read_thread(void *param)
|
||||
{
|
||||
hid_device *dev = param;
|
||||
|
||||
/* Stop the Run Loop. This is mostly used for when blocking is
|
||||
enabled, but it doesn't hurt for non-blocking as well. */
|
||||
CFRunLoopStop(CFRunLoopGetCurrent());
|
||||
/* Move the device's run loop to this thread. */
|
||||
IOHIDDeviceScheduleWithRunLoop(dev->device_handle, CFRunLoopGetCurrent(), dev->run_loop_mode);
|
||||
|
||||
/* Create the RunLoopSource which is used to signal the
|
||||
event loop to stop when hid_close() is called. */
|
||||
CFRunLoopSourceContext ctx;
|
||||
memset(&ctx, 0, sizeof(ctx));
|
||||
ctx.version = 0;
|
||||
ctx.info = dev;
|
||||
ctx.perform = &perform_signal_callback;
|
||||
dev->source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0/*order*/, &ctx);
|
||||
CFRunLoopAddSource(CFRunLoopGetCurrent(), dev->source, dev->run_loop_mode);
|
||||
|
||||
/* Store off the Run Loop so it can be stopped from hid_close()
|
||||
and on device disconnection. */
|
||||
dev->run_loop = CFRunLoopGetCurrent();
|
||||
|
||||
/* Notify the main thread that the read thread is up and running. */
|
||||
pthread_barrier_wait(&dev->barrier);
|
||||
|
||||
/* 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 */
|
||||
if (code == kCFRunLoopRunFinished) {
|
||||
dev->disconnected = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
/* Break if The Run Loop returns Finished or Stopped. */
|
||||
if (code != kCFRunLoopRunTimedOut &&
|
||||
code != kCFRunLoopRunHandledSource) {
|
||||
/* There was some kind of error. Setting
|
||||
shutdown seems to make sense, but
|
||||
there may be something else more appropriate */
|
||||
dev->shutdown_thread = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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
|
||||
signaled. */
|
||||
pthread_mutex_lock(&dev->mutex);
|
||||
pthread_cond_broadcast(&dev->condition);
|
||||
pthread_mutex_unlock(&dev->mutex);
|
||||
|
||||
/* 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. */
|
||||
pthread_barrier_wait(&dev->shutdown_barrier);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hid_device * HID_API_EXPORT hid_open_path(const char *path)
|
||||
|
@ -485,8 +728,11 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path)
|
|||
dev = new_hid_device();
|
||||
|
||||
/* Set up the HID Manager if it hasn't been done */
|
||||
if (!hid_mgr)
|
||||
init_hid_manager();
|
||||
if (hid_init() < 0)
|
||||
return NULL;
|
||||
|
||||
/* give the IOHIDManager a chance to update itself */
|
||||
process_pending_events();
|
||||
|
||||
CFSetRef device_set = IOHIDManagerCopyDevices(hid_mgr);
|
||||
|
||||
|
@ -504,29 +750,32 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path)
|
|||
IOReturn ret = IOHIDDeviceOpen(os_dev, kIOHIDOptionsTypeNone);
|
||||
if (ret == kIOReturnSuccess) {
|
||||
char str[32];
|
||||
CFIndex max_input_report_len;
|
||||
|
||||
free(device_array);
|
||||
CFRelease(device_set);
|
||||
dev->device_handle = os_dev;
|
||||
|
||||
/* Create the buffers for receiving data */
|
||||
max_input_report_len = (CFIndex) get_max_report_length(os_dev);
|
||||
dev->input_report_buf = calloc(max_input_report_len, sizeof(uint8_t));
|
||||
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, "%p", os_dev);
|
||||
sprintf(str, "HIDAPI_%p", os_dev);
|
||||
dev->run_loop_mode =
|
||||
CFStringCreateWithCString(NULL, str, kCFStringEncodingASCII);
|
||||
|
||||
/* Attach the device to a Run Loop */
|
||||
IOHIDDeviceScheduleWithRunLoop(os_dev, CFRunLoopGetCurrent(), dev->run_loop_mode);
|
||||
IOHIDDeviceRegisterInputReportCallback(
|
||||
os_dev, dev->input_report_buf, max_input_report_len,
|
||||
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;
|
||||
}
|
||||
|
@ -601,96 +850,124 @@ static int return_data(hid_device *dev, unsigned char *data, size_t length)
|
|||
return len;
|
||||
}
|
||||
|
||||
int HID_API_EXPORT hid_read(hid_device *dev, unsigned char *data, size_t length)
|
||||
static int cond_wait(const hid_device *dev, pthread_cond_t *cond, pthread_mutex_t *mutex)
|
||||
{
|
||||
int ret_val = -1;
|
||||
while (!dev->input_reports) {
|
||||
int res = pthread_cond_wait(cond, mutex);
|
||||
if (res != 0)
|
||||
return res;
|
||||
|
||||
/* Lock this function */
|
||||
/* A res of 0 means we may have been signaled or it may
|
||||
be a spurious wakeup. Check to see that there's acutally
|
||||
data in the queue before returning, and if not, go back
|
||||
to sleep. See the pthread_cond_timedwait() man page for
|
||||
details. */
|
||||
|
||||
if (dev->shutdown_thread || dev->disconnected)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cond_timedwait(const hid_device *dev, pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime)
|
||||
{
|
||||
while (!dev->input_reports) {
|
||||
int res = pthread_cond_timedwait(cond, mutex, abstime);
|
||||
if (res != 0)
|
||||
return res;
|
||||
|
||||
/* A res of 0 means we may have been signaled or it may
|
||||
be a spurious wakeup. Check to see that there's acutally
|
||||
data in the queue before returning, and if not, go back
|
||||
to sleep. See the pthread_cond_timedwait() man page for
|
||||
details. */
|
||||
|
||||
if (dev->shutdown_thread || dev->disconnected)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds)
|
||||
{
|
||||
int bytes_read = -1;
|
||||
|
||||
/* Lock the access to the report list. */
|
||||
pthread_mutex_lock(&dev->mutex);
|
||||
|
||||
/* There's an input report queued up. Return it. */
|
||||
if (dev->input_reports) {
|
||||
/* Return the first one */
|
||||
ret_val = return_data(dev, data, length);
|
||||
bytes_read = return_data(dev, data, length);
|
||||
goto ret;
|
||||
}
|
||||
|
||||
/* Return if the device has been disconnected. */
|
||||
if (dev->disconnected) {
|
||||
ret_val = -1;
|
||||
goto ret;
|
||||
if (dev->disconnected) {
|
||||
bytes_read = -1;
|
||||
goto ret;
|
||||
}
|
||||
|
||||
/* There are no input reports queued up.
|
||||
Need to get some from the OS. */
|
||||
if (dev->shutdown_thread) {
|
||||
/* This means the device has been closed (or there
|
||||
has been an error. An error code of -1 should
|
||||
be returned. */
|
||||
bytes_read = -1;
|
||||
goto ret;
|
||||
}
|
||||
|
||||
/* Move the device's run loop to this thread. */
|
||||
dev->runloopref = CFRunLoopGetCurrent();
|
||||
IOHIDDeviceScheduleWithRunLoop(dev->device_handle, dev->runloopref, dev->run_loop_mode);
|
||||
/* There is no data. Go to sleep and wait for data. */
|
||||
|
||||
if (dev->blocking) {
|
||||
/* Run the Run Loop until it stops timing out. In other
|
||||
words, until something happens. This is necessary because
|
||||
there is no INFINITE timeout value. */
|
||||
SInt32 code;
|
||||
while (1) {
|
||||
code = CFRunLoopRunInMode(dev->run_loop_mode, 1000, TRUE);
|
||||
|
||||
/* Return if the device has been disconnected */
|
||||
if (code == kCFRunLoopRunFinished) {
|
||||
dev->disconnected = 1;
|
||||
ret_val = -1;
|
||||
goto ret;
|
||||
}
|
||||
|
||||
|
||||
/* Return if some data showed up. */
|
||||
if (dev->input_reports)
|
||||
break;
|
||||
|
||||
/* Break if The Run Loop returns Finished or Stopped. */
|
||||
if (code != kCFRunLoopRunTimedOut &&
|
||||
code != kCFRunLoopRunHandledSource)
|
||||
break;
|
||||
if (milliseconds == -1) {
|
||||
/* Blocking */
|
||||
int res;
|
||||
res = cond_wait(dev, &dev->condition, &dev->mutex);
|
||||
if (res == 0)
|
||||
bytes_read = return_data(dev, data, length);
|
||||
else {
|
||||
/* There was an error, or a device disconnection. */
|
||||
bytes_read = -1;
|
||||
}
|
||||
}
|
||||
else if (milliseconds > 0) {
|
||||
/* Non-blocking, but called with timeout. */
|
||||
int res;
|
||||
struct timespec ts;
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
TIMEVAL_TO_TIMESPEC(&tv, &ts);
|
||||
ts.tv_sec += milliseconds / 1000;
|
||||
ts.tv_nsec += (milliseconds % 1000) * 1000000;
|
||||
if (ts.tv_nsec >= 1000000000L) {
|
||||
ts.tv_sec++;
|
||||
ts.tv_nsec -= 1000000000L;
|
||||
}
|
||||
|
||||
/* See if the run loop and callback gave us any reports. */
|
||||
if (dev->input_reports) {
|
||||
ret_val = return_data(dev, data, length);
|
||||
goto ret;
|
||||
}
|
||||
else {
|
||||
dev->disconnected = 1;
|
||||
ret_val = -1; /* An error occured (maybe CTRL-C?). */
|
||||
goto ret;
|
||||
}
|
||||
res = cond_timedwait(dev, &dev->condition, &dev->mutex, &ts);
|
||||
if (res == 0)
|
||||
bytes_read = return_data(dev, data, length);
|
||||
else if (res == ETIMEDOUT)
|
||||
bytes_read = 0;
|
||||
else
|
||||
bytes_read = -1;
|
||||
}
|
||||
else {
|
||||
/* Non-blocking. See if the OS has any reports to give. */
|
||||
SInt32 code;
|
||||
code = CFRunLoopRunInMode(dev->run_loop_mode, 0, TRUE);
|
||||
if (code == kCFRunLoopRunFinished) {
|
||||
/* The run loop is finished, indicating an error
|
||||
or the device had been disconnected. */
|
||||
dev->disconnected = 1;
|
||||
ret_val = -1;
|
||||
goto ret;
|
||||
}
|
||||
if (dev->input_reports) {
|
||||
/* Return the first one */
|
||||
ret_val = return_data(dev, data, length);
|
||||
goto ret;
|
||||
}
|
||||
else {
|
||||
ret_val = 0; /* No data*/
|
||||
goto ret;
|
||||
}
|
||||
/* Purely non-blocking */
|
||||
bytes_read = 0;
|
||||
}
|
||||
|
||||
ret:
|
||||
/* Unlock */
|
||||
pthread_mutex_unlock(&dev->mutex);
|
||||
return ret_val;
|
||||
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)
|
||||
|
@ -730,8 +1007,29 @@ void HID_API_EXPORT hid_close(hid_device *dev)
|
|||
{
|
||||
if (!dev)
|
||||
return;
|
||||
|
||||
/* Disconnect the report callback before close. */
|
||||
if (!dev->disconnected) {
|
||||
IOHIDDeviceRegisterInputReportCallback(
|
||||
dev->device_handle, dev->input_report_buf, dev->max_input_report_len,
|
||||
NULL, dev);
|
||||
IOHIDManagerRegisterDeviceRemovalCallback(hid_mgr, NULL, dev);
|
||||
IOHIDDeviceUnscheduleFromRunLoop(dev->device_handle, dev->run_loop, dev->run_loop_mode);
|
||||
IOHIDDeviceScheduleWithRunLoop(dev->device_handle, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
|
||||
}
|
||||
|
||||
CFRunLoopStop(dev->runloopref);
|
||||
/* Cause read_thread() to stop. */
|
||||
dev->shutdown_thread = 1;
|
||||
|
||||
/* Wake up the run thread's event loop so that the thread can exit. */
|
||||
CFRunLoopSourceSignal(dev->source);
|
||||
CFRunLoopWakeUp(dev->run_loop);
|
||||
|
||||
/* Notify the read thread that it can shut down now. */
|
||||
pthread_barrier_wait(&dev->shutdown_barrier);
|
||||
|
||||
/* Wait for read_thread() to end. */
|
||||
pthread_join(dev->thread, NULL);
|
||||
|
||||
/* Close the OS handle to the device, but only if it's not
|
||||
been unplugged. If it's been unplugged, then calling
|
||||
|
@ -739,6 +1037,13 @@ void HID_API_EXPORT hid_close(hid_device *dev)
|
|||
if (!dev->disconnected) {
|
||||
IOHIDDeviceClose(dev->device_handle, kIOHIDOptionsTypeNone);
|
||||
}
|
||||
|
||||
/* Clear out the queue of received reports. */
|
||||
pthread_mutex_lock(&dev->mutex);
|
||||
while (dev->input_reports) {
|
||||
return_data(dev, NULL, 0);
|
||||
}
|
||||
pthread_mutex_unlock(&dev->mutex);
|
||||
|
||||
free_hid_device(dev);
|
||||
}
|
||||
|
@ -774,6 +1079,10 @@ HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev)
|
|||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#if 0
|
||||
static int32_t get_location_id(IOHIDDeviceRef device)
|
||||
{
|
||||
|
|
|
@ -11,10 +11,11 @@ all: testgui
|
|||
CC=gcc
|
||||
CXX=g++
|
||||
COBJS=../mac/hid.o
|
||||
CPPOBJS=test.o mac_support.o
|
||||
OBJS=$(COBJS) $(CPPOBJS)
|
||||
CPPOBJS=test.o
|
||||
OBJCOBJS=mac_support_cocoa.o
|
||||
OBJS=$(COBJS) $(CPPOBJS) $(OBJCOBJS)
|
||||
CFLAGS=-I../hidapi -Wall -g -c `fox-config --cflags`
|
||||
LIBS=`fox-config --libs` -framework IOKit -framework CoreFoundation -framework Carbon
|
||||
LIBS=`fox-config --libs` -framework IOKit -framework CoreFoundation -framework Cocoa
|
||||
|
||||
|
||||
testgui: $(OBJS)
|
||||
|
@ -29,7 +30,11 @@ $(COBJS): %.o: %.c
|
|||
$(CPPOBJS): %.o: %.cpp
|
||||
$(CXX) $(CFLAGS) $< -o $@
|
||||
|
||||
$(OBJCOBJS): %.o: %.m
|
||||
$(CXX) $(CFLAGS) -x objective-c++ $< -o $@
|
||||
|
||||
|
||||
clean:
|
||||
rm *.o testgui
|
||||
rm $(OBJS) testgui
|
||||
|
||||
.PHONY: clean
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
HIDAPI Test GUI
|
||||
|
||||
This test GUI application uses Fox Toolkit. On Windows, it can be downloaded
|
||||
as part of the hidapi-externals.zip file located on the GitHub project page
|
||||
where HIDAPI is downloaded:
|
||||
http://github.com/signal11/hidapi/downloads
|
||||
|
||||
On Windows:
|
||||
Copy ..\hidapi\objfre_wxp_x86\i386\hidapi.dll to this folder
|
||||
|
||||
On Linux:
|
||||
Install Fox-Toolkit from apt-get or yum:
|
||||
On Debian or Ubuntu:
|
||||
sudo apt-get install libfox-1.6-dev
|
||||
On Red Hat or Fedora:
|
||||
yum install libfox-1.6-devel
|
||||
|
||||
On Mac:
|
||||
Install Fox-Toolkit from ports:
|
||||
sudo port install fox
|
||||
|
||||
Once Fox-Toolkit is installed, Linux and Mac users can simply type 'make'.
|
||||
On Windows, HIDAPI can be built from the .vcproj file in this directory.
|
||||
|
||||
Alan Ott
|
||||
Signal 11 Software
|
||||
2010-07-22
|
Binary file not shown.
|
@ -9,7 +9,9 @@
|
|||
#ifndef MAC_SUPPORT_H__
|
||||
#define MAC_SUPPORT_H__
|
||||
|
||||
void init_apple_message_system();
|
||||
void check_apple_events();
|
||||
extern "C" {
|
||||
void init_apple_message_system();
|
||||
void check_apple_events();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -180,6 +180,9 @@ MainWindow::MainWindow(FXApp *app)
|
|||
|
||||
MainWindow::~MainWindow()
|
||||
{
|
||||
if (connected_device)
|
||||
hid_close(connected_device);
|
||||
hid_exit();
|
||||
delete title_font;
|
||||
}
|
||||
|
||||
|
|
Binary file not shown.
|
@ -183,7 +183,7 @@
|
|||
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
|
||||
>
|
||||
<File
|
||||
RelativePath="..\windows\hid.cpp"
|
||||
RelativePath="..\windows\hid.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
|
|
29
cpp/hidapi/udev/99-hid.rules
Normal file
29
cpp/hidapi/udev/99-hid.rules
Normal file
|
@ -0,0 +1,29 @@
|
|||
# This is a sample udev file for HIDAPI devices which changes the permissions
|
||||
# to 0666 (world readable/writable) for a specified device on Linux systems.
|
||||
|
||||
|
||||
# If you are using the libusb implementation of hidapi (hid-libusb.c), then
|
||||
# use something like the following line, substituting the VID and PID with
|
||||
# those of your device. Note that for kernels before 2.6.24, you will need
|
||||
# to substitute "usb" with "usb_device". It shouldn't hurt to use two lines
|
||||
# (one each way) for compatibility with older systems.
|
||||
|
||||
# HIDAPI/libusb
|
||||
SUBSYSTEM=="usb", ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="003f", MODE="0666"
|
||||
|
||||
|
||||
# If you are using the hidraw implementation, then do something like the
|
||||
# following, substituting the VID and PID with your device. Busnum 1 is USB.
|
||||
|
||||
# HIDAPI/hidraw
|
||||
KERNEL=="hidraw*", ATTRS{busnum}=="1", ATTRS{idVendor}=="04d8", ATTRS{idProduct}=="003f", MODE="0666"
|
||||
|
||||
# Once done, optionally rename this file for your device, and drop it into
|
||||
# /etc/udev/rules.d and unplug and re-plug your device. This is all that is
|
||||
# necessary to see the new permissions. Udev does not have to be restarted.
|
||||
|
||||
# If you think permissions of 0666 are too loose, then see:
|
||||
# http://reactivated.net/writing_udev_rules.html for more information on finer
|
||||
# grained permission setting. For example, it might be sufficient to just
|
||||
# set the group or user owner for specific devices (for example the plugdev
|
||||
# group on some systems).
|
14
cpp/hidapi/windows/Makefile
Normal file
14
cpp/hidapi/windows/Makefile
Normal file
|
@ -0,0 +1,14 @@
|
|||
|
||||
|
||||
OS=$(shell uname)
|
||||
|
||||
ifneq (,$(findstring MINGW,$(OS)))
|
||||
FILE=Makefile.mingw
|
||||
endif
|
||||
|
||||
ifeq ($(FILE), )
|
||||
all:
|
||||
$(error Your platform ${OS} is not supported at this time.)
|
||||
endif
|
||||
|
||||
include $(FILE)
|
32
cpp/hidapi/windows/Makefile.mingw
Normal file
32
cpp/hidapi/windows/Makefile.mingw
Normal file
|
@ -0,0 +1,32 @@
|
|||
###########################################
|
||||
# Simple Makefile for HIDAPI test program
|
||||
#
|
||||
# Alan Ott
|
||||
# Signal 11 Software
|
||||
# 2010-06-01
|
||||
###########################################
|
||||
|
||||
all: hidtest
|
||||
|
||||
CC=gcc
|
||||
CXX=g++
|
||||
COBJS=hid.o
|
||||
CPPOBJS=../hidtest/hidtest.o
|
||||
OBJS=$(COBJS) $(CPPOBJS)
|
||||
CFLAGS=-I../hidapi -g -c
|
||||
LIBS= -lsetupapi
|
||||
|
||||
|
||||
hidtest: $(OBJS)
|
||||
g++ -g $^ $(LIBS) -o hidtest
|
||||
|
||||
$(COBJS): %.o: %.c
|
||||
$(CC) $(CFLAGS) $< -o $@
|
||||
|
||||
$(CPPOBJS): %.o: %.cpp
|
||||
$(CXX) $(CFLAGS) $< -o $@
|
||||
|
||||
clean:
|
||||
rm *.o ../hidtest/*.o hidtest.exe
|
||||
|
||||
.PHONY: clean
|
|
@ -22,6 +22,10 @@
|
|||
|
||||
#include <windows.h>
|
||||
|
||||
#ifndef _NTDEF_
|
||||
typedef LONG NTSTATUS;
|
||||
#endif
|
||||
|
||||
#ifdef __MINGW32__
|
||||
#include <ntdef.h>
|
||||
#include <winbase.h>
|
||||
|
@ -34,9 +38,11 @@
|
|||
|
||||
//#define HIDAPI_USE_DDK
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#include <setupapi.h>
|
||||
#include "WinIoCTL.h"
|
||||
#include <winioctl.h>
|
||||
#ifdef HIDAPI_USE_DDK
|
||||
#include <hidsdi.h>
|
||||
#endif
|
||||
|
@ -46,7 +52,10 @@ extern "C" {
|
|||
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"
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
@ -58,7 +67,9 @@ extern "C" {
|
|||
#pragma warning(disable:4996)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef HIDAPI_USE_DDK
|
||||
// Since we're not building with the DDK, and the HID header
|
||||
|
@ -107,25 +118,35 @@ extern "C" {
|
|||
static HidD_FreePreparsedData_ HidD_FreePreparsedData;
|
||||
static HidP_GetCaps_ HidP_GetCaps;
|
||||
|
||||
static HMODULE lib_handle = NULL;
|
||||
static BOOLEAN initialized = FALSE;
|
||||
#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;
|
||||
BOOL read_pending;
|
||||
char *read_buf;
|
||||
OVERLAPPED ol;
|
||||
};
|
||||
|
||||
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->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);
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
@ -162,11 +183,11 @@ static void register_error(hid_device *device, const char *op)
|
|||
}
|
||||
|
||||
#ifndef HIDAPI_USE_DDK
|
||||
static void lookup_functions()
|
||||
static int lookup_functions()
|
||||
{
|
||||
HMODULE lib = LoadLibraryA("hid.dll");
|
||||
if (lib) {
|
||||
#define RESOLVE(x) x = (x##_)GetProcAddress(lib, #x);
|
||||
lib_handle = LoadLibraryA("hid.dll");
|
||||
if (lib_handle) {
|
||||
#define RESOLVE(x) x = (x##_)GetProcAddress(lib_handle, #x); if (!x) return -1;
|
||||
RESOLVE(HidD_GetAttributes);
|
||||
RESOLVE(HidD_GetSerialNumberString);
|
||||
RESOLVE(HidD_GetManufacturerString);
|
||||
|
@ -177,44 +198,89 @@ static void lookup_functions()
|
|||
RESOLVE(HidD_GetPreparsedData);
|
||||
RESOLVE(HidD_FreePreparsedData);
|
||||
RESOLVE(HidP_GetCaps);
|
||||
//FreeLibrary(lib);
|
||||
#undef RESOLVE
|
||||
}
|
||||
initialized = true;
|
||||
else
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static HANDLE open_device(const char *path, BOOL enumerate)
|
||||
{
|
||||
HANDLE handle;
|
||||
DWORD desired_access = (enumerate)? 0: (GENERIC_WRITE | GENERIC_READ);
|
||||
DWORD share_mode = (enumerate)?
|
||||
FILE_SHARE_READ|FILE_SHARE_WRITE:
|
||||
FILE_SHARE_READ;
|
||||
|
||||
handle = CreateFileA(path,
|
||||
desired_access,
|
||||
share_mode,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_FLAG_OVERLAPPED,//FILE_ATTRIBUTE_NORMAL,
|
||||
0);
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
int HID_API_EXPORT hid_init(void)
|
||||
{
|
||||
#ifndef HIDAPI_USE_DDK
|
||||
if (!initialized) {
|
||||
if (lookup_functions() < 0) {
|
||||
hid_exit();
|
||||
return -1;
|
||||
}
|
||||
initialized = TRUE;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
int HID_API_EXPORT hid_exit(void)
|
||||
{
|
||||
#ifndef HIDAPI_USE_DDK
|
||||
if (lib_handle)
|
||||
FreeLibrary(lib_handle);
|
||||
lib_handle = NULL;
|
||||
initialized = FALSE;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
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 *cur_dev = NULL;
|
||||
|
||||
#ifndef HIDAPI_USE_DDK
|
||||
if (!initialized)
|
||||
lookup_functions();
|
||||
#endif
|
||||
|
||||
// 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;
|
||||
|
||||
if (hid_init() < 0)
|
||||
return NULL;
|
||||
|
||||
// Initialize the Windows objects.
|
||||
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.
|
||||
device_info_set = SetupDiGetClassDevsA(&InterfaceClassGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
|
||||
|
||||
// Iterate over each device in the HID class, looking for the right one.
|
||||
int device_index = 0;
|
||||
|
||||
for (;;) {
|
||||
HANDLE write_handle = INVALID_HANDLE_VALUE;
|
||||
DWORD required_size = 0;
|
||||
HIDD_ATTRIBUTES attrib;
|
||||
|
||||
res = SetupDiEnumDeviceInterfaces(device_info_set,
|
||||
NULL,
|
||||
|
@ -261,13 +327,7 @@ struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned shor
|
|||
//wprintf(L"HandleName: %s\n", device_interface_detail_data->DevicePath);
|
||||
|
||||
// Open a handle to the device
|
||||
write_handle = CreateFileA(device_interface_detail_data->DevicePath,
|
||||
GENERIC_WRITE |GENERIC_READ,
|
||||
0x0, /*share mode*/
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_FLAG_OVERLAPPED,//FILE_ATTRIBUTE_NORMAL,
|
||||
0);
|
||||
write_handle = open_device(device_interface_detail_data->DevicePath, TRUE);
|
||||
|
||||
// Check validity of write_handle.
|
||||
if (write_handle == INVALID_HANDLE_VALUE) {
|
||||
|
@ -278,7 +338,6 @@ struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned shor
|
|||
|
||||
|
||||
// Get the Vendor ID and Product ID for this device.
|
||||
HIDD_ATTRIBUTES attrib;
|
||||
attrib.Size = sizeof(HIDD_ATTRIBUTES);
|
||||
HidD_GetAttributes(write_handle, &attrib);
|
||||
//wprintf(L"Product/Vendor: %x %x\n", attrib.ProductID, attrib.VendorID);
|
||||
|
@ -299,7 +358,7 @@ struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned shor
|
|||
size_t len;
|
||||
|
||||
/* VID/PID match. Create the record. */
|
||||
tmp = (hid_device_info*) calloc(1, sizeof(struct hid_device_info));
|
||||
tmp = (struct hid_device_info*) calloc(1, sizeof(struct hid_device_info));
|
||||
if (cur_dev) {
|
||||
cur_dev->next = tmp;
|
||||
}
|
||||
|
@ -360,8 +419,24 @@ struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned shor
|
|||
/* Release Number */
|
||||
cur_dev->release_number = attrib.VersionNumber;
|
||||
|
||||
/* Interface Number (Unsupported on Windows)*/
|
||||
/* Interface Number. It can sometimes be parsed out of the path
|
||||
on Windows if a device has multiple interfaces. See
|
||||
http://msdn.microsoft.com/en-us/windows/hardware/gg487473 or
|
||||
search for "Hardware IDs for HID Devices" at MSDN. If it's not
|
||||
in the path, it's set to -1. */
|
||||
cur_dev->interface_number = -1;
|
||||
if (cur_dev->path) {
|
||||
char *interface_component = strstr(cur_dev->path, "&mi_");
|
||||
if (interface_component) {
|
||||
char *hex_str = interface_component + 4;
|
||||
char *endptr = NULL;
|
||||
cur_dev->interface_number = strtol(hex_str, &endptr, 16);
|
||||
if (endptr == hex_str) {
|
||||
/* The parsing failed. Set interface_number to -1. */
|
||||
cur_dev->interface_number = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cont_close:
|
||||
|
@ -441,21 +516,14 @@ HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path)
|
|||
BOOLEAN res;
|
||||
NTSTATUS nt_res;
|
||||
|
||||
#ifndef HIDAPI_USE_DDK
|
||||
if (!initialized)
|
||||
lookup_functions();
|
||||
#endif
|
||||
if (hid_init() < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dev = new_hid_device();
|
||||
|
||||
// Open a handle to the device
|
||||
dev->device_handle = CreateFileA(path,
|
||||
GENERIC_WRITE |GENERIC_READ,
|
||||
0x0, /*share mode*/
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_FLAG_OVERLAPPED,//FILE_ATTRIBUTE_NORMAL,
|
||||
0);
|
||||
dev->device_handle = open_device(path, FALSE);
|
||||
|
||||
// Check validity of write_handle.
|
||||
if (dev->device_handle == INVALID_HANDLE_VALUE) {
|
||||
|
@ -475,9 +543,12 @@ HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path)
|
|||
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);
|
||||
|
||||
dev->read_buf = (char*) malloc(dev->input_report_length);
|
||||
|
||||
return dev;
|
||||
|
||||
err_pp_data:
|
||||
|
@ -494,15 +565,35 @@ 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.
|
||||
register_error(dev, "WriteFile");
|
||||
return -1;
|
||||
bytes_written = -1;
|
||||
goto end_of_function;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -512,49 +603,50 @@ int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char *
|
|||
if (!res) {
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
||||
int HID_API_EXPORT HID_API_CALL hid_read(hid_device *dev, unsigned char *data, size_t length)
|
||||
int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds)
|
||||
{
|
||||
DWORD bytes_read;
|
||||
DWORD bytes_read = 0;
|
||||
BOOL res;
|
||||
|
||||
HANDLE ev;
|
||||
ev = CreateEvent(NULL, FALSE, FALSE /*inital state f=nonsignaled*/, NULL);
|
||||
// Copy the handle for convenience.
|
||||
HANDLE ev = dev->ol.hEvent;
|
||||
|
||||
OVERLAPPED ol;
|
||||
memset(&ol, 0, sizeof(ol));
|
||||
ol.hEvent = ev;
|
||||
|
||||
// Limit the data to be returned. This ensures we get
|
||||
// only one report returned per call to hid_read().
|
||||
length = (length < dev->input_report_length)? length: dev->input_report_length;
|
||||
|
||||
res = ReadFile(dev->device_handle, data, length, &bytes_read, &ol);
|
||||
|
||||
|
||||
if (!res) {
|
||||
if (GetLastError() != ERROR_IO_PENDING) {
|
||||
// ReadFile() has failed.
|
||||
// Clean up and return error.
|
||||
CloseHandle(ev);
|
||||
goto end_of_function;
|
||||
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->ol);
|
||||
|
||||
if (!res) {
|
||||
if (GetLastError() != ERROR_IO_PENDING) {
|
||||
// ReadFile() has failed.
|
||||
// Clean up and return error.
|
||||
CancelIo(dev->device_handle);
|
||||
dev->read_pending = FALSE;
|
||||
goto end_of_function;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!dev->blocking) {
|
||||
if (milliseconds >= 0) {
|
||||
// See if there is any data yet.
|
||||
res = WaitForSingleObject(ev, 0);
|
||||
CloseHandle(ev);
|
||||
res = WaitForSingleObject(ev, milliseconds);
|
||||
if (res != WAIT_OBJECT_0) {
|
||||
// There was no data. Cancel this read and return.
|
||||
CancelIo(dev->device_handle);
|
||||
// Zero bytes available.
|
||||
// There was no data this time. Return zero bytes available,
|
||||
// but leave the Overlapped I/O running.
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -562,26 +654,43 @@ int HID_API_EXPORT HID_API_CALL hid_read(hid_device *dev, unsigned char *data, s
|
|||
// 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, &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;
|
||||
|
||||
if (bytes_read > 0 && data[0] == 0x0) {
|
||||
/* If report numbers aren't being used, but Windows sticks a report
|
||||
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. */
|
||||
bytes_read--;
|
||||
memmove(data, data+1, bytes_read);
|
||||
if (res && bytes_read > 0) {
|
||||
if (dev->read_buf[0] == 0x0) {
|
||||
/* If report numbers aren't being used, but Windows sticks a report
|
||||
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;
|
||||
memcpy(data, dev->read_buf, copy_len);
|
||||
}
|
||||
}
|
||||
|
||||
end_of_function:
|
||||
if (!res) {
|
||||
register_error(dev, "ReadFile");
|
||||
register_error(dev, "GetOverlappedResult");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return bytes_read;
|
||||
}
|
||||
|
||||
int HID_API_EXPORT HID_API_CALL 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_API_CALL hid_set_nonblocking(hid_device *dev, int nonblock)
|
||||
{
|
||||
dev->blocking = !nonblock;
|
||||
|
@ -646,8 +755,11 @@ 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);
|
||||
}
|
||||
|
||||
|
@ -776,5 +888,6 @@ int __cdecl main(int argc, char* argv[])
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
|
||||
#endif
|
||||
|
|
|
@ -175,7 +175,7 @@
|
|||
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
|
||||
>
|
||||
<File
|
||||
RelativePath=".\hid.cpp"
|
||||
RelativePath=".\hid.c"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
|
|
|
@ -166,6 +166,7 @@ bool HidController::Close
|
|||
}
|
||||
|
||||
m_bOpen = false;
|
||||
hid_exit();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -246,7 +247,7 @@ bool HidController::Init
|
|||
int hidApiResult;
|
||||
const uint8 dataOutEnableZwave[3] = { 0x02, 0x01, 0x04 };
|
||||
|
||||
Log::Write( "Open HID port %s", m_hidControllerName.c_str() );
|
||||
Log::Write( "Open HID port %s", m_hidControllerName.c_str() );
|
||||
m_hHidController = hid_open(m_vendorId, m_productId, NULL);
|
||||
if (!m_hHidController)
|
||||
{
|
||||
|
@ -343,17 +344,17 @@ bool HidController::Init
|
|||
hidApiResult = hid_set_nonblocking(m_hHidController, 0);
|
||||
CHECK_HIDAPI_RESULT(hidApiResult, HidOpenFailure);
|
||||
|
||||
// Open successful
|
||||
// Open successful
|
||||
m_bOpen = true;
|
||||
return true;
|
||||
return true;
|
||||
|
||||
HidOpenFailure:
|
||||
Log::Write( "Failed to open HID port %s", m_hidControllerName.c_str() );
|
||||
Log::Write( "Failed to open HID port %s", m_hidControllerName.c_str() );
|
||||
const wchar_t* errString = hid_error(m_hHidController);
|
||||
Log::Write("HIDAPI ERROR STRING (if any):\n%ls\n", errString);
|
||||
hid_close(m_hHidController);
|
||||
m_hHidController = NULL;
|
||||
return false;
|
||||
m_hHidController = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -480,9 +481,9 @@ uint32 HidController::Write
|
|||
//-----------------------------------------------------------------------------
|
||||
int HidController::GetFeatureReport
|
||||
(
|
||||
uint32 _length,
|
||||
uint32 _length,
|
||||
uint8 _reportId,
|
||||
uint8* _buffer
|
||||
uint8* _buffer
|
||||
)
|
||||
{
|
||||
int result;
|
||||
|
|
|
@ -142,11 +142,11 @@ bool EventImpl::Wait
|
|||
else
|
||||
{
|
||||
++m_waitingThreads;
|
||||
if( _timeout == 0 )
|
||||
if( _timeout == 0 )
|
||||
{
|
||||
result = m_isSignaled;
|
||||
}
|
||||
else if( _timeout > 0 )
|
||||
result = m_isSignaled;
|
||||
}
|
||||
else if( _timeout > 0 )
|
||||
{
|
||||
struct timeval now;
|
||||
struct timespec abstime;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue