Revert hotplug support in libx52

This removes the earlier work done in libx52 to support USB hotplug.
This wasn't adequately tested, and the reason to have hotplug support
was to address perceived deficiencies in the standard API.

However, on recent reflection and experimentation, it seems to be easier
to support adding methods to connect to an X52/X52Pro joystick
dynamically after initializing the library. This approach also lends
itself to adding checks when sending control packets to close the device
handle when it detects device disconnection. Also, one could add a
disconnect method to disconnect from any connected joysticks.

Finally, this commit reverts a series of commits that chronicled my
journey into implementing hotplug support and simulating it in
libusbx52. By coalescing the revert into a single commit, it makes it
easier to revert the revert in the future, if necessary.
debian-packaging
nirenjan 2020-05-22 00:42:01 -07:00
parent 97743d4ebd
commit cc8d6e9344
11 changed files with 45 additions and 716 deletions

View File

@ -6,7 +6,6 @@ The format is based upon [Keep a Changelog].
## [Unreleased]
### Added
- USB Hotplug support to libx52 and libusbx52
- Internationalization for the following:
* libx52
* x52test

View File

@ -1,63 +0,0 @@
libx52 Hotplug Support
======================
This document describes a design for USB hotplug support in libx52. The idea is
to have a daemon process running at all times, which will update the hardware
when the X52/X52Pro is plugged in.
# Assumptions
The core assumption is that a maximum of 1 device is present at any time. There
is no goal to support more than 1 X52/X52Pro at the same time.
# LibUSB Hotplug Support
libusb-1.0 version 1.0.16 and newer support hotplug notifications when a device
is inserted or removed. These call a registered callback function which can read
the device descriptor, and take action accordingly.
# libx52 Hotplug design
The hotplug mechanism consists of the following functions:
```
int libx52_hotplug_init(libx52_hotplug_service **svc);
void libx52_hotplug_exit(libx52_hotplug_service *svc);
int libx52_hotplug_register_callback(libx52_hotplug_service *svc,
libx52_hotplug_fn *callback_fn,
void *user_data,
libx52_hotplug_callback_handle **cb_handle);
int libx52_hotplug_deregister_callback(libx52_hotplug_callback_handle *cb_handle);
typedef void (*libx52_hotplug_fn)(bool inserted, void *user_data, libx52_device *dev);
```
The init function will take care of initializing libx52, so the user should
not call `libx52_init`. The exit function will also take care of deinitializing
libx52, so the user should not call `libx52_exit`.
The user may register any number of callbacks, and they are called in an
undefined order on device insertion and removal. Each register callback also
returns a callback handle in an output parameter. This callback handle can be
used to deregister the callback in the future, if necessary.
`libx52_hotplug_exit` will take care of deregistering all registered callbacks,
and will deinitialize libx52.
# Interaction with libusbx52
As of this writing, libusbx52 reads from a regular file, however, in order to
support hotplug, the read should read from a fifo. Under Linux, a program can
open a fifo for reading and writing, and this should allow the program to write
a small amount of data into the fifo and exit, and the program using libusbx52
can read from that file.
In this case, the data format should change to include an additional character
prior to the idVendor and idProduct fields. This additional character will
indicate whether the device is being inserted or removed.
libusbx52 must now spawn a separate thread to deal with the fifo. This thread
will read from the fifo, and callback the registered handler when an event
occurs.

View File

@ -14,7 +14,7 @@ CC = $(PTHREAD_CC)
# libusb stub library for use by test programs
check_LTLIBRARIES = libusbx52.la
libusbx52_la_SOURCES = usb_x52_stub.c usb_x52_hotplug.c usb_x52_vector.c
libusbx52_la_SOURCES = usb_x52_stub.c
libusbx52_la_CFLAGS = @LIBUSB_CFLAGS@
libusbx52_la_LDFLAGS = -rpath /nowhere -module

View File

@ -7,7 +7,7 @@
*/
#include <stdio.h>
#include <pthread.h>
// #include <libusb-1.0/libusb.h>
#include <libusb.h>
struct libusb_device {
@ -23,17 +23,6 @@ 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 {
@ -76,20 +65,3 @@ 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);

View File

@ -1,124 +0,0 @@
/*
* 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);
}
}

View File

@ -51,6 +51,7 @@ 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);
/*
@ -60,11 +61,40 @@ int libusb_init(libusb_context **ctx)
if (parsed < 2) {
break;
}
dev_count++;
} while (!feof(dev_list));
if (vector_push(tmp_ctx, vid, pid) == NULL) {
/* Make sure we have at least 1 device */
if (dev_count == 0) {
rc = LIBUSB_ERROR_NOT_FOUND;
goto init_err_recovery;
}
} while (!feof(dev_list));
/* 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);
@ -73,11 +103,6 @@ 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);

View File

@ -1,83 +0,0 @@
/*
* 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;
}

View File

@ -24,4 +24,4 @@ x52include_HEADERS = libx52.h
pkgconfig_DATA = libx52.pc
# Extra files that need to be in the distribution
EXTRA_DIST = libx52.h x52_commands.h x52_common.h x52_hotplug.h README.md
EXTRA_DIST = libx52.h x52_commands.h x52_common.h README.md

View File

@ -6,22 +6,11 @@
* SPDX-License-Identifier: GPL-2.0-only WITH Classpath-exception-2.0
*/
/**
* @file libx52.h
* @brief Headers for the Saitek X52 MFD & LED driver library
*
* This file contains the type and function prototypes for the Saitek X52
* driver library. These are only needed to control the MFD and LEDs, and
* don't impact the joystick (HID) functionality.
*
* @author Nirenjan Krishnan (nirenjan@nirenjan.org)
*/
#ifndef LIBX52_H
#define LIBX52_H
#include <time.h>
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
@ -39,38 +28,6 @@ struct libx52_device;
*/
typedef struct libx52_device libx52_device;
/**
* @addtogroup libx52hotplug
* @{
*/
/**
* @brief Opaque structure used by libx52 hotplug API
*/
struct libx52_hotplug_callback_handle;
/**
* @brief Opaque structure used by libx52 hotplug API
*/
typedef struct libx52_hotplug_callback_handle libx52_hotplug_callback_handle;
/**
* @brief Opaque structure used by libx52 hotplug API
*/
struct libx52_hotplug_service;
/**
* @brief Opaque structure used by libx52 hotplug API
*/
typedef struct libx52_hotplug_service libx52_hotplug_service;
/**
* @brief Callback function for hotplug events
*/
typedef void (*libx52_hotplug_fn)(bool inserted, void *user_data, libx52_device *dev);
/** @} */
/**
* @brief List of supported clocks on the MFD
* @ingroup libx52clock
@ -252,7 +209,7 @@ typedef enum {
* structures to access the joystick, and returns a \ref libx52_device pointer.
* All calls to libx52 use the returned pointer to control the device.
*
* If no joystick is found `libx52_init()` returns \ref LIBX52_ERROR_NO_DEVICE.
* If no joystick is found `libx52_init()` returns _NULL_.
*
* @par Limitations
* This function does not support hotplugging. The joystick must be plugged in
@ -276,90 +233,10 @@ int libx52_init(libx52_device ** dev);
*/
void libx52_exit(libx52_device *dev);
/**
* @brief Initialize the X52 library hotplug service
*
* This function initializes the libx52 library hotplug service, which will
* monitor the bus for insertion and removal of any X52/X52Pro joystick, and
* will trigger registered callbacks on such events.
*
* @see libx52_hotplug_register_callback
* @see libx52_hotplug_deregister_callback
*
* @param[out] svc Pointer to a \ref libx52_hotplug_service *
*
* @returns \ref libx52_error_code indicating status
*/
int libx52_hotplug_init(libx52_hotplug_service **svc);
/**
* @brief Exit the library and free up any resources used
*
* This function releases any resources allocated by \ref libx52_hotplug_init
* and terminates the library. Using the freed device now is invalid and can
* cause errors.
*
* @param[in] svc Pointer to the hotplug service
* @returns None
*/
void libx52_hotplug_exit(libx52_hotplug_service *svc);
/** @} */
/**
* @defgroup libx52hotplug Hotplug Callbacks
* @{
*/
/**
* @brief Register a callback function
*
* This function registers a callback function that is triggered when a
* supported joystick is inserted or removed.
*
* @param[in] svc Pointer to the hotplug service
* @param[in] callback_fn Pointer to callback function
* @param[in] user_data Any user data that needs to be passed to the
* callback function
* @param[out] cb_handle Pointer to a \ref libx52_hotplug_callback_handle *
* This will be used by the application to deregister
* the callback if necessary. This may be NULL, in
* which case, the callback handle is not saved.
*
* @returns \ref libx52_error_code indicating status
*
* @par Example
* @code
* libx52_hotplug_service *svc;
* libx52_hotplug_callback_handle *hdl;
* ...
* rc = libx52_hotplug_register_callback(svc, hotplug_cb, context, &hdl);
* if (rc != LIBX52_SUCCESS) {
* return rc;
* }
* @endcode
*/
int libx52_hotplug_register_callback(libx52_hotplug_service *svc,
libx52_hotplug_fn callback_fn,
void *user_data,
libx52_hotplug_callback_handle **cb_handle);
/**
* @brief Deregister a callback function
*
* This function deregisters a previously registered callback function when
* a supported joystick is inserted or removed.
*
* @param[in] hdl Pointer to the hotplug callback handle
*
* @returns \ref libx52_error_code indicating status
*/
int libx52_hotplug_deregister_callback(libx52_hotplug_callback_handle *hdl);
/** @} */
/**
* @defgroup libx52mfdled MFD & LED control
* @defgroup mfdled MFD & LED control
* @{
*/
@ -633,8 +510,6 @@ int libx52_vendor_command(libx52_device *x52, uint16_t index, uint16_t value);
*/
const char * libx52_strerror(libx52_error_code error);
/** @} */
#ifdef __cplusplus
}
#endif

View File

@ -15,7 +15,6 @@
#include "libx52.h"
#include "x52_commands.h"
#include "x52_common.h"
#include "x52_hotplug.h"
#include "gettext.h"
#define VENDOR_SAITEK 0x06a3
@ -38,12 +37,10 @@ static int libx52_check_product(uint16_t idVendor, uint16_t idProduct)
return 0;
}
/* Set flags according to the device model */
static void libx52_set_dev_flags(libx52_device *dev, uint16_t idProduct)
/* Check if the attached device is an X52 Pro */
static int libx52_device_is_x52pro(uint16_t idProduct)
{
if (idProduct == X52_PROD_X52PRO) {
set_bit(&(dev->flags), X52_FLAG_IS_PRO);
}
return (idProduct == X52_PROD_X52PRO);
}
int libx52_init(libx52_device **dev)
@ -97,7 +94,9 @@ int libx52_init(libx52_device **dev)
x52_dev->hdl = hdl;
libx52_set_dev_flags(x52_dev, desc.idProduct);
if (libx52_device_is_x52pro(desc.idProduct)) {
set_bit(&(x52_dev->flags), X52_FLAG_IS_PRO);
}
break;
}
}
@ -129,234 +128,3 @@ void libx52_exit(libx52_device *dev)
free(dev);
}
static int _hotplug_handler(
libusb_context *ctx,
libusb_device *dev,
libusb_hotplug_event event,
void *user_data
) {
libx52_hotplug_service *svc = user_data;
libx52_hotplug_callback_handle *cb;
struct libusb_device_descriptor desc;
size_t i;
int rc;
rc = libusb_get_device_descriptor(dev, &desc);
if (rc != LIBUSB_SUCCESS) {
return rc;
}
if (libx52_check_product(desc.idVendor, desc.idProduct)) {
return LIBX52_ERROR_NOT_SUPPORTED;
}
if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED) {
rc = libusb_open(dev, &(svc->dev->hdl));
if (rc != LIBUSB_SUCCESS) {
return libx52internal_translate_libusb_error(rc);
}
libx52_set_dev_flags(svc->dev, desc.idProduct);
} else if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) {
if (svc->dev->hdl != NULL) {
libusb_close(svc->dev->hdl);
svc->dev->hdl = NULL;
}
} else {
return LIBX52_ERROR_INVALID_PARAM;
}
/* Iterate through registered callbacks */
for (i = 0; i < svc->num_callbacks; i++) {
cb = svc->callbacks[i];
if (cb->callback != NULL) {
(cb->callback)(event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED,
cb->user_data, svc->dev);
}
}
return 0;
}
int libx52_hotplug_init(libx52_hotplug_service **service)
{
libx52_hotplug_service *svc;
int rc = LIBX52_SUCCESS;
/* Make sure that we have a valid return pointer */
if (service == NULL) {
return LIBX52_ERROR_INVALID_PARAM;
}
/* Allocate memory for the library's data structures */
svc = calloc(1, sizeof(*svc));
if (svc == NULL) {
rc = LIBX52_ERROR_OUT_OF_MEMORY;
goto err_recovery;
}
/* Allocate initial buffer for callbacks list */
svc->callbacks = calloc(DEFAULT_NUM_CALLBACKS, sizeof(*(svc->callbacks)));
if (svc->callbacks == NULL) {
rc = LIBX52_ERROR_OUT_OF_MEMORY;
goto err_recovery;
}
svc->num_callbacks = DEFAULT_NUM_CALLBACKS;
svc->dev = calloc(1, sizeof(*(svc->dev)));
if (svc->dev == NULL) {
rc = LIBX52_ERROR_OUT_OF_MEMORY;
goto err_recovery;
}
/* Initialize libusb */
rc = libusb_init(&(svc->dev->ctx));
if (rc != LIBUSB_SUCCESS) {
rc = LIBX52_ERROR_INIT_FAILURE;
goto err_recovery;
}
#if defined(LIBUSB_API_VERSION) && (LIBUSB_API_VERSION >= 0x01000106)
/*
* Use the libusb_set_option flag instead of libusb_set_debug. This
* was introduced in libusb 1.0.22
*/
libusb_set_option(svc->dev->ctx, LIBUSB_OPTION_LOG_LEVEL,
LIBUSB_LOG_LEVEL_WARNING);
#else
libusb_set_debug(svc->dev->ctx, LIBUSB_LOG_LEVEL_WARNING);
#endif
/* Register with the libusb hotplug API */
rc = libusb_hotplug_register_callback(svc->dev->ctx,
LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT,
LIBUSB_HOTPLUG_ENUMERATE, VENDOR_SAITEK,
LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY,
_hotplug_handler, svc, &(svc->cb_handle));
if (rc != LIBUSB_SUCCESS) {
rc = LIBX52_ERROR_INIT_FAILURE;
libusb_exit(svc->dev->ctx);
goto err_recovery;
}
*service = svc;
return rc;
err_recovery:
if (svc->dev != NULL) {
free(svc->dev);
}
if (svc->callbacks != NULL) {
free(svc->callbacks);
}
free(svc);
return rc;
}
void libx52_hotplug_exit(libx52_hotplug_service *svc) {
if (svc == NULL || svc->dev == NULL || svc->callbacks == NULL) {
return;
}
/* Deregister the callback handle */
libusb_hotplug_deregister_callback(svc->dev->ctx, svc->cb_handle);
/* Clean up registered callback handlers */
for (size_t i = 0; i < svc->num_callbacks; i++) {
free(svc->callbacks[i]);
}
free(svc->callbacks);
if (svc->dev->hdl != NULL) {
libusb_close(svc->dev->hdl);
}
libusb_exit(svc->dev->ctx);
/* Clear the device area and free it */
memset(svc->dev, 0, sizeof(*svc->dev));
free(svc->dev);
/* Clear the service pointer before freeing it */
memset(svc, 0, sizeof(*svc));
free(svc);
}
int libx52_hotplug_register_callback(libx52_hotplug_service *svc,
libx52_hotplug_fn callback_fn,
void *user_data,
libx52_hotplug_callback_handle **cb_handle)
{
libx52_hotplug_callback_handle *hdl;
libx52_hotplug_callback_handle **callbacks;
size_t i;
if (svc == NULL || callback_fn == NULL) {
return LIBX52_ERROR_INVALID_PARAM;
}
/* Find an empty slot */
for (i = 0; i < svc->num_callbacks; i++) {
if (svc->callbacks[i] == NULL) {
break;
}
}
if (i == svc->num_callbacks) {
/* Existing callbacks array is full, create a new one */
callbacks = calloc(svc->num_callbacks + DEFAULT_NUM_CALLBACKS, sizeof(*callbacks));
if (callbacks == NULL) {
return LIBX52_ERROR_OUT_OF_MEMORY;
}
/* Copy the callbacks array to the new one */
for (i = 0; i < svc->num_callbacks; i++) {
callbacks[i] = svc->callbacks[i];
}
/* Free the old array, and adjust the link to the new larger array */
svc->num_callbacks += DEFAULT_NUM_CALLBACKS;
free(svc->callbacks);
svc->callbacks = callbacks;
}
hdl = calloc(1, sizeof(*hdl));
if (hdl == NULL) {
return LIBX52_ERROR_OUT_OF_MEMORY;
}
hdl->svc = svc;
hdl->id = i;
hdl->callback = callback_fn;
hdl->user_data = user_data;
/* Insert the node into the callbacks list */
svc->callbacks[i] = hdl;
if (cb_handle != NULL) {
*cb_handle = hdl;
}
return LIBX52_SUCCESS;
}
int libx52_hotplug_deregister_callback(libx52_hotplug_callback_handle *hdl)
{
libx52_hotplug_service *svc;
if (hdl == NULL || hdl->svc == NULL) {
return LIBX52_ERROR_INVALID_PARAM;
}
svc = hdl->svc;
if (hdl->id >= svc->num_callbacks) {
return LIBX52_ERROR_INVALID_PARAM;
}
/* Free the handle */
svc->callbacks[hdl->id] = NULL;
free(hdl);
return LIBX52_SUCCESS;
}

View File

@ -1,40 +0,0 @@
/*
* Saitex X52 Pro MFD & LED driver
*
* Copyright (C) 2012-2020 Nirenjan Krishnan (nirenjan@nirenjan.org)
*
* SPDX-License-Identifier: GPL-2.0-only WITH Classpath-exception-2.0
*/
#ifndef X52_HOTPLUG_H
#define X52_HOTPLUG_H
#include <stdint.h>
#include <libusb.h>
#include "libx52.h"
/*
* Structure for callback handle. This is a node in a doubly linked list,
* which is iterated over by the libusb callback handler.
*/
struct libx52_hotplug_callback_handle {
libx52_hotplug_service *svc;
libx52_hotplug_fn callback;
void *user_data;
size_t id;
};
struct libx52_hotplug_service {
libx52_device *dev;
libusb_hotplug_callback_handle cb_handle;
libx52_hotplug_callback_handle **callbacks;
size_t num_callbacks;
};
#define DEFAULT_NUM_CALLBACKS 8
#endif /* !defined X52_HOTPLUG_H */