From 9477791bfe796c1c5d11b02cddc7ca7ed195742c Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Thu, 20 Jul 2017 11:35:27 +0200 Subject: [PATCH] Use a reference counted USB session Replace the global USB library context with a reference counted session to manage the lifetime of the USB library context. For the libusb based implementation, this is actually a much better match for the underlying libusb api, and allows to eliminate the global state. For the hidapi based implementation, the global state is unavoidable because the hidapi doesn't support multiple sessions. Therefore we use a singleton session. --- src/usbhid.c | 160 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 118 insertions(+), 42 deletions(-) diff --git a/src/usbhid.c b/src/usbhid.c index 0d3bb8a..0606f12 100644 --- a/src/usbhid.c +++ b/src/usbhid.c @@ -68,8 +68,16 @@ typedef pthread_mutex_t dc_mutex_t; #define ISINSTANCE(device) dc_iostream_isinstance((device), &dc_usbhid_vtable) +typedef struct dc_usbhid_session_t { + size_t refcount; +#if defined(USE_LIBUSB) + libusb_context *handle; +#endif +} dc_usbhid_session_t; + struct dc_usbhid_device_t { unsigned short vid, pid; + dc_usbhid_session_t *session; }; #ifdef USBHID @@ -84,6 +92,7 @@ static dc_status_t dc_usbhid_close (dc_iostream_t *iostream); typedef struct dc_usbhid_iterator_t { dc_iterator_t base; dc_filter_t filter; + dc_usbhid_session_t *session; #if defined(USE_LIBUSB) struct libusb_device **devices; size_t count; @@ -97,6 +106,7 @@ typedef struct dc_usbhid_t { /* Base class. */ dc_iostream_t base; /* Internal state. */ + dc_usbhid_session_t *session; #if defined(USE_LIBUSB) libusb_device_handle *handle; int interface; @@ -133,10 +143,9 @@ static const dc_iostream_vtable_t dc_usbhid_vtable = { dc_usbhid_close, /* close */ }; +#ifdef USE_HIDAPI static dc_mutex_t g_usbhid_mutex = DC_MUTEX_INIT; -static size_t g_usbhid_refcount = 0; -#ifdef USE_LIBUSB -static libusb_context *g_usbhid_ctx = NULL; +static dc_usbhid_session_t *g_usbhid_session = NULL; #endif #if defined(USE_LIBUSB) @@ -162,6 +171,7 @@ syserror(int errcode) } #endif +#ifdef USE_HIDAPI static void dc_mutex_lock (dc_mutex_t *mutex) { @@ -183,55 +193,113 @@ dc_mutex_unlock (dc_mutex_t *mutex) pthread_mutex_unlock (mutex); #endif } +#endif static dc_status_t -dc_usbhid_init (dc_context_t *context) +dc_usbhid_session_new (dc_usbhid_session_t **out, dc_context_t *context) { dc_status_t status = DC_STATUS_SUCCESS; + dc_usbhid_session_t *session = NULL; + if (out == NULL) + return DC_STATUS_INVALIDARGS; + +#ifdef USE_HIDAPI dc_mutex_lock (&g_usbhid_mutex); - if (g_usbhid_refcount == 0) { -#if defined(USE_LIBUSB) - int rc = libusb_init (&g_usbhid_ctx); - if (rc != LIBUSB_SUCCESS) { - ERROR (context, "Failed to initialize usb support (%s).", - libusb_error_name (rc)); - status = syserror (rc); - goto error; - } -#elif defined(USE_HIDAPI) - int rc = hid_init(); - if (rc < 0) { - ERROR (context, "Failed to initialize usb support."); - status = DC_STATUS_IO; - goto error; - } + if (g_usbhid_session) { + g_usbhid_session->refcount++; + *out = g_usbhid_session; + dc_mutex_unlock (&g_usbhid_mutex); + return DC_STATUS_SUCCESS; + } #endif + + session = (dc_usbhid_session_t *) malloc (sizeof(dc_usbhid_session_t)); + if (session == NULL) { + ERROR (context, "Failed to allocate memory."); + status = DC_STATUS_NOMEMORY; + goto error_unlock; } - g_usbhid_refcount++; + session->refcount = 1; + +#if defined(USE_LIBUSB) + int rc = libusb_init (&session->handle); + if (rc != LIBUSB_SUCCESS) { + ERROR (context, "Failed to initialize usb support (%s).", + libusb_error_name (rc)); + status = syserror (rc); + goto error_free; + } +#elif defined(USE_HIDAPI) + int rc = hid_init(); + if (rc < 0) { + ERROR (context, "Failed to initialize usb support."); + status = DC_STATUS_IO; + goto error_free; + } + + g_usbhid_session = session; -error: dc_mutex_unlock (&g_usbhid_mutex); +#endif + + *out = session; + + return status; + +error_free: + free (session); +error_unlock: +#ifdef USE_HIDAPI + dc_mutex_unlock (&g_usbhid_mutex); +#endif return status; } -static dc_status_t -dc_usbhid_exit (void) +static dc_usbhid_session_t * +dc_usbhid_session_ref (dc_usbhid_session_t *session) { - dc_mutex_lock (&g_usbhid_mutex); + if (session == NULL) + return NULL; - if (--g_usbhid_refcount == 0) { +#ifdef USE_HIDAPI + dc_mutex_lock (&g_usbhid_mutex); +#endif + + session->refcount++; + +#ifdef USE_HIDAPI + dc_mutex_unlock (&g_usbhid_mutex); +#endif + + return session; +} + +static dc_status_t +dc_usbhid_session_unref (dc_usbhid_session_t *session) +{ + if (session == NULL) + return DC_STATUS_SUCCESS; + +#ifdef USE_HIDAPI + dc_mutex_lock (&g_usbhid_mutex); +#endif + + if (--session->refcount == 0) { #if defined(USE_LIBUSB) - libusb_exit (g_usbhid_ctx); - g_usbhid_ctx = NULL; + libusb_exit (session->handle); #elif defined(USE_HIDAPI) hid_exit (); + g_usbhid_session = NULL; #endif + free (session); } +#ifdef USE_HIDAPI dc_mutex_unlock (&g_usbhid_mutex); +#endif return DC_STATUS_SUCCESS; } @@ -258,6 +326,12 @@ dc_usbhid_device_get_pid (dc_usbhid_device_t *device) void dc_usbhid_device_free(dc_usbhid_device_t *device) { + if (device == NULL) + return; + +#ifdef USBHID + dc_usbhid_session_unref (device->session); +#endif free (device); } @@ -278,7 +352,7 @@ dc_usbhid_iterator_new (dc_iterator_t **out, dc_context_t *context, dc_descripto } // Initialize the usb library. - status = dc_usbhid_init (context); + status = dc_usbhid_session_new (&iterator->session, context); if (status != DC_STATUS_SUCCESS) { goto error_free; } @@ -286,12 +360,12 @@ dc_usbhid_iterator_new (dc_iterator_t **out, dc_context_t *context, dc_descripto #if defined(USE_LIBUSB) // Enumerate the USB devices. struct libusb_device **devices = NULL; - ssize_t ndevices = libusb_get_device_list (g_usbhid_ctx, &devices); + ssize_t ndevices = libusb_get_device_list (iterator->session->handle, &devices); if (ndevices < 0) { ERROR (context, "Failed to enumerate the usb devices (%s).", libusb_error_name (ndevices)); status = syserror (ndevices); - goto error_usb_exit; + goto error_session_unref; } iterator->devices = devices; @@ -301,7 +375,7 @@ dc_usbhid_iterator_new (dc_iterator_t **out, dc_context_t *context, dc_descripto struct hid_device_info *devices = hid_enumerate(0x0, 0x0); if (devices == NULL) { status = DC_STATUS_IO; - goto error_usb_exit; + goto error_session_unref; } iterator->devices = devices; @@ -313,8 +387,8 @@ dc_usbhid_iterator_new (dc_iterator_t **out, dc_context_t *context, dc_descripto return DC_STATUS_SUCCESS; -error_usb_exit: - dc_usbhid_exit (); +error_session_unref: + dc_usbhid_session_unref (iterator->session); error_free: dc_iterator_deallocate ((dc_iterator_t *) iterator); return status; @@ -407,6 +481,7 @@ dc_usbhid_iterator_next (dc_iterator_t *abstract, void *out) return DC_STATUS_NOMEMORY; } + device->session = dc_usbhid_session_ref (iterator->session); device->vid = dev.idVendor; device->pid = dev.idProduct; @@ -432,6 +507,7 @@ dc_usbhid_iterator_next (dc_iterator_t *abstract, void *out) return DC_STATUS_NOMEMORY; } + device->session = dc_usbhid_session_ref (iterator->session); device->vid = current->vendor_id; device->pid = current->product_id; @@ -454,7 +530,7 @@ dc_usbhid_iterator_free (dc_iterator_t *abstract) #elif defined(USE_HIDAPI) hid_free_enumeration (iterator->devices); #endif - dc_usbhid_exit (); + dc_usbhid_session_unref (iterator->session); return DC_STATUS_SUCCESS; } @@ -480,7 +556,7 @@ dc_usbhid_open (dc_iostream_t **out, dc_context_t *context, unsigned int vid, un } // Initialize the usb library. - status = dc_usbhid_init (context); + status = dc_usbhid_session_new (&usbhid->session, context); if (status != DC_STATUS_SUCCESS) { goto error_free; } @@ -491,12 +567,12 @@ dc_usbhid_open (dc_iostream_t **out, dc_context_t *context, unsigned int vid, un int rc = 0; // Enumerate the USB devices. - ssize_t ndevices = libusb_get_device_list (g_usbhid_ctx, &devices); + ssize_t ndevices = libusb_get_device_list (usbhid->session->handle, &devices); if (ndevices < 0) { ERROR (context, "Failed to enumerate the usb devices (%s).", libusb_error_name (ndevices)); status = syserror (ndevices); - goto error_usb_exit; + goto error_session_unref; } // Find the first device matching the VID/PID. @@ -615,7 +691,7 @@ dc_usbhid_open (dc_iostream_t **out, dc_context_t *context, unsigned int vid, un if (usbhid->handle == NULL) { ERROR (context, "Failed to open the usb device."); status = DC_STATUS_IO; - goto error_usb_exit; + goto error_session_unref; } usbhid->timeout = -1; @@ -633,8 +709,8 @@ error_usb_free_config: error_usb_free_list: libusb_free_device_list (devices, 1); #endif -error_usb_exit: - dc_usbhid_exit (); +error_session_unref: + dc_usbhid_session_unref (usbhid->session); error_free: dc_iostream_deallocate ((dc_iostream_t *) usbhid); return status; @@ -656,7 +732,7 @@ dc_usbhid_close (dc_iostream_t *abstract) #elif defined(USE_HIDAPI) hid_close(usbhid->handle); #endif - dc_usbhid_exit(); + dc_usbhid_session_unref (usbhid->session); return status; }