aboutsummaryrefslogtreecommitdiff
path: root/libusb/core.c
diff options
context:
space:
mode:
Diffstat (limited to 'libusb/core.c')
-rw-r--r--libusb/core.c454
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(&timestamp_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(&timestamp_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