mirror of https://github.com/nirenjan/libx52.git
Add hotplug support to libusbx52
parent
946916f456
commit
aa259bf343
|
@ -9,18 +9,18 @@ ACLOCAL_AMFLAGS = -I m4
|
|||
# libusb stub library for use by test programs
|
||||
check_LTLIBRARIES = libusbx52.la
|
||||
|
||||
libusbx52_la_SOURCES = usb_x52_stub.c
|
||||
libusbx52_la_CFLAGS = @LIBUSB_CFLAGS@
|
||||
libusbx52_la_SOURCES = usb_x52_stub.c usb_x52_hotplug.c usb_x52_vector.c
|
||||
libusbx52_la_CFLAGS = @LIBUSB_CFLAGS@ -pthread
|
||||
libusbx52_la_LDFLAGS = -rpath /nowhere -module
|
||||
|
||||
# Utility programs for use by tests
|
||||
check_PROGRAMS = x52test_create_device_list x52test_log_actions
|
||||
|
||||
x52test_create_device_list_SOURCES = util/create_device_list.c
|
||||
x52test_create_device_list_CFLAGS = @LIBUSB_CFLAGS@
|
||||
x52test_create_device_list_CFLAGS = @LIBUSB_CFLAGS@ -pthread
|
||||
|
||||
x52test_log_actions_SOURCES = util/log_actions.c
|
||||
x52test_log_actions_CFLAGS = @X52_INCLUDE@ @LIBUSB_CFLAGS@
|
||||
x52test_log_actions_CFLAGS = @X52_INCLUDE@ @LIBUSB_CFLAGS@ -pthread
|
||||
x52test_log_actions_LDADD = libusbx52.la
|
||||
|
||||
EXTRA_DIST = README.md libusbx52.h
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
// #include <libusb-1.0/libusb.h>
|
||||
#include <pthread.h>
|
||||
#include <libusb.h>
|
||||
|
||||
struct libusb_device {
|
||||
|
@ -23,6 +23,17 @@ struct libusb_context {
|
|||
int debug_level;
|
||||
int num_devices;
|
||||
struct libusb_device *devices;
|
||||
|
||||
// Hotplug support
|
||||
int hotplug_vid;
|
||||
int hotplug_pid;
|
||||
libusb_hotplug_event events;
|
||||
libusb_hotplug_callback_fn callback;
|
||||
void * cb_user_data;
|
||||
|
||||
// Hotplug threading
|
||||
volatile int stop_thread;
|
||||
pthread_t hotplug_pthread;
|
||||
};
|
||||
|
||||
struct libusb_device_handle {
|
||||
|
@ -65,3 +76,20 @@ struct libusb_device_handle {
|
|||
*/
|
||||
#define DEFAULT_OUTPUT_DATA_FILE "/tmp/libusbx52_output_data"
|
||||
|
||||
/**
|
||||
* @brief Device update FIFO environment variable
|
||||
*
|
||||
* This is used by the test driver to update the simulated USB device list
|
||||
*/
|
||||
#define INPUT_DEVICE_FIFO_ENV "LIBUSBX52_DEVICE_FIFO"
|
||||
|
||||
/**
|
||||
* @brief Default file location of the device update FIFO
|
||||
*
|
||||
* This FIFO is read by a thread in libusbx52 and used to simulate the hotplug
|
||||
* functionality of libusb.
|
||||
*/
|
||||
#define DEFAULT_INPUT_DEVICE_FIFO_FILE "/tmp/libusbx52_device_fifo"
|
||||
|
||||
libusb_device* vector_push(libusb_context *ctx, int vid, int pid);
|
||||
libusb_device* vector_pop(libusb_context *ctx, int vid, int pid);
|
||||
|
|
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
* LibUSB stub driver for hotplug APIs
|
||||
*
|
||||
* Copyright (C) 2020 Nirenjan Krishnan (nirenjan@nirenjan.org)
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only WITH Classpath-exception-2.0
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include <libusb.h>
|
||||
#include "libusbx52.h"
|
||||
|
||||
#define CB_HANDLE_MAGIC 0xca11bacc
|
||||
|
||||
static void *service_hotplug_events(void *args)
|
||||
{
|
||||
libusb_context *ctx = args;
|
||||
int parsed;
|
||||
int vid;
|
||||
int pid;
|
||||
char action;
|
||||
char *dev_fifo_file;
|
||||
FILE *fifo;
|
||||
libusb_device *dev;
|
||||
|
||||
// Get the filename of the FIFO
|
||||
dev_fifo_file = getenv(INPUT_DEVICE_FIFO_ENV);
|
||||
if (dev_fifo_file == NULL || dev_fifo_file[0] == '\0') {
|
||||
dev_fifo_file = DEFAULT_INPUT_DEVICE_FIFO_FILE;
|
||||
}
|
||||
|
||||
// Remove the FIFO if it exists
|
||||
unlink(dev_fifo_file);
|
||||
|
||||
// Create the FIFO
|
||||
mkfifo(dev_fifo_file, 0777);
|
||||
|
||||
fifo = fopen(dev_fifo_file, "r");
|
||||
if (fifo == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while (!ctx->stop_thread) {
|
||||
parsed = fscanf(fifo, "%c %x %x", &action, &vid, &pid);
|
||||
if (parsed == 3) {
|
||||
switch (action) {
|
||||
case '+':
|
||||
dev = vector_push(ctx, vid, pid);
|
||||
if ((ctx->events & LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED) &&
|
||||
(ctx->hotplug_vid == LIBUSB_HOTPLUG_MATCH_ANY || ctx->hotplug_vid == vid) &&
|
||||
(ctx->hotplug_pid == LIBUSB_HOTPLUG_MATCH_ANY || ctx->hotplug_pid == pid)) {
|
||||
(ctx->callback)(ctx, dev,
|
||||
LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED,
|
||||
ctx->cb_user_data);
|
||||
}
|
||||
break;
|
||||
|
||||
case '-':
|
||||
dev = vector_pop(ctx, vid, pid);
|
||||
if ((ctx->events & LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) &&
|
||||
(ctx->hotplug_vid == LIBUSB_HOTPLUG_MATCH_ANY || ctx->hotplug_vid == vid) &&
|
||||
(ctx->hotplug_pid == LIBUSB_HOTPLUG_MATCH_ANY || ctx->hotplug_pid == pid)) {
|
||||
(ctx->callback)(ctx, dev,
|
||||
LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT,
|
||||
ctx->cb_user_data);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'x':
|
||||
/* Terminate the loop */
|
||||
ctx->stop_thread = 1;
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Close the FIFO
|
||||
fclose(fifo);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int libusb_hotplug_register_callback(libusb_context *ctx,
|
||||
libusb_hotplug_event events,
|
||||
libusb_hotplug_flag flags,
|
||||
int vendor_id,
|
||||
int product_id,
|
||||
int dev_class,
|
||||
libusb_hotplug_callback_fn cb_fn,
|
||||
void *user_data,
|
||||
libusb_hotplug_callback_handle *cb_handle)
|
||||
{
|
||||
// Save the events, flags, etc in the context.
|
||||
ctx->hotplug_vid = vendor_id;
|
||||
ctx->hotplug_pid = product_id;
|
||||
ctx->events = events;
|
||||
|
||||
ctx->callback = cb_fn;
|
||||
ctx->cb_user_data = user_data;
|
||||
|
||||
// Spawn the thread
|
||||
pthread_create(&(ctx->hotplug_pthread), NULL, service_hotplug_events, ctx);
|
||||
|
||||
// Since the stub library only accepts a single callback function,
|
||||
// we will return a static value that will be used to check when
|
||||
// deregistering. Only if the value matches will we terminate the thread.
|
||||
*cb_handle = CB_HANDLE_MAGIC;
|
||||
return LIBUSB_SUCCESS;
|
||||
}
|
||||
|
||||
void libusb_hotplug_deregister_callback(libusb_context *ctx,
|
||||
libusb_hotplug_callback_handle cb_handle)
|
||||
{
|
||||
if (cb_handle == CB_HANDLE_MAGIC) {
|
||||
ctx->stop_thread = 1;
|
||||
pthread_join(ctx->hotplug_pthread, NULL);
|
||||
}
|
||||
}
|
|
@ -51,7 +51,6 @@ int libusb_init(libusb_context **ctx)
|
|||
}
|
||||
|
||||
/* Determine the number of devices in the file */
|
||||
dev_count = 0;
|
||||
do {
|
||||
parsed = fscanf(dev_list, "%x %x", &vid, &pid);
|
||||
/*
|
||||
|
@ -61,41 +60,12 @@ int libusb_init(libusb_context **ctx)
|
|||
if (parsed < 2) {
|
||||
break;
|
||||
}
|
||||
dev_count++;
|
||||
|
||||
if (vector_push(tmp_ctx, vid, pid) == NULL) {
|
||||
goto init_err_recovery;
|
||||
}
|
||||
} while (!feof(dev_list));
|
||||
|
||||
/* Make sure we have at least 1 device */
|
||||
if (dev_count == 0) {
|
||||
rc = LIBUSB_ERROR_NOT_FOUND;
|
||||
goto init_err_recovery;
|
||||
}
|
||||
|
||||
/* We now have the number of devices, allocate memory for them */
|
||||
tmp_ctx->devices = calloc(dev_count, sizeof(*(tmp_ctx->devices)));
|
||||
if (tmp_ctx->devices == NULL) {
|
||||
rc = LIBUSB_ERROR_NO_MEM;
|
||||
goto init_err_recovery;
|
||||
}
|
||||
tmp_ctx->num_devices = dev_count;
|
||||
|
||||
/* Rewind and read the file again, but now put them into the device list */
|
||||
rewind(dev_list);
|
||||
|
||||
for (i = 0; i < dev_count && !feof(dev_list); i++) {
|
||||
/* Set the base fields */
|
||||
tmp_ctx->devices[i].context = tmp_ctx;
|
||||
tmp_ctx->devices[i].index = i;
|
||||
|
||||
parsed = fscanf(dev_list, "%x %x", &vid, &pid);
|
||||
if (parsed < 2) {
|
||||
/* Parse error, skip this device */
|
||||
continue;
|
||||
}
|
||||
/* Set the VID & PID */
|
||||
tmp_ctx->devices[i].desc.idVendor = vid;
|
||||
tmp_ctx->devices[i].desc.idProduct = pid;
|
||||
}
|
||||
|
||||
/* Done, close the file and return */
|
||||
fclose(dev_list);
|
||||
|
||||
|
@ -103,6 +73,11 @@ int libusb_init(libusb_context **ctx)
|
|||
return LIBUSB_SUCCESS;
|
||||
|
||||
init_err_recovery:
|
||||
/* Free the device list, if it is available */
|
||||
if (tmp_ctx->devices != NULL) {
|
||||
free(tmp_ctx->devices);
|
||||
}
|
||||
|
||||
/* Close the device list file if it is open */
|
||||
if (dev_list) {
|
||||
fclose(dev_list);
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* LibUSB stub - device list
|
||||
*
|
||||
* Copyright (C) 2020 Nirenjan Krishnan (nirenjan@nirenjan.org)
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-only WITH Classpath-exception-2.0
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#include "libusbx52.h"
|
||||
|
||||
static pthread_mutex_t vector_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
libusb_device * vector_push(libusb_context *ctx, int vid, int pid)
|
||||
{
|
||||
int i;
|
||||
int num_devices;
|
||||
libusb_device *devlist;
|
||||
|
||||
pthread_mutex_lock(&vector_mutex);
|
||||
|
||||
// Make sure that we have an empty slot, if not, we need to reallocate the
|
||||
// device list
|
||||
for (i = 0; i < ctx->num_devices; i++) {
|
||||
if (ctx->devices[i].context == NULL) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == ctx->num_devices) {
|
||||
// No empty slots, we will need to reallocate the device list
|
||||
num_devices = ctx->num_devices + 1;
|
||||
devlist = calloc(num_devices, sizeof(*devlist));
|
||||
if (devlist == NULL) {
|
||||
pthread_mutex_unlock(&vector_mutex);
|
||||
return NULL;
|
||||
}
|
||||
memcpy(devlist, ctx->devices, ctx->num_devices * sizeof(*devlist));
|
||||
ctx->num_devices = num_devices;
|
||||
|
||||
free(ctx->devices);
|
||||
ctx->devices = devlist;
|
||||
}
|
||||
|
||||
ctx->devices[i].context = ctx;
|
||||
ctx->devices[i].index = i;
|
||||
ctx->devices[i].desc.idVendor = vid;
|
||||
ctx->devices[i].desc.idProduct = pid;
|
||||
|
||||
pthread_mutex_unlock(&vector_mutex);
|
||||
return &(ctx->devices[i]);
|
||||
}
|
||||
|
||||
libusb_device * vector_pop(libusb_context *ctx, int vid, int pid)
|
||||
{
|
||||
int i;
|
||||
libusb_device *dev;
|
||||
|
||||
// Search through the device list for a matching VID/PID pair
|
||||
// If found, then delete it from the list
|
||||
|
||||
pthread_mutex_lock(&vector_mutex);
|
||||
|
||||
for (i = 0; i < ctx->num_devices; i++) {
|
||||
dev = &(ctx->devices[i]);
|
||||
if (dev->desc.idVendor == vid && dev->desc.idProduct == pid) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i < ctx->num_devices) {
|
||||
memset(dev, 0, sizeof(*dev));
|
||||
} else {
|
||||
dev = NULL;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&vector_mutex);
|
||||
|
||||
return dev;
|
||||
}
|
Loading…
Reference in New Issue