Explorar el Código

Added hotplug detection when using libusb

Also switched to a single thread for libusb read operations instead of one thread per device
Sam Lantinga hace 1 día
padre
commit
1475239328

+ 1 - 1
cmake/sdlchecks.cmake

@@ -1272,7 +1272,7 @@ macro(CheckHIDAPI)
           #include <stddef.h>
           #include <libusb.h>
           int main(int argc, char **argv) {
-            libusb_close(NULL);
+            libusb_interrupt_event_handler(NULL);
             return 0;
           }" HAVE_LIBUSB_H)
         cmake_pop_check_state()

+ 4 - 0
src/hidapi/SDL_hidapi.c

@@ -734,6 +734,10 @@ static SDL_LibUSBContext *libusb_ctx;
 #define libusb_bulk_transfer                libusb_ctx->bulk_transfer
 #define libusb_handle_events                libusb_ctx->handle_events
 #define libusb_handle_events_completed      libusb_ctx->handle_events_completed
+#define libusb_interrupt_event_handler      libusb_ctx->interrupt_event_handler
+#define libusb_has_capability               libusb_ctx->has_capability
+#define libusb_hotplug_register_callback    libusb_ctx->hotplug_register_callback
+#define libusb_hotplug_deregister_callback  libusb_ctx->hotplug_deregister_callback
 #define libusb_error_name                   libusb_ctx->error_name
 
 struct LIBUSB_hid_device_;

+ 109 - 16
src/hidapi/libusb/hid.c

@@ -600,6 +600,66 @@ HID_API_EXPORT const char* HID_API_CALL hid_version_str(void)
 	return HID_API_VERSION_STR;
 }
 
+#ifdef HIDAPI_USING_SDL_RUNTIME
+static libusb_hotplug_callback_handle hotplug_callback_handle;
+static int shutdown_event_thread;
+static hidapi_thread_state event_thread_state;
+
+static int hotplug_callback(struct libusb_context *ctx, struct libusb_device *dev, libusb_hotplug_event event, void *user_data)
+{
+	switch (event) {
+	case LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED:
+		++SDL_HIDAPI_discovery.m_unDeviceChangeCounter;
+		break;
+	case LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT:
+		++SDL_HIDAPI_discovery.m_unDeviceChangeCounter;
+		break;
+	default:
+		break;
+	}
+	return 0;
+}
+
+
+static void *event_thread(void *param)
+{
+	while (!shutdown_event_thread) {
+		int res = libusb_handle_events(usb_context);
+		if (res < 0) {
+			/* There was an error. */
+			LOG("event_thread(): (%d) %s\n", res, libusb_error_name(res));
+		}
+	}
+	return NULL;
+}
+
+static void start_event_thread()
+{
+	if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
+		int res = libusb_hotplug_register_callback(usb_context, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT, 0, LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY, hotplug_callback, NULL, &hotplug_callback_handle);
+		if (res < 0) {
+			LOG("Couldn't register hotplug: (%d) %s\n", res, libusb_error_name(res));
+		}
+	}
+
+	hidapi_thread_create(&event_thread_state, event_thread, NULL);
+}
+
+static void stop_event_thread()
+{
+	shutdown_event_thread = 1;
+	libusb_interrupt_event_handler(usb_context);
+	hidapi_thread_join(&event_thread_state);
+	shutdown_event_thread = 0;
+
+	if (hotplug_callback_handle) {
+		libusb_hotplug_deregister_callback(usb_context, hotplug_callback_handle);
+		hotplug_callback_handle = 0;
+	}
+}
+#endif /* HIDAPI_USING_SDL_RUNTIME */
+
+
 int HID_API_EXPORT hid_init(void)
 {
 	if (!usb_context) {
@@ -613,6 +673,10 @@ int HID_API_EXPORT hid_init(void)
 		locale = setlocale(LC_CTYPE, NULL);
 		if (!locale)
 			(void) setlocale(LC_CTYPE, "");
+
+#ifdef HIDAPI_USING_SDL_RUNTIME
+		start_event_thread();
+#endif
 	}
 
 	return 0;
@@ -622,6 +686,10 @@ int HID_API_EXPORT hid_exit(void)
 {
 	usb_string_cache_destroy();
 
+#ifdef HIDAPI_USING_SDL_RUNTIME
+	stop_event_thread();
+#endif
+
 	if (usb_context) {
 		libusb_exit(usb_context);
 		usb_context = NULL;
@@ -1186,10 +1254,9 @@ static void LIBUSB_CALL read_callback(struct libusb_transfer *transfer)
 }
 
 
-static void *read_thread(void *param)
+static void start_read_operations(hid_device *dev)
 {
 	int res;
-	hid_device *dev = param;
 	uint8_t *buf;
 	const size_t length = dev->input_ep_max_packet_size;
 
@@ -1209,10 +1276,37 @@ static void *read_thread(void *param)
 	   from inside read_callback() */
 	res = libusb_submit_transfer(dev->transfer);
 	if(res < 0) {
-                LOG("libusb_submit_transfer failed: %d %s. Stopping read_thread from running\n", res, libusb_error_name(res));
-                dev->shutdown_thread = 1;
-                dev->transfer_loop_finished = 1;
+		LOG("libusb_submit_transfer failed: %d %s. Stopping read_thread from running\n", res, libusb_error_name(res));
+		dev->shutdown_thread = 1;
+		dev->transfer_loop_finished = 1;
 	}
+}
+
+
+static void stop_read_operations(hid_device *dev)
+{
+	while (!dev->transfer_loop_finished)
+		libusb_handle_events_completed(usb_context, &dev->transfer_loop_finished);
+
+	/* Now that the read operations are 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. */
+	hidapi_thread_mutex_lock(&dev->thread_state);
+	hidapi_thread_cond_broadcast(&dev->thread_state);
+	hidapi_thread_mutex_unlock(&dev->thread_state);
+}
+
+
+#ifdef HIDAPI_USING_SDL_RUNTIME
+/* We have a separate thread handling all the events */
+#else
+static void *read_thread(void *param)
+{
+	hid_device *dev = param;
+
+	start_read_operations(dev);
 
 	/* Notify the main thread that the read thread is up and running. */
 	hidapi_thread_barrier_wait(&dev->thread_state);
@@ -1239,17 +1333,7 @@ static void *read_thread(void *param)
 	   if no transfers are pending, but that's OK. */
 	libusb_cancel_transfer(dev->transfer);
 
-	while (!dev->transfer_loop_finished)
-		libusb_handle_events_completed(usb_context, &dev->transfer_loop_finished);
-
-	/* 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. */
-	hidapi_thread_mutex_lock(&dev->thread_state);
-	hidapi_thread_cond_broadcast(&dev->thread_state);
-	hidapi_thread_mutex_unlock(&dev->thread_state);
+	stop_read_operations(dev);
 
 	/* The dev->transfer->buffer and dev->transfer objects are cleaned up
 	   in hid_close(). They are not cleaned up here because this thread
@@ -1261,6 +1345,7 @@ static void *read_thread(void *param)
 
 	return NULL;
 }
+#endif /* HIDAPI_USING_SDL_RUNTIME */
 
 static void init_xbox360(libusb_device_handle *device_handle, unsigned short idVendor, unsigned short idProduct, const struct libusb_config_descriptor *conf_desc)
 {
@@ -1441,10 +1526,14 @@ static int hidapi_initialize_device(hid_device *dev, const struct libusb_interfa
 
 	calculate_device_quirks(dev, desc.idVendor, desc.idProduct);
 
+#ifdef HIDAPI_USING_SDL_RUNTIME
+	start_read_operations(dev);
+#else
 	hidapi_thread_create(&dev->thread_state, read_thread, dev);
 
 	/* Wait here for the read thread to be initialized. */
 	hidapi_thread_barrier_wait(&dev->thread_state);
+#endif
 	return 1;
 }
 
@@ -1868,8 +1957,12 @@ void HID_API_EXPORT hid_close(hid_device *dev)
 	dev->shutdown_thread = 1;
 	libusb_cancel_transfer(dev->transfer);
 
+#ifdef HIDAPI_USING_SDL_RUNTIME
+	stop_read_operations(dev);
+#else
 	/* Wait for read_thread() to end. */
 	hidapi_thread_join(&dev->thread_state);
+#endif
 
 	/* Clean up the Transfer objects allocated in read_thread(). */
 	free(dev->transfer->buffer);

+ 20 - 4
src/hidapi/libusb/hidapi_thread_sdl.h

@@ -53,10 +53,17 @@ static int SDL_CreateThreadBarrier(SDL_ThreadBarrier *barrier, Uint32 count)
 
 static void SDL_DestroyThreadBarrier(SDL_ThreadBarrier *barrier)
 {
-    SDL_DestroyCondition(barrier->cond);
-    SDL_DestroyMutex(barrier->mutex);
+    if (barrier->cond) {
+        SDL_DestroyCondition(barrier->cond);
+        barrier->cond = NULL;
+    }
+    if (barrier->mutex) {
+        SDL_DestroyMutex(barrier->mutex);
+        barrier->mutex = NULL;
+    }
 }
 
+#if 0 // Not used
 static int SDL_WaitThreadBarrier(SDL_ThreadBarrier *barrier)
 {
     SDL_LockMutex(barrier->mutex);
@@ -71,6 +78,7 @@ static int SDL_WaitThreadBarrier(SDL_ThreadBarrier *barrier)
     SDL_UnlockMutex(barrier->mutex);
     return 0;
 }
+#endif
 
 #include "../../thread/SDL_systhread.h"
 
@@ -97,8 +105,14 @@ static void hidapi_thread_state_init(hidapi_thread_state *state)
 static void hidapi_thread_state_destroy(hidapi_thread_state *state)
 {
     SDL_DestroyThreadBarrier(&state->barrier);
-    SDL_DestroyCondition(state->condition);
-    SDL_DestroyMutex(state->mutex);
+    if (state->condition) {
+        SDL_DestroyCondition(state->condition);
+        state->condition = NULL;
+    }
+    if (state->mutex) {
+        SDL_DestroyMutex(state->mutex);
+        state->mutex = NULL;
+    }
 }
 
 static void hidapi_thread_cleanup_push(void (*routine)(void *), void *arg)
@@ -153,10 +167,12 @@ static void hidapi_thread_cond_broadcast(hidapi_thread_state *state)
     SDL_BroadcastCondition(state->condition);
 }
 
+#if 0 // Not used
 static void hidapi_thread_barrier_wait(hidapi_thread_state *state)
 {
     SDL_WaitThreadBarrier(&state->barrier);
 }
+#endif
 
 typedef struct
 {

+ 4 - 0
src/misc/SDL_libusb.c

@@ -90,6 +90,10 @@ bool SDL_InitLibUSB(SDL_LibUSBContext **ctx)
             LOAD_LIBUSB_SYMBOL(int (LIBUSB_CALL *)(libusb_device_handle *, unsigned char, unsigned char *, int, int *, unsigned int), bulk_transfer)
             LOAD_LIBUSB_SYMBOL(int (LIBUSB_CALL *)(libusb_context *), handle_events)
             LOAD_LIBUSB_SYMBOL(int (LIBUSB_CALL *)(libusb_context *, int *), handle_events_completed)
+            LOAD_LIBUSB_SYMBOL(void (LIBUSB_CALL *)(libusb_context *ctx), interrupt_event_handler)
+            LOAD_LIBUSB_SYMBOL(int (LIBUSB_CALL *)(uint32_t), has_capability)
+            LOAD_LIBUSB_SYMBOL(int (LIBUSB_CALL *)(libusb_context *, int, int, int, int, int, libusb_hotplug_callback_fn, void *, libusb_hotplug_callback_handle *), hotplug_register_callback)
+            LOAD_LIBUSB_SYMBOL(void (LIBUSB_CALL *)(libusb_context *, libusb_hotplug_callback_handle), hotplug_deregister_callback)
             LOAD_LIBUSB_SYMBOL(const char * (LIBUSB_CALL *)(int), error_name)
 #undef LOAD_LIBUSB_SYMBOL
         }

+ 4 - 0
src/misc/SDL_libusb.h

@@ -91,6 +91,10 @@ typedef struct SDL_LibUSBContext
     );
     int (LIBUSB_CALL *handle_events)(libusb_context *ctx);
     int (LIBUSB_CALL *handle_events_completed)(libusb_context *ctx, int *completed);
+    void (LIBUSB_CALL *interrupt_event_handler)(libusb_context *ctx);
+    int (LIBUSB_CALL *has_capability)(uint32_t capability);
+    int (LIBUSB_CALL *hotplug_register_callback)(libusb_context *ctx, int events, int flags, int vendor_id, int product_id, int dev_class, libusb_hotplug_callback_fn cb_fn, void *user_data, libusb_hotplug_callback_handle *callback_handle);
+    void (LIBUSB_CALL *hotplug_deregister_callback)(libusb_context *ctx, libusb_hotplug_callback_handle callback_handle);
     const char * (LIBUSB_CALL *error_name)(int errcode);
 /* *INDENT-ON* */ // clang-format on