diff options
Diffstat (limited to 'libusb/core.c')
-rw-r--r-- | libusb/core.c | 454 |
1 files changed, 233 insertions, 221 deletions
diff --git a/libusb/core.c b/libusb/core.c index 07d459c..ec429b7 100644 --- a/libusb/core.c +++ b/libusb/core.c @@ -21,7 +21,6 @@ */ #include "libusbi.h" -#include "hotplug.h" #include "version.h" #ifdef __ANDROID__ @@ -33,17 +32,21 @@ #include <syslog.h> #endif -struct libusb_context *usbi_default_context; static const struct libusb_version libusb_version_internal = { LIBUSB_MAJOR, LIBUSB_MINOR, LIBUSB_MICRO, LIBUSB_NANO, LIBUSB_RC, "http://libusb.info" }; -static int default_context_refcnt; -static usbi_mutex_static_t default_context_lock = USBI_MUTEX_INITIALIZER; static struct timespec timestamp_origin; #if defined(ENABLE_LOGGING) && !defined(USE_SYSTEM_LOGGING_FACILITY) static libusb_log_cb log_handler; #endif +struct libusb_context *usbi_default_context; +struct libusb_context *usbi_fallback_context; +static int default_context_refcnt; +static usbi_mutex_static_t default_context_lock = USBI_MUTEX_INITIALIZER; +static struct usbi_option default_context_options[LIBUSB_OPTION_MAX]; + + usbi_mutex_static_t active_contexts_lock = USBI_MUTEX_INITIALIZER; struct list_head active_contexts_list; @@ -310,7 +313,8 @@ if (cfg != desired) * - libusb is able to send a packet of zero length to an endpoint simply by * submitting a transfer of zero length. * - The \ref libusb_transfer_flags::LIBUSB_TRANSFER_ADD_ZERO_PACKET - * "LIBUSB_TRANSFER_ADD_ZERO_PACKET" flag is currently only supported on Linux. + * "LIBUSB_TRANSFER_ADD_ZERO_PACKET" flag is currently supported on Linux, + * Darwin and Windows (WinUSB). */ /** @@ -577,7 +581,9 @@ libusb_free_device_list(list, 1); * * The libusb_get_device_list() function can be used to obtain a list of * devices currently connected to the system. This is known as device - * discovery. + * discovery. Devices can also be discovered with the hotplug mechanism, + * whereby a callback function registered with libusb_hotplug_register_callback() + * will be called when a device of interest is connected or disconnected. * * Just because you have a reference to a device does not mean it is * necessarily usable. The device may have been unplugged, you may not have @@ -608,7 +614,7 @@ libusb_free_device_list(list, 1); * * With the above information in mind, the process of opening a device can * be viewed as follows: - * -# Discover devices using libusb_get_device_list(). + * -# Discover devices using libusb_get_device_list() or libusb_hotplug_register_callback(). * -# Choose the device that you want to operate, and call libusb_open(). * -# Unref all devices in the discovered device list. * -# Free the discovered device list. @@ -633,7 +639,7 @@ libusb_free_device_list(list, 1); * which grows when required. it can be freed once discovery has completed, * eliminating the need for a list node in the libusb_device structure * itself. */ -#define DISCOVERED_DEVICES_SIZE_STEP 8 +#define DISCOVERED_DEVICES_SIZE_STEP 16 static struct discovered_devs *discovered_devs_alloc(void) { @@ -674,7 +680,7 @@ struct discovered_devs *discovered_devs_append( } /* exceeded capacity, need to grow */ - usbi_dbg("need to increase capacity"); + usbi_dbg(DEVICE_CTX(dev), "need to increase capacity"); capacity = discdevs->capacity + DISCOVERED_DEVICES_SIZE_STEP; /* can't use usbi_reallocf here because in failure cases it would * free the existing discdevs without unreferencing its devices. */ @@ -704,16 +710,14 @@ struct libusb_device *usbi_alloc_device(struct libusb_context *ctx, if (!dev) return NULL; - usbi_mutex_init(&dev->lock); + usbi_atomic_store(&dev->refcnt, 1); dev->ctx = ctx; - dev->refcnt = 1; dev->session_data = session_id; dev->speed = LIBUSB_SPEED_UNKNOWN; - if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) { - usbi_connect_device (dev); - } + if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) + usbi_connect_device(dev); return dev; } @@ -722,39 +726,26 @@ void usbi_connect_device(struct libusb_device *dev) { struct libusb_context *ctx = DEVICE_CTX(dev); - dev->attached = 1; + usbi_atomic_store(&dev->attached, 1); usbi_mutex_lock(&dev->ctx->usb_devs_lock); list_add(&dev->list, &dev->ctx->usb_devs); usbi_mutex_unlock(&dev->ctx->usb_devs_lock); - /* Signal that an event has occurred for this device if we support hotplug AND - * the hotplug message list is ready. This prevents an event from getting raised - * during initial enumeration. */ - if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG) && dev->ctx->hotplug_msgs.next) { - usbi_hotplug_notification(ctx, dev, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED); - } + usbi_hotplug_notification(ctx, dev, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED); } void usbi_disconnect_device(struct libusb_device *dev) { struct libusb_context *ctx = DEVICE_CTX(dev); - usbi_mutex_lock(&dev->lock); - dev->attached = 0; - usbi_mutex_unlock(&dev->lock); + usbi_atomic_store(&dev->attached, 0); usbi_mutex_lock(&ctx->usb_devs_lock); list_del(&dev->list); usbi_mutex_unlock(&ctx->usb_devs_lock); - /* Signal that an event has occurred for this device if we support hotplug AND - * the hotplug message list is ready. This prevents an event from getting raised - * during initial enumeration. libusb_handle_events will take care of dereferencing - * the device. */ - if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG) && dev->ctx->hotplug_msgs.next) { - usbi_hotplug_notification(ctx, dev, LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT); - } + usbi_hotplug_notification(ctx, dev, LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT); } /* Perform some final sanity checks on a newly discovered device. If this @@ -775,7 +766,7 @@ int usbi_sanitize_device(struct libusb_device *dev) usbi_err(DEVICE_CTX(dev), "too many configurations"); return LIBUSB_ERROR_IO; } else if (0 == num_configurations) { - usbi_dbg("zero configurations, maybe an unauthorized device"); + usbi_dbg(DEVICE_CTX(dev), "zero configurations, maybe an unauthorized device"); } return 0; @@ -830,7 +821,7 @@ ssize_t API_EXPORTED libusb_get_device_list(libusb_context *ctx, int r = 0; ssize_t i, len; - usbi_dbg(" "); + usbi_dbg(ctx, " "); if (!discdevs) return LIBUSB_ERROR_NO_MEM; @@ -1173,9 +1164,11 @@ out: DEFAULT_VISIBILITY libusb_device * LIBUSB_CALL libusb_ref_device(libusb_device *dev) { - usbi_mutex_lock(&dev->lock); - dev->refcnt++; - usbi_mutex_unlock(&dev->lock); + long refcnt; + + refcnt = usbi_atomic_inc(&dev->refcnt); + assert(refcnt >= 2); + return dev; } @@ -1186,17 +1179,16 @@ libusb_device * LIBUSB_CALL libusb_ref_device(libusb_device *dev) */ void API_EXPORTED libusb_unref_device(libusb_device *dev) { - int refcnt; + long refcnt; if (!dev) return; - usbi_mutex_lock(&dev->lock); - refcnt = --dev->refcnt; - usbi_mutex_unlock(&dev->lock); + refcnt = usbi_atomic_dec(&dev->refcnt); + assert(refcnt >= 0); if (refcnt == 0) { - usbi_dbg("destroy device %d.%d", dev->bus_number, dev->device_address); + usbi_dbg(DEVICE_CTX(dev), "destroy device %d.%d", dev->bus_number, dev->device_address); libusb_unref_device(dev->parent_dev); @@ -1208,7 +1200,6 @@ void API_EXPORTED libusb_unref_device(libusb_device *dev) usbi_disconnect_device(dev); } - usbi_mutex_destroy(&dev->lock); free(dev); } } @@ -1218,8 +1209,10 @@ void API_EXPORTED libusb_unref_device(libusb_device *dev) * handle for the underlying device. The handle allows you to use libusb to * perform I/O on the device in question. * - * Must call libusb_set_option(NULL, LIBUSB_OPTION_WEAK_AUTHORITY) - * before libusb_init if don't have authority to access the usb device directly. + * Call libusb_set_option(NULL, LIBUSB_OPTION_NO_DEVICE_DISCOVERY) before + * libusb_init() if you want to skip enumeration of USB devices. In particular, + * this might be needed on Android if you don't have authority to access USB + * devices in general. * * On Linux, the system device handle must be a valid file descriptor opened * on the device node. @@ -1233,6 +1226,8 @@ void API_EXPORTED libusb_unref_device(libusb_device *dev) * * This is a non-blocking function; no requests are sent over the bus. * + * Since version 1.0.23, \ref LIBUSB_API_VERSION >= 0x01000107 + * * \param ctx the context to operate on, or NULL for the default context * \param sys_dev the platform-specific system device handle * \param dev_handle output location for the returned device handle pointer. Only @@ -1251,7 +1246,7 @@ int API_EXPORTED libusb_wrap_sys_device(libusb_context *ctx, intptr_t sys_dev, size_t priv_size = usbi_backend.device_handle_priv_size; int r; - usbi_dbg("wrap_sys_device 0x%" PRIxPTR, (uintptr_t)sys_dev); + usbi_dbg(ctx, "wrap_sys_device 0x%" PRIxPTR, (uintptr_t)sys_dev); ctx = usbi_get_context(ctx); @@ -1266,7 +1261,7 @@ int API_EXPORTED libusb_wrap_sys_device(libusb_context *ctx, intptr_t sys_dev, r = usbi_backend.wrap_sys_device(ctx, _dev_handle, sys_dev); if (r < 0) { - usbi_dbg("wrap_sys_device 0x%" PRIxPTR " returns %d", (uintptr_t)sys_dev, r); + usbi_dbg(ctx, "wrap_sys_device 0x%" PRIxPTR " returns %d", (uintptr_t)sys_dev, r); usbi_mutex_destroy(&_dev_handle->lock); free(_dev_handle); return r; @@ -1306,11 +1301,11 @@ int API_EXPORTED libusb_open(libusb_device *dev, struct libusb_device_handle *_dev_handle; size_t priv_size = usbi_backend.device_handle_priv_size; int r; - usbi_dbg("open %d.%d", dev->bus_number, dev->device_address); - if (!dev->attached) { + usbi_dbg(DEVICE_CTX(dev), "open %d.%d", dev->bus_number, dev->device_address); + + if (!usbi_atomic_load(&dev->attached)) return LIBUSB_ERROR_NO_DEVICE; - } _dev_handle = calloc(1, PTR_ALIGN(sizeof(*_dev_handle)) + priv_size); if (!_dev_handle) @@ -1322,7 +1317,7 @@ int API_EXPORTED libusb_open(libusb_device *dev, r = usbi_backend.open(_dev_handle); if (r < 0) { - usbi_dbg("open %d.%d returns %d", dev->bus_number, dev->device_address, r); + usbi_dbg(DEVICE_CTX(dev), "open %d.%d returns %d", dev->bus_number, dev->device_address, r); libusb_unref_device(dev); usbi_mutex_destroy(&_dev_handle->lock); free(_dev_handle); @@ -1428,7 +1423,7 @@ static void do_close(struct libusb_context *ctx, * just making sure that we don't attempt to process the transfer after * the device handle is invalid */ - usbi_dbg("Removed transfer %p from the in-flight list because device handle %p closed", + usbi_dbg(ctx, "Removed transfer %p from the in-flight list because device handle %p closed", transfer, dev_handle); } usbi_mutex_unlock(&ctx->flying_transfers_lock); @@ -1462,9 +1457,9 @@ void API_EXPORTED libusb_close(libusb_device_handle *dev_handle) if (!dev_handle) return; - usbi_dbg(" "); - ctx = HANDLE_CTX(dev_handle); + usbi_dbg(ctx, " "); + handling_events = usbi_handling_events(ctx); /* Similarly to libusb_open(), we want to interrupt all event handlers @@ -1546,27 +1541,28 @@ int API_EXPORTED libusb_get_configuration(libusb_device_handle *dev_handle, { int r = LIBUSB_ERROR_NOT_SUPPORTED; uint8_t tmp = 0; + struct libusb_context *ctx = HANDLE_CTX(dev_handle); - usbi_dbg(" "); + usbi_dbg(ctx, " "); if (usbi_backend.get_configuration) r = usbi_backend.get_configuration(dev_handle, &tmp); if (r == LIBUSB_ERROR_NOT_SUPPORTED) { - usbi_dbg("falling back to control message"); + usbi_dbg(ctx, "falling back to control message"); r = libusb_control_transfer(dev_handle, LIBUSB_ENDPOINT_IN, LIBUSB_REQUEST_GET_CONFIGURATION, 0, 0, &tmp, 1, 1000); if (r == 1) { r = 0; } else if (r == 0) { - usbi_err(HANDLE_CTX(dev_handle), "zero bytes returned in ctrl transfer?"); + usbi_err(ctx, "zero bytes returned in ctrl transfer?"); r = LIBUSB_ERROR_IO; } else { - usbi_dbg("control failed, error %d", r); + usbi_dbg(ctx, "control failed, error %d", r); } } if (r == 0) { - usbi_dbg("active config %u", tmp); + usbi_dbg(ctx, "active config %u", tmp); *config = (int)tmp; } @@ -1630,7 +1626,7 @@ int API_EXPORTED libusb_get_configuration(libusb_device_handle *dev_handle, int API_EXPORTED libusb_set_configuration(libusb_device_handle *dev_handle, int configuration) { - usbi_dbg("configuration %d", configuration); + usbi_dbg(HANDLE_CTX(dev_handle), "configuration %d", configuration); if (configuration < -1 || configuration > (int)UINT8_MAX) return LIBUSB_ERROR_INVALID_PARAM; return usbi_backend.set_configuration(dev_handle, configuration); @@ -1669,11 +1665,11 @@ int API_EXPORTED libusb_claim_interface(libusb_device_handle *dev_handle, { int r = 0; - usbi_dbg("interface %d", interface_number); + usbi_dbg(HANDLE_CTX(dev_handle), "interface %d", interface_number); if (interface_number < 0 || interface_number >= USB_MAXINTERFACES) return LIBUSB_ERROR_INVALID_PARAM; - if (!dev_handle->dev->attached) + if (!usbi_atomic_load(&dev_handle->dev->attached)) return LIBUSB_ERROR_NO_DEVICE; usbi_mutex_lock(&dev_handle->lock); @@ -1713,7 +1709,7 @@ int API_EXPORTED libusb_release_interface(libusb_device_handle *dev_handle, { int r; - usbi_dbg("interface %d", interface_number); + usbi_dbg(HANDLE_CTX(dev_handle), "interface %d", interface_number); if (interface_number < 0 || interface_number >= USB_MAXINTERFACES) return LIBUSB_ERROR_INVALID_PARAM; @@ -1756,19 +1752,19 @@ out: int API_EXPORTED libusb_set_interface_alt_setting(libusb_device_handle *dev_handle, int interface_number, int alternate_setting) { - usbi_dbg("interface %d altsetting %d", + usbi_dbg(HANDLE_CTX(dev_handle), "interface %d altsetting %d", interface_number, alternate_setting); if (interface_number < 0 || interface_number >= USB_MAXINTERFACES) return LIBUSB_ERROR_INVALID_PARAM; if (alternate_setting < 0 || alternate_setting > (int)UINT8_MAX) return LIBUSB_ERROR_INVALID_PARAM; - usbi_mutex_lock(&dev_handle->lock); - if (!dev_handle->dev->attached) { + if (!usbi_atomic_load(&dev_handle->dev->attached)) { usbi_mutex_unlock(&dev_handle->lock); return LIBUSB_ERROR_NO_DEVICE; } + usbi_mutex_lock(&dev_handle->lock); if (!(dev_handle->claimed_interfaces & (1U << interface_number))) { usbi_mutex_unlock(&dev_handle->lock); return LIBUSB_ERROR_NOT_FOUND; @@ -1798,8 +1794,8 @@ int API_EXPORTED libusb_set_interface_alt_setting(libusb_device_handle *dev_hand int API_EXPORTED libusb_clear_halt(libusb_device_handle *dev_handle, unsigned char endpoint) { - usbi_dbg("endpoint %x", endpoint); - if (!dev_handle->dev->attached) + usbi_dbg(HANDLE_CTX(dev_handle), "endpoint 0x%x", endpoint); + if (!usbi_atomic_load(&dev_handle->dev->attached)) return LIBUSB_ERROR_NO_DEVICE; return usbi_backend.clear_halt(dev_handle, endpoint); @@ -1826,8 +1822,8 @@ int API_EXPORTED libusb_clear_halt(libusb_device_handle *dev_handle, */ int API_EXPORTED libusb_reset_device(libusb_device_handle *dev_handle) { - usbi_dbg(" "); - if (!dev_handle->dev->attached) + usbi_dbg(HANDLE_CTX(dev_handle), " "); + if (!usbi_atomic_load(&dev_handle->dev->attached)) return LIBUSB_ERROR_NO_DEVICE; if (usbi_backend.reset_device) @@ -1860,12 +1856,12 @@ int API_EXPORTED libusb_reset_device(libusb_device_handle *dev_handle) int API_EXPORTED libusb_alloc_streams(libusb_device_handle *dev_handle, uint32_t num_streams, unsigned char *endpoints, int num_endpoints) { - usbi_dbg("streams %u eps %d", (unsigned)num_streams, num_endpoints); + usbi_dbg(HANDLE_CTX(dev_handle), "streams %u eps %d", (unsigned)num_streams, num_endpoints); if (!num_streams || !endpoints || num_endpoints <= 0) return LIBUSB_ERROR_INVALID_PARAM; - if (!dev_handle->dev->attached) + if (!usbi_atomic_load(&dev_handle->dev->attached)) return LIBUSB_ERROR_NO_DEVICE; if (usbi_backend.alloc_streams) @@ -1890,12 +1886,12 @@ int API_EXPORTED libusb_alloc_streams(libusb_device_handle *dev_handle, int API_EXPORTED libusb_free_streams(libusb_device_handle *dev_handle, unsigned char *endpoints, int num_endpoints) { - usbi_dbg("eps %d", num_endpoints); + usbi_dbg(HANDLE_CTX(dev_handle), "eps %d", num_endpoints); if (!endpoints || num_endpoints <= 0) return LIBUSB_ERROR_INVALID_PARAM; - if (!dev_handle->dev->attached) + if (!usbi_atomic_load(&dev_handle->dev->attached)) return LIBUSB_ERROR_NO_DEVICE; if (usbi_backend.free_streams) @@ -1933,7 +1929,7 @@ DEFAULT_VISIBILITY unsigned char * LIBUSB_CALL libusb_dev_mem_alloc(libusb_device_handle *dev_handle, size_t length) { - if (!dev_handle->dev->attached) + if (!usbi_atomic_load(&dev_handle->dev->attached)) return NULL; if (usbi_backend.dev_mem_alloc) @@ -1979,12 +1975,12 @@ int API_EXPORTED libusb_dev_mem_free(libusb_device_handle *dev_handle, int API_EXPORTED libusb_kernel_driver_active(libusb_device_handle *dev_handle, int interface_number) { - usbi_dbg("interface %d", interface_number); + usbi_dbg(HANDLE_CTX(dev_handle), "interface %d", interface_number); if (interface_number < 0 || interface_number >= USB_MAXINTERFACES) return LIBUSB_ERROR_INVALID_PARAM; - if (!dev_handle->dev->attached) + if (!usbi_atomic_load(&dev_handle->dev->attached)) return LIBUSB_ERROR_NO_DEVICE; if (usbi_backend.kernel_driver_active) @@ -1997,7 +1993,7 @@ int API_EXPORTED libusb_kernel_driver_active(libusb_device_handle *dev_handle, * Detach a kernel driver from an interface. If successful, you will then be * able to claim the interface and perform I/O. * - * This functionality is not available on Darwin or Windows. + * This functionality is not available on Windows. * * Note that libusb itself also talks to the device through a special kernel * driver, if this driver is already attached to the device, this call will @@ -2017,12 +2013,12 @@ int API_EXPORTED libusb_kernel_driver_active(libusb_device_handle *dev_handle, int API_EXPORTED libusb_detach_kernel_driver(libusb_device_handle *dev_handle, int interface_number) { - usbi_dbg("interface %d", interface_number); + usbi_dbg(HANDLE_CTX(dev_handle), "interface %d", interface_number); if (interface_number < 0 || interface_number >= USB_MAXINTERFACES) return LIBUSB_ERROR_INVALID_PARAM; - if (!dev_handle->dev->attached) + if (!usbi_atomic_load(&dev_handle->dev->attached)) return LIBUSB_ERROR_NO_DEVICE; if (usbi_backend.detach_kernel_driver) @@ -2033,10 +2029,9 @@ int API_EXPORTED libusb_detach_kernel_driver(libusb_device_handle *dev_handle, /** \ingroup libusb_dev * Re-attach an interface's kernel driver, which was previously detached - * using libusb_detach_kernel_driver(). This call is only effective on - * Linux and returns LIBUSB_ERROR_NOT_SUPPORTED on all other platforms. + * using libusb_detach_kernel_driver(). * - * This functionality is not available on Darwin or Windows. + * This functionality is not available on Windows. * * \param dev_handle a device handle * \param interface_number the interface to attach the driver from @@ -2054,12 +2049,12 @@ int API_EXPORTED libusb_detach_kernel_driver(libusb_device_handle *dev_handle, int API_EXPORTED libusb_attach_kernel_driver(libusb_device_handle *dev_handle, int interface_number) { - usbi_dbg("interface %d", interface_number); + usbi_dbg(HANDLE_CTX(dev_handle), "interface %d", interface_number); if (interface_number < 0 || interface_number >= USB_MAXINTERFACES) return LIBUSB_ERROR_INVALID_PARAM; - if (!dev_handle->dev->attached) + if (!usbi_atomic_load(&dev_handle->dev->attached)) return LIBUSB_ERROR_NO_DEVICE; if (usbi_backend.attach_kernel_driver) @@ -2130,6 +2125,8 @@ void API_EXPORTED libusb_set_debug(libusb_context *ctx, int level) * If ENABLE_DEBUG_LOGGING is defined then per context callback function will * never be called. * + * Since version 1.0.23, \ref LIBUSB_API_VERSION >= 0x01000107 + * * \param ctx context on which to assign log handler, or NULL for the default * context. Parameter ignored if only LIBUSB_LOG_CB_GLOBAL mode is requested. * \param cb pointer to the callback function, or NULL to stop log @@ -2170,6 +2167,9 @@ void API_EXPORTED libusb_set_log_cb(libusb_context *ctx, libusb_log_cb cb, * Some options require one or more arguments to be provided. Consult each * option's documentation for specific requirements. * + * If the context ctx is NULL, the option will be added to a list of default + * options that will be applied to all subsequently created contexts. + * * Since version 1.0.22, \ref LIBUSB_API_VERSION >= 0x01000106 * * \param ctx context on which to operate @@ -2185,40 +2185,63 @@ void API_EXPORTED libusb_set_log_cb(libusb_context *ctx, libusb_log_cb cb, int API_EXPORTED libusb_set_option(libusb_context *ctx, enum libusb_option option, ...) { - int arg, r = LIBUSB_SUCCESS; + int arg = 0, r = LIBUSB_SUCCESS; va_list ap; - ctx = usbi_get_context(ctx); - va_start(ap, option); - switch (option) { - case LIBUSB_OPTION_LOG_LEVEL: + if (LIBUSB_OPTION_LOG_LEVEL == option) { arg = va_arg(ap, int); if (arg < LIBUSB_LOG_LEVEL_NONE || arg > LIBUSB_LOG_LEVEL_DEBUG) { r = LIBUSB_ERROR_INVALID_PARAM; - break; } + } + va_end(ap); + + if (LIBUSB_SUCCESS != r) { + return r; + } + + if (option >= LIBUSB_OPTION_MAX) { + return LIBUSB_ERROR_INVALID_PARAM; + } + + if (NULL == ctx) { + usbi_mutex_static_lock(&default_context_lock); + default_context_options[option].is_set = 1; + if (LIBUSB_OPTION_LOG_LEVEL == option) { + default_context_options[option].arg.ival = arg; + } + usbi_mutex_static_unlock(&default_context_lock); + } + + ctx = usbi_get_context(ctx); + if (NULL == ctx) { + return LIBUSB_SUCCESS; + } + + switch (option) { + case LIBUSB_OPTION_LOG_LEVEL: #if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING) if (!ctx->debug_fixed) ctx->debug = (enum libusb_log_level)arg; #endif break; - /* Handle all backend-specific options here */ + /* Handle all backend-specific options here */ case LIBUSB_OPTION_USE_USBDK: - case LIBUSB_OPTION_WEAK_AUTHORITY: + case LIBUSB_OPTION_NO_DEVICE_DISCOVERY: if (usbi_backend.set_option) - r = usbi_backend.set_option(ctx, option, ap); - else - r = LIBUSB_ERROR_NOT_SUPPORTED; + return usbi_backend.set_option(ctx, option, ap); + + return LIBUSB_ERROR_NOT_SUPPORTED; break; + case LIBUSB_OPTION_MAX: default: - r = LIBUSB_ERROR_INVALID_PARAM; + return LIBUSB_ERROR_INVALID_PARAM; } - va_end(ap); - return r; + return LIBUSB_SUCCESS;; } #if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING) @@ -2249,113 +2272,125 @@ static enum libusb_log_level get_env_debug_level(void) * context will be created. If there was already a default context, it will * be reused (and nothing will be initialized/reinitialized). * - * \param context Optional output location for context pointer. + * \param ctx Optional output location for context pointer. * Only valid on return code 0. * \returns 0 on success, or a LIBUSB_ERROR code on failure * \see libusb_contexts */ -int API_EXPORTED libusb_init(libusb_context **context) +int API_EXPORTED libusb_init(libusb_context **ctx) { - struct libusb_device *dev, *next; size_t priv_size = usbi_backend.context_priv_size; - struct libusb_context *ctx; - static int first_init = 1; - int r = 0; + struct libusb_context *_ctx; + int r; usbi_mutex_static_lock(&default_context_lock); - if (!timestamp_origin.tv_sec) - usbi_get_monotonic_time(×tamp_origin); - - if (!context && usbi_default_context) { - usbi_dbg("reusing default context"); + if (!ctx && default_context_refcnt > 0) { + usbi_dbg(usbi_default_context, "reusing default context"); default_context_refcnt++; usbi_mutex_static_unlock(&default_context_lock); return 0; } - ctx = calloc(1, PTR_ALIGN(sizeof(*ctx)) + priv_size); - if (!ctx) { - r = LIBUSB_ERROR_NO_MEM; - goto err_unlock; + /* check for first init */ + if (!active_contexts_list.next) { + list_init(&active_contexts_list); + usbi_get_monotonic_time(×tamp_origin); + } + + _ctx = calloc(1, PTR_ALIGN(sizeof(*_ctx)) + priv_size); + if (!_ctx) { + usbi_mutex_static_unlock(&default_context_lock); + return LIBUSB_ERROR_NO_MEM; } #if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING) - ctx->debug = get_env_debug_level(); - if (ctx->debug != LIBUSB_LOG_LEVEL_NONE) - ctx->debug_fixed = 1; + if (NULL == ctx && default_context_options[LIBUSB_OPTION_LOG_LEVEL].is_set) { + _ctx->debug = default_context_options[LIBUSB_OPTION_LOG_LEVEL].arg.ival; + } else { + _ctx->debug = get_env_debug_level(); + } + if (_ctx->debug != LIBUSB_LOG_LEVEL_NONE) + _ctx->debug_fixed = 1; #endif - /* default context should be initialized before calling usbi_dbg */ - if (!usbi_default_context) { - usbi_default_context = ctx; - default_context_refcnt++; - usbi_dbg("created default context"); + usbi_mutex_init(&_ctx->usb_devs_lock); + usbi_mutex_init(&_ctx->open_devs_lock); + list_init(&_ctx->usb_devs); + list_init(&_ctx->open_devs); + + /* apply default options to all new contexts */ + for (enum libusb_option option = 0 ; option < LIBUSB_OPTION_MAX ; option++) { + if (LIBUSB_OPTION_LOG_LEVEL == option || !default_context_options[option].is_set) { + continue; + } + r = libusb_set_option(_ctx, option); + if (LIBUSB_SUCCESS != r) + goto err_free_ctx; + } + + /* default context must be initialized before calling usbi_dbg */ + if (!ctx) { + usbi_default_context = _ctx; + default_context_refcnt = 1; + usbi_dbg(usbi_default_context, "created default context"); } - usbi_dbg("libusb v%u.%u.%u.%u%s", libusb_version_internal.major, libusb_version_internal.minor, + usbi_dbg(_ctx, "libusb v%u.%u.%u.%u%s", libusb_version_internal.major, libusb_version_internal.minor, libusb_version_internal.micro, libusb_version_internal.nano, libusb_version_internal.rc); - usbi_mutex_init(&ctx->usb_devs_lock); - usbi_mutex_init(&ctx->open_devs_lock); - usbi_mutex_init(&ctx->hotplug_cbs_lock); - list_init(&ctx->usb_devs); - list_init(&ctx->open_devs); - list_init(&ctx->hotplug_cbs); - ctx->next_hotplug_cb_handle = 1; + r = usbi_io_init(_ctx); + if (r < 0) + goto err_free_ctx; usbi_mutex_static_lock(&active_contexts_lock); - if (first_init) { - first_init = 0; - list_init(&active_contexts_list); - } - list_add (&ctx->list, &active_contexts_list); + list_add(&_ctx->list, &active_contexts_list); usbi_mutex_static_unlock(&active_contexts_lock); if (usbi_backend.init) { - r = usbi_backend.init(ctx); + r = usbi_backend.init(_ctx); if (r) - goto err_free_ctx; + goto err_io_exit; } - r = usbi_io_init(ctx); - if (r < 0) - goto err_backend_exit; + /* Initialize hotplug after the initial enumeration is done. */ + usbi_hotplug_init(_ctx); - usbi_mutex_static_unlock(&default_context_lock); + if (ctx) { + *ctx = _ctx; - if (context) - *context = ctx; + if (!usbi_fallback_context) { + usbi_fallback_context = _ctx; + usbi_warn(usbi_fallback_context, "installing new context as implicit default"); + } + } - return 0; + usbi_mutex_static_unlock(&default_context_lock); -err_backend_exit: - if (usbi_backend.exit) - usbi_backend.exit(ctx); -err_free_ctx: - if (ctx == usbi_default_context) { - usbi_default_context = NULL; - default_context_refcnt--; - } + return 0; +err_io_exit: usbi_mutex_static_lock(&active_contexts_lock); - list_del(&ctx->list); + list_del(&_ctx->list); usbi_mutex_static_unlock(&active_contexts_lock); - usbi_mutex_lock(&ctx->usb_devs_lock); - for_each_device_safe(ctx, dev, next) { - list_del(&dev->list); - libusb_unref_device(dev); + usbi_hotplug_exit(_ctx); + usbi_io_exit(_ctx); + +err_free_ctx: + if (!ctx) { + /* clear default context that was not fully initialized */ + usbi_default_context = NULL; + default_context_refcnt = 0; } - usbi_mutex_unlock(&ctx->usb_devs_lock); - usbi_mutex_destroy(&ctx->open_devs_lock); - usbi_mutex_destroy(&ctx->usb_devs_lock); - usbi_mutex_destroy(&ctx->hotplug_cbs_lock); + usbi_mutex_destroy(&_ctx->open_devs_lock); + usbi_mutex_destroy(&_ctx->usb_devs_lock); + + free(_ctx); - free(ctx); -err_unlock: usbi_mutex_static_unlock(&default_context_lock); + return r; } @@ -2366,90 +2401,66 @@ err_unlock: */ void API_EXPORTED libusb_exit(libusb_context *ctx) { - struct libusb_device *dev, *next; - struct timeval tv = { 0, 0 }; - int destroying_default_context = 0; - - usbi_dbg(" "); + struct libusb_context *_ctx; + struct libusb_device *dev; - ctx = usbi_get_context(ctx); + usbi_mutex_static_lock(&default_context_lock); /* if working with default context, only actually do the deinitialization * if we're the last user */ - usbi_mutex_static_lock(&default_context_lock); - if (ctx == usbi_default_context) { + if (!ctx) { if (!usbi_default_context) { - usbi_dbg("no default context, not initialized?"); + usbi_dbg(ctx, "no default context, not initialized?"); usbi_mutex_static_unlock(&default_context_lock); return; } if (--default_context_refcnt > 0) { - usbi_dbg("not destroying default context"); + usbi_dbg(ctx, "not destroying default context"); usbi_mutex_static_unlock(&default_context_lock); return; } - usbi_dbg("destroying default context"); - /* - * Setting this flag without unlocking the default context, as - * we are actually destroying the default context. - * usbi_default_context is not set to NULL yet, as all activities - * would only stop after usbi_backend->exit() returns. - */ - destroying_default_context = 1; + usbi_dbg(ctx, "destroying default context"); + _ctx = usbi_default_context; } else { - /* Unlock default context, as we're not modifying it. */ - usbi_mutex_static_unlock(&default_context_lock); + usbi_dbg(ctx, " "); + _ctx = ctx; } usbi_mutex_static_lock(&active_contexts_lock); - list_del(&ctx->list); + list_del(&_ctx->list); usbi_mutex_static_unlock(&active_contexts_lock); - if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) { - usbi_hotplug_deregister(ctx, 1); - - /* - * Ensure any pending unplug events are read from the hotplug - * pipe. The usb_device-s hold in the events are no longer part - * of usb_devs, but the events still hold a reference! - * - * Note we don't do this if the application has left devices - * open (which implies a buggy app) to avoid packet completion - * handlers running when the app does not expect them to run. - */ - if (list_empty(&ctx->open_devs)) - libusb_handle_events_timeout(ctx, &tv); + if (usbi_backend.exit) + usbi_backend.exit(_ctx); - usbi_mutex_lock(&ctx->usb_devs_lock); - for_each_device_safe(ctx, dev, next) { - list_del(&dev->list); - libusb_unref_device(dev); - } - usbi_mutex_unlock(&ctx->usb_devs_lock); - } + if (!ctx) + usbi_default_context = NULL; + if (ctx == usbi_fallback_context) + usbi_fallback_context = NULL; - /* a few sanity checks. don't bother with locking because unless - * there is an application bug, nobody will be accessing these. */ - if (!list_empty(&ctx->usb_devs)) - usbi_warn(ctx, "some libusb_devices were leaked"); - if (!list_empty(&ctx->open_devs)) - usbi_warn(ctx, "application left some devices open"); + usbi_mutex_static_unlock(&default_context_lock); - usbi_io_exit(ctx); - if (usbi_backend.exit) - usbi_backend.exit(ctx); + /* Don't bother with locking after this point because unless there is + * an application bug, nobody will be accessing the context. */ - usbi_mutex_destroy(&ctx->open_devs_lock); - usbi_mutex_destroy(&ctx->usb_devs_lock); - usbi_mutex_destroy(&ctx->hotplug_cbs_lock); - free(ctx); + usbi_hotplug_exit(_ctx); + usbi_io_exit(_ctx); - if (destroying_default_context) { - usbi_default_context = NULL; - usbi_mutex_static_unlock(&default_context_lock); + for_each_device(_ctx, dev) { + usbi_warn(_ctx, "device %d.%d still referenced", + dev->bus_number, dev->device_address); + DEVICE_CTX(dev) = NULL; } + + if (!list_empty(&_ctx->open_devs)) + usbi_warn(_ctx, "application left some devices open"); + + usbi_mutex_destroy(&_ctx->open_devs_lock); + usbi_mutex_destroy(&_ctx->usb_devs_lock); + + free(_ctx); } /** \ingroup libusb_misc @@ -2574,7 +2585,8 @@ static void log_v(struct libusb_context *ctx, enum libusb_log_level level, #else enum libusb_log_level ctx_level; - ctx = usbi_get_context(ctx); + ctx = ctx ? ctx : usbi_default_context; + ctx = ctx ? ctx : usbi_fallback_context; if (ctx) ctx_level = ctx->debug; else |