From 7b920f5c426e6c1574027254d658dfb8a6798d47 Mon Sep 17 00:00:00 2001 From: Jef Driesen Date: Mon, 25 Sep 2017 23:50:55 +0200 Subject: [PATCH] Initialize the usb library only once Initializing the hidapi library more than once is tricky. The hid_init() function can be called multiple times, but the the hid_exit() function will free the resources unconditionally, regardless of how many times the hid_init() function has been called. The consequence is a premature termination of the library. To avoid this problem, the calls are reference counted. Note that this workaround can't protect against calls made outside the libdivecomputer code! The libusb library doesn't suffer from this problem, because each initialization returns a new context pointer. But for consistency, we now also use a single reference counted libusb context. --- src/usbhid.c | 96 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 66 insertions(+), 30 deletions(-) diff --git a/src/usbhid.c b/src/usbhid.c index 38931b8..0f7ba7b 100644 --- a/src/usbhid.c +++ b/src/usbhid.c @@ -52,7 +52,6 @@ struct dc_usbhid_t { dc_context_t *context; /* Internal state. */ #if defined(USE_LIBUSB) - libusb_context *ctx; libusb_device_handle *handle; int interface; unsigned char endpoint_in; @@ -87,13 +86,65 @@ syserror(int errcode) } #endif +#ifdef USBHID +static size_t g_usbhid_refcount = 0; +#ifdef USE_LIBUSB +static libusb_context *g_usbhid_ctx = NULL; +#endif + +static dc_status_t +dc_usbhid_init (dc_context_t *context) +{ + dc_status_t status = DC_STATUS_SUCCESS; + + 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; + } +#endif + } + + g_usbhid_refcount++; + +error: + return status; +} + +static dc_status_t +dc_usbhid_exit (void) +{ + + if (--g_usbhid_refcount == 0) { +#if defined(USE_LIBUSB) + libusb_exit (g_usbhid_ctx); + g_usbhid_ctx = NULL; +#elif defined(USE_HIDAPI) + hid_exit (); +#endif + } + + return DC_STATUS_SUCCESS; +} +#endif + dc_status_t dc_usbhid_open (dc_usbhid_t **out, dc_context_t *context, unsigned int vid, unsigned int pid) { #ifdef USBHID dc_status_t status = DC_STATUS_SUCCESS; dc_usbhid_t *usbhid = NULL; - int rc = 0; if (out == NULL) return DC_STATUS_INVALIDARGS; @@ -110,21 +161,19 @@ dc_usbhid_open (dc_usbhid_t **out, dc_context_t *context, unsigned int vid, unsi // Library context. usbhid->context = context; -#if defined(USE_LIBUSB) - struct libusb_device **devices = NULL; - struct libusb_config_descriptor *config = NULL; - - // Initialize the libusb library. - rc = libusb_init (&usbhid->ctx); - if (rc != LIBUSB_SUCCESS) { - ERROR (context, "Failed to initialize usb support (%s).", - libusb_error_name (rc)); - status = syserror (rc); + // Initialize the usb library. + status = dc_usbhid_init (context); + if (status != DC_STATUS_SUCCESS) { goto error_free; } +#if defined(USE_LIBUSB) + struct libusb_device **devices = NULL; + struct libusb_config_descriptor *config = NULL; + int rc = 0; + // Enumerate the USB devices. - ssize_t ndevices = libusb_get_device_list (usbhid->ctx, &devices); + ssize_t ndevices = libusb_get_device_list (g_usbhid_ctx, &devices); if (ndevices < 0) { ERROR (context, "Failed to enumerate the usb devices (%s).", libusb_error_name (ndevices)); @@ -243,21 +292,12 @@ dc_usbhid_open (dc_usbhid_t **out, dc_context_t *context, unsigned int vid, unsi libusb_free_device_list (devices, 1); #elif defined(USE_HIDAPI) - - // Initialize the hidapi library. - rc = hid_init(); - if (rc < 0) { - ERROR (context, "Failed to initialize usb support."); - status = DC_STATUS_IO; - goto error_free; - } - // Open the USB device. usbhid->handle = hid_open (vid, pid, NULL); if (usbhid->handle == NULL) { ERROR (context, "Failed to open the usb device."); status = DC_STATUS_IO; - goto error_hid_exit; + goto error_usb_exit; } usbhid->timeout = -1; @@ -274,12 +314,9 @@ error_usb_free_config: libusb_free_config_descriptor (config); error_usb_free_list: libusb_free_device_list (devices, 1); -error_usb_exit: - libusb_exit (usbhid->ctx); -#elif defined(USE_HIDAPI) -error_hid_exit: - hid_exit (); #endif +error_usb_exit: + dc_usbhid_exit (); error_free: free (usbhid); return status; @@ -300,11 +337,10 @@ dc_usbhid_close (dc_usbhid_t *usbhid) #if defined(USE_LIBUSB) libusb_release_interface (usbhid->handle, usbhid->interface); libusb_close (usbhid->handle); - libusb_exit (usbhid->ctx); #elif defined(USE_HIDAPI) hid_close(usbhid->handle); - hid_exit(); #endif + dc_usbhid_exit(); free (usbhid); return status;