FIDO-Raw
view release on metacpan or search on metacpan
deps/hidapi/libusb/hid.c view on Meta::CPAN
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
#ifdef DEBUG_PRINTF
#define LOG(...) fprintf(stderr, __VA_ARGS__)
#else
#define LOG(...) do {} while (0)
#endif
#ifndef __FreeBSD__
#define DETACH_KERNEL_DRIVER
#endif
/* Uncomment to enable the retrieval of Usage and Usage Page in
hid_enumerate(). Warning, on platforms different from FreeBSD
this is very invasive as it requires the detach
and re-attach of the kernel driver. See comments inside hid_enumerate().
libusb HIDAPI programs are encouraged to use the interface number
instead to differentiate between interfaces on a composite HID device. */
/*#define INVASIVE_GET_USAGE*/
/* Linked List of input reports received from the device. */
struct input_report {
uint8_t *data;
size_t len;
struct input_report *next;
};
struct hid_device_ {
/* Handle to the actual device. */
libusb_device_handle *device_handle;
/* Endpoint information */
int input_endpoint;
int output_endpoint;
int input_ep_max_packet_size;
/* The interface number of the HID */
int interface;
/* Indexes of Strings */
int manufacturer_index;
int product_index;
int serial_index;
/* Whether blocking reads are used */
int blocking; /* boolean */
/* Read thread objects */
pthread_t thread;
pthread_mutex_t mutex; /* Protects input_reports */
pthread_cond_t condition;
pthread_barrier_t barrier; /* Ensures correct startup sequence */
int shutdown_thread;
int cancelled;
struct libusb_transfer *transfer;
/* List of received input reports. */
struct input_report *input_reports;
/* Was kernel driver detached by libusb */
#ifdef DETACH_KERNEL_DRIVER
int is_driver_detached;
#endif
};
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);
static hid_device *new_hid_device(void)
{
hid_device *dev = (hid_device*) calloc(1, sizeof(hid_device));
dev->blocking = 1;
pthread_mutex_init(&dev->mutex, NULL);
pthread_cond_init(&dev->condition, NULL);
pthread_barrier_init(&dev->barrier, NULL, 2);
return dev;
}
static void free_hid_device(hid_device *dev)
{
/* Clean up the thread objects */
pthread_barrier_destroy(&dev->barrier);
pthread_cond_destroy(&dev->condition);
pthread_mutex_destroy(&dev->mutex);
/* Free the device itself */
free(dev);
}
#if 0
deps/hidapi/libusb/hid.c view on Meta::CPAN
dev->input_reports = rpt;
pthread_cond_signal(&dev->condition);
}
else {
/* Find the end of the list and attach. */
struct input_report *cur = dev->input_reports;
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);
}
}
pthread_mutex_unlock(&dev->mutex);
}
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) {
//LOG("Timeout (normal)\n");
}
else {
LOG("Unknown transfer code: %d\n", transfer->status);
}
/* Re-submit the transfer object. */
res = libusb_submit_transfer(transfer);
if (res != 0) {
LOG("Unable to submit URB. libusb error code: %d\n", res);
dev->shutdown_thread = 1;
dev->cancelled = 1;
}
}
static void *read_thread(void *param)
{
hid_device *dev = param;
uint8_t *buf;
const size_t length = dev->input_ep_max_packet_size;
/* Set up the transfer object. */
buf = (uint8_t*) malloc(length);
dev->transfer = libusb_alloc_transfer(0);
libusb_fill_interrupt_transfer(dev->transfer,
dev->device_handle,
dev->input_endpoint,
buf,
length,
read_callback,
dev,
5000/*timeout*/);
/* Make the first submission. Further submissions are made
from inside read_callback() */
libusb_submit_transfer(dev->transfer);
/* Notify the main thread that the read thread is up and running. */
pthread_barrier_wait(&dev->barrier);
/* Handle all the events. */
while (!dev->shutdown_thread) {
int res;
res = libusb_handle_events(usb_context);
if (res < 0) {
/* There was an error. */
LOG("read_thread(): libusb reports error # %d\n", res);
/* Break out of this loop only on fatal error.*/
if (res != LIBUSB_ERROR_BUSY &&
res != LIBUSB_ERROR_TIMEOUT &&
res != LIBUSB_ERROR_OVERFLOW &&
res != LIBUSB_ERROR_INTERRUPTED) {
break;
}
}
}
/* Cancel any transfer that may be pending. This call will fail
if no transfers are pending, but that's OK. */
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
make sure that a thread which is about to go to sleep waiting on
the condition actually 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
could end either due to a disconnect or due to a user
call to hid_close(). In both cases the objects can be safely
cleaned up after the call to pthread_join() (in hid_close()), but
since hid_close() calls libusb_cancel_transfer(), on these objects,
they can not be cleaned up here. */
return NULL;
}
hid_device * HID_API_EXPORT hid_open_path(const char *path)
deps/hidapi/libusb/hid.c view on Meta::CPAN
struct libusb_config_descriptor *conf_desc = NULL;
int i,j,k;
libusb_get_device_descriptor(usb_dev, &desc);
if (libusb_get_active_config_descriptor(usb_dev, &conf_desc) < 0)
continue;
for (j = 0; j < conf_desc->bNumInterfaces; j++) {
const struct libusb_interface *intf = &conf_desc->interface[j];
for (k = 0; k < intf->num_altsetting; k++) {
const struct libusb_interface_descriptor *intf_desc;
intf_desc = &intf->altsetting[k];
if (intf_desc->bInterfaceClass == LIBUSB_CLASS_HID) {
char *dev_path = make_path(usb_dev, intf_desc->bInterfaceNumber);
if (!strcmp(dev_path, path)) {
/* Matched Paths. Open this device */
/* OPEN HERE */
res = libusb_open(usb_dev, &dev->device_handle);
if (res < 0) {
LOG("can't open device\n");
free(dev_path);
break;
}
good_open = 1;
#ifdef DETACH_KERNEL_DRIVER
/* Detach the kernel driver, but only if the
device is managed by the kernel */
dev->is_driver_detached = 0;
if (libusb_kernel_driver_active(dev->device_handle, intf_desc->bInterfaceNumber) == 1) {
res = libusb_detach_kernel_driver(dev->device_handle, intf_desc->bInterfaceNumber);
if (res < 0) {
libusb_close(dev->device_handle);
LOG("Unable to detach Kernel Driver\n");
free(dev_path);
good_open = 0;
break;
}
else {
dev->is_driver_detached = 1;
LOG("Driver successfully detached from kernel.\n");
}
}
#endif
res = libusb_claim_interface(dev->device_handle, intf_desc->bInterfaceNumber);
if (res < 0) {
LOG("can't claim interface %d: %d\n", intf_desc->bInterfaceNumber, res);
free(dev_path);
libusb_close(dev->device_handle);
good_open = 0;
break;
}
/* Store off the string descriptor indexes */
dev->manufacturer_index = desc.iManufacturer;
dev->product_index = desc.iProduct;
dev->serial_index = desc.iSerialNumber;
/* Store off the interface number */
dev->interface = intf_desc->bInterfaceNumber;
/* Find the INPUT and OUTPUT endpoints. An
OUTPUT endpoint is not required. */
for (i = 0; i < intf_desc->bNumEndpoints; i++) {
const struct libusb_endpoint_descriptor *ep
= &intf_desc->endpoint[i];
/* Determine the type and direction of this
endpoint. */
int is_interrupt =
(ep->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK)
== LIBUSB_TRANSFER_TYPE_INTERRUPT;
int is_output =
(ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK)
== LIBUSB_ENDPOINT_OUT;
int is_input =
(ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK)
== LIBUSB_ENDPOINT_IN;
/* Decide whether to use it for input or output. */
if (dev->input_endpoint == 0 &&
is_interrupt && is_input) {
/* Use this endpoint for INPUT */
dev->input_endpoint = ep->bEndpointAddress;
dev->input_ep_max_packet_size = ep->wMaxPacketSize;
}
if (dev->output_endpoint == 0 &&
is_interrupt && is_output) {
/* Use this endpoint for OUTPUT */
dev->output_endpoint = ep->bEndpointAddress;
}
}
pthread_create(&dev->thread, NULL, read_thread, dev);
/* Wait here for the read thread to be initialized. */
pthread_barrier_wait(&dev->barrier);
}
free(dev_path);
}
}
}
libusb_free_config_descriptor(conf_desc);
}
libusb_free_device_list(devs, 1);
/* If we have a good handle, return it. */
if (good_open) {
return dev;
}
else {
/* Unable to open any devices. */
free_hid_device(dev);
return NULL;
}
}
int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t length)
{
int res;
int report_number = data[0];
int skipped_report_id = 0;
if (report_number == 0x0) {
data++;
length--;
skipped_report_id = 1;
}
if (dev->output_endpoint <= 0) {
/* No interrupt out endpoint. Use the Control Endpoint */
res = libusb_control_transfer(dev->device_handle,
LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE|LIBUSB_ENDPOINT_OUT,
0x09/*HID Set_Report*/,
(2/*HID output*/ << 8) | report_number,
dev->interface,
(unsigned char *)data, length,
1000/*timeout millis*/);
if (res < 0)
return -1;
if (skipped_report_id)
length++;
return length;
}
else {
/* Use the interrupt out endpoint */
int actual_length;
res = libusb_interrupt_transfer(dev->device_handle,
dev->output_endpoint,
(unsigned char*)data,
length,
&actual_length, 1000);
if (res < 0)
return -1;
if (skipped_report_id)
actual_length++;
return actual_length;
}
}
/* Helper function, to simplify hid_read().
This should be called with dev->mutex locked. */
static int return_data(hid_device *dev, unsigned char *data, size_t length)
{
/* Copy the data out of the linked list item (rpt) into the
return buffer (data), and delete the liked list item. */
struct input_report *rpt = dev->input_reports;
size_t len = (length < rpt->len)? length: rpt->len;
if (len > 0)
memcpy(data, rpt->data, len);
dev->input_reports = rpt->next;
free(rpt->data);
free(rpt);
return len;
}
static void cleanup_mutex(void *param)
{
hid_device *dev = param;
pthread_mutex_unlock(&dev->mutex);
}
int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds)
{
int bytes_read = -1;
#if 0
int transferred;
int res = libusb_interrupt_transfer(dev->device_handle, dev->input_endpoint, data, length, &transferred, 5000);
LOG("transferred: %d\n", transferred);
return transferred;
#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) {
/* Return the first one */
bytes_read = return_data(dev, data, length);
goto ret;
}
if (dev->shutdown_thread) {
/* This means the device has been disconnected.
An error code of -1 should be returned. */
bytes_read = -1;
goto ret;
}
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 {
( run in 0.494 second using v1.01-cache-2.11-cpan-97f6503c9c8 )