diff --git a/libusb/hid.c b/libusb/hid.c index 188e536d5..329dc4513 100644 --- a/libusb/hid.c +++ b/libusb/hid.c @@ -37,7 +37,6 @@ #include #include #include -#include #include /* GNU / LibUSB */ @@ -51,66 +50,10 @@ #include "hidapi_libusb.h" -#if defined(__ANDROID__) && __ANDROID_API__ < __ANDROID_API_N__ - -/* Barrier implementation because Android/Bionic don't have pthread_barrier. - This implementation came from Brent Priddy and was posted on - StackOverflow. It is used with his permission. */ -typedef int pthread_barrierattr_t; -typedef struct pthread_barrier { - pthread_mutex_t mutex; - pthread_cond_t cond; - int count; - int trip_count; -} pthread_barrier_t; - -static int pthread_barrier_init(pthread_barrier_t *barrier, const pthread_barrierattr_t *attr, unsigned int count) -{ - if(count == 0) { - errno = EINVAL; - return -1; - } - - if(pthread_mutex_init(&barrier->mutex, 0) < 0) { - return -1; - } - if(pthread_cond_init(&barrier->cond, 0) < 0) { - pthread_mutex_destroy(&barrier->mutex); - return -1; - } - barrier->trip_count = count; - barrier->count = 0; - - return 0; -} - -static int pthread_barrier_destroy(pthread_barrier_t *barrier) -{ - pthread_cond_destroy(&barrier->cond); - pthread_mutex_destroy(&barrier->mutex); - return 0; -} - -static int pthread_barrier_wait(pthread_barrier_t *barrier) -{ - pthread_mutex_lock(&barrier->mutex); - ++(barrier->count); - if(barrier->count >= barrier->trip_count) - { - barrier->count = 0; - pthread_cond_broadcast(&barrier->cond); - pthread_mutex_unlock(&barrier->mutex); - return 1; - } - else - { - pthread_cond_wait(&barrier->cond, &(barrier->mutex)); - pthread_mutex_unlock(&barrier->mutex); - return 0; - } -} - +#ifndef HIDAPI_THREAD_MODEL_INCLUDE +#define HIDAPI_THREAD_MODEL_INCLUDE "hidapi_thread_pthread.h" #endif +#include HIDAPI_THREAD_MODEL_INCLUDE #ifdef __cplusplus extern "C" { @@ -168,10 +111,7 @@ struct hid_device_ { 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 */ + hidapi_thread_state thread_state; int shutdown_thread; int transfer_loop_finished; struct libusb_transfer *transfer; @@ -201,9 +141,7 @@ 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); + hidapi_thread_state_init(&dev->thread_state); return dev; } @@ -211,9 +149,7 @@ static hid_device *new_hid_device(void) 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); + hidapi_thread_state_destroy(&dev->thread_state); hid_free_enumeration(dev->device_info); @@ -912,13 +848,13 @@ static void read_callback(struct libusb_transfer *transfer) rpt->len = transfer->actual_length; rpt->next = NULL; - pthread_mutex_lock(&dev->mutex); + hidapi_thread_mutex_lock(&dev->thread_state); /* Attach the new report object to the end of the list. */ if (dev->input_reports == NULL) { /* The list is empty. Put it at the root. */ dev->input_reports = rpt; - pthread_cond_signal(&dev->condition); + hidapi_thread_cond_signal(&dev->thread_state); } else { /* Find the end of the list and attach. */ @@ -937,7 +873,7 @@ static void read_callback(struct libusb_transfer *transfer) return_data(dev, NULL, 0); } } - pthread_mutex_unlock(&dev->mutex); + hidapi_thread_mutex_unlock(&dev->thread_state); } else if (transfer->status == LIBUSB_TRANSFER_CANCELLED) { dev->shutdown_thread = 1; @@ -996,7 +932,7 @@ static void *read_thread(void *param) } /* Notify the main thread that the read thread is up and running. */ - pthread_barrier_wait(&dev->barrier); + hidapi_thread_barrier_wait(&dev->thread_state); /* Handle all the events. */ while (!dev->shutdown_thread) { @@ -1028,15 +964,15 @@ static void *read_thread(void *param) 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); + hidapi_thread_mutex_lock(&dev->thread_state); + hidapi_thread_cond_broadcast(&dev->thread_state); + hidapi_thread_mutex_unlock(&dev->thread_state); /* 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 + cleaned up after the call to hidapi_thread_join() (in hid_close()), but since hid_close() calls libusb_cancel_transfer(), on these objects, they can not be cleaned up here. */ @@ -1128,10 +1064,10 @@ static int hidapi_initialize_device(hid_device *dev, int config_number, const st } } - pthread_create(&dev->thread, NULL, read_thread, dev); + hidapi_thread_create(&dev->thread_state, read_thread, dev); /* Wait here for the read thread to be initialized. */ - pthread_barrier_wait(&dev->barrier); + hidapi_thread_barrier_wait(&dev->thread_state); return 1; } @@ -1347,7 +1283,7 @@ static int return_data(hid_device *dev, unsigned char *data, size_t length) static void cleanup_mutex(void *param) { hid_device *dev = param; - pthread_mutex_unlock(&dev->mutex); + hidapi_thread_mutex_unlock(&dev->thread_state); } @@ -1363,8 +1299,8 @@ int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t /* error: variable ‘bytes_read’ might be clobbered by ‘longjmp’ or ‘vfork’ [-Werror=clobbered] */ int bytes_read; /* = -1; */ - pthread_mutex_lock(&dev->mutex); - pthread_cleanup_push(&cleanup_mutex, dev); + hidapi_thread_mutex_lock(&dev->thread_state); + hidapi_thread_cleanup_push(cleanup_mutex, dev); bytes_read = -1; @@ -1385,7 +1321,7 @@ int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t if (milliseconds == -1) { /* Blocking */ while (!dev->input_reports && !dev->shutdown_thread) { - pthread_cond_wait(&dev->condition, &dev->mutex); + hidapi_thread_cond_wait(&dev->thread_state); } if (dev->input_reports) { bytes_read = return_data(dev, data, length); @@ -1394,17 +1330,12 @@ int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t 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; - } + hidapi_timespec ts; + hidapi_thread_gettime(&ts); + hidapi_thread_addtime(&ts, milliseconds); while (!dev->input_reports && !dev->shutdown_thread) { - res = pthread_cond_timedwait(&dev->condition, &dev->mutex, &ts); + res = hidapi_thread_cond_timedwait(&dev->thread_state, &ts); if (res == 0) { if (dev->input_reports) { bytes_read = return_data(dev, data, length); @@ -1415,7 +1346,7 @@ int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t or the read thread was shutdown. Run the loop again (ie: don't break). */ } - else if (res == ETIMEDOUT) { + else if (res == HIDAPI_THREAD_TIMED_OUT) { /* Timed out. */ bytes_read = 0; break; @@ -1433,8 +1364,8 @@ int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t } ret: - pthread_mutex_unlock(&dev->mutex); - pthread_cleanup_pop(0); + hidapi_thread_mutex_unlock(&dev->thread_state); + hidapi_thread_cleanup_pop(0); return bytes_read; } @@ -1552,7 +1483,7 @@ void HID_API_EXPORT hid_close(hid_device *dev) libusb_cancel_transfer(dev->transfer); /* Wait for read_thread() to end. */ - pthread_join(dev->thread, NULL); + hidapi_thread_join(&dev->thread_state); /* Clean up the Transfer objects allocated in read_thread(). */ free(dev->transfer->buffer); @@ -1575,11 +1506,11 @@ void HID_API_EXPORT hid_close(hid_device *dev) libusb_close(dev->device_handle); /* Clear out the queue of received reports. */ - pthread_mutex_lock(&dev->mutex); + hidapi_thread_mutex_lock(&dev->thread_state); while (dev->input_reports) { return_data(dev, NULL, 0); } - pthread_mutex_unlock(&dev->mutex); + hidapi_thread_mutex_unlock(&dev->thread_state); free_hid_device(dev); } diff --git a/libusb/hidapi_thread_pthread.h b/libusb/hidapi_thread_pthread.h new file mode 100644 index 000000000..0abe733e5 --- /dev/null +++ b/libusb/hidapi_thread_pthread.h @@ -0,0 +1,174 @@ +/******************************************************* + HIDAPI - Multi-Platform library for + communication with HID devices. + + Alan Ott + Signal 11 Software + + libusb/hidapi Team + + Sam Lantinga + + Copyright 2023, All Rights Reserved. + + At the discretion of the user of this library, + this software may be licensed under the terms of the + GNU General Public License v3, a BSD-Style license, or the + original HIDAPI license as outlined in the LICENSE.txt, + LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt + files located at the root of the source distribution. + These files may also be found in the public source + code repository located at: + https://github.com/libusb/hidapi . +********************************************************/ + +#include + +#if defined(__ANDROID__) && __ANDROID_API__ < __ANDROID_API_N__ + +/* Barrier implementation because Android/Bionic don't have pthread_barrier. + This implementation came from Brent Priddy and was posted on + StackOverflow. It is used with his permission. */ +typedef int pthread_barrierattr_t; +typedef struct pthread_barrier { + pthread_mutex_t mutex; + pthread_cond_t cond; + int count; + int trip_count; +} pthread_barrier_t; + +static int pthread_barrier_init(pthread_barrier_t *barrier, const pthread_barrierattr_t *attr, unsigned int count) +{ + if(count == 0) { + errno = EINVAL; + return -1; + } + + if(pthread_mutex_init(&barrier->mutex, 0) < 0) { + return -1; + } + if(pthread_cond_init(&barrier->cond, 0) < 0) { + pthread_mutex_destroy(&barrier->mutex); + return -1; + } + barrier->trip_count = count; + barrier->count = 0; + + return 0; +} + +static int pthread_barrier_destroy(pthread_barrier_t *barrier) +{ + pthread_cond_destroy(&barrier->cond); + pthread_mutex_destroy(&barrier->mutex); + return 0; +} + +static int pthread_barrier_wait(pthread_barrier_t *barrier) +{ + pthread_mutex_lock(&barrier->mutex); + ++(barrier->count); + if(barrier->count >= barrier->trip_count) { + barrier->count = 0; + pthread_cond_broadcast(&barrier->cond); + pthread_mutex_unlock(&barrier->mutex); + return 1; + } + else { + pthread_cond_wait(&barrier->cond, &(barrier->mutex)); + pthread_mutex_unlock(&barrier->mutex); + return 0; + } +} + +#endif + +#define HIDAPI_THREAD_TIMED_OUT ETIMEDOUT + +typedef struct timespec hidapi_timespec; + +typedef struct +{ + pthread_t thread; + pthread_mutex_t mutex; /* Protects input_reports */ + pthread_cond_t condition; + pthread_barrier_t barrier; /* Ensures correct startup sequence */ + +} hidapi_thread_state; + +static void hidapi_thread_state_init(hidapi_thread_state *state) +{ + pthread_mutex_init(&state->mutex, NULL); + pthread_cond_init(&state->condition, NULL); + pthread_barrier_init(&state->barrier, NULL, 2); +} + +static void hidapi_thread_state_destroy(hidapi_thread_state *state) +{ + pthread_barrier_destroy(&state->barrier); + pthread_cond_destroy(&state->condition); + pthread_mutex_destroy(&state->mutex); +} + +#define hidapi_thread_cleanup_push pthread_cleanup_push +#define hidapi_thread_cleanup_pop pthread_cleanup_pop + +static void hidapi_thread_mutex_lock(hidapi_thread_state *state) +{ + pthread_mutex_lock(&state->mutex); +} + +static void hidapi_thread_mutex_unlock(hidapi_thread_state *state) +{ + pthread_mutex_unlock(&state->mutex); +} + +static void hidapi_thread_cond_wait(hidapi_thread_state *state) +{ + pthread_cond_wait(&state->condition, &state->mutex); +} + +static int hidapi_thread_cond_timedwait(hidapi_thread_state *state, hidapi_timespec *ts) +{ + return pthread_cond_timedwait(&state->condition, &state->mutex, ts); +} + +static void hidapi_thread_cond_signal(hidapi_thread_state *state) +{ + pthread_cond_signal(&state->condition); +} + +static void hidapi_thread_cond_broadcast(hidapi_thread_state *state) +{ + pthread_cond_broadcast(&state->condition); +} + +static void hidapi_thread_barrier_wait(hidapi_thread_state *state) +{ + pthread_barrier_wait(&state->barrier); +} + +static void hidapi_thread_create(hidapi_thread_state *state, void *(*func)(void*), void *func_arg) +{ + pthread_create(&state->thread, NULL, func, func_arg); +} + +static void hidapi_thread_join(hidapi_thread_state *state) +{ + pthread_join(state->thread, NULL); +} + +static void hidapi_thread_gettime(hidapi_timespec *ts) +{ + clock_gettime(CLOCK_REALTIME, ts); +} + +static void hidapi_thread_addtime(hidapi_timespec *ts, int milliseconds) +{ + ts->tv_sec += milliseconds / 1000; + ts->tv_nsec += (milliseconds % 1000) * 1000000; + if (ts->tv_nsec >= 1000000000L) { + ts->tv_sec++; + ts->tv_nsec -= 1000000000L; + } +}