core: allow libusb_set_option on the default context before libusb_init
This commit updates libusb_set_option to save the options if setting them on the default
context. This ensures the options 1) can be set before libusb_init(NULL, ...), and 2)
are honored even if the default context is destroyed and re-created.
Signed-off-by: Nathan Hjelm <hjelmn@google.com>
diff --git a/libusb/core.c b/libusb/core.c
index 6fd0c29..054bc6a 100644
--- a/libusb/core.c
+++ b/libusb/core.c
@@ -43,6 +43,8 @@
struct libusb_context *usbi_default_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;
@@ -2173,40 +2175,63 @@
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:
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)
@@ -2270,7 +2295,11 @@
}
#if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING)
- _ctx->debug = get_env_debug_level();
+ 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
@@ -2308,11 +2337,20 @@
usbi_hotplug_init(_ctx);
- usbi_mutex_static_unlock(&default_context_lock);
-
- if (ctx)
+ if (!ctx) {
+ 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_io_exit;
+ }
+ } else
*ctx = _ctx;
+ usbi_mutex_static_unlock(&default_context_lock);
+
return 0;
err_io_exit:
diff --git a/libusb/libusb.h b/libusb/libusb.h
index 57ca58f..a50e06d 100644
--- a/libusb/libusb.h
+++ b/libusb/libusb.h
@@ -2107,7 +2107,9 @@
*
* Only valid for Android builds.
*/
- LIBUSB_OPTION_WEAK_AUTHORITY = 2
+ LIBUSB_OPTION_WEAK_AUTHORITY = 2,
+
+ LIBUSB_OPTION_MAX = 3
};
int LIBUSB_CALL libusb_set_option(libusb_context *ctx, enum libusb_option option, ...);
diff --git a/libusb/libusbi.h b/libusb/libusbi.h
index 97c894e..31d4917 100644
--- a/libusb/libusbi.h
+++ b/libusb/libusbi.h
@@ -791,6 +791,13 @@
short poll_events);
void usbi_remove_event_source(struct libusb_context *ctx, usbi_os_handle_t os_handle);
+struct usbi_option {
+ int is_set;
+ union {
+ int ival;
+ } arg;
+};
+
/* OS event abstraction */
int usbi_create_event(usbi_event_t *event);
diff --git a/libusb/version_nano.h b/libusb/version_nano.h
index 6524fe2..9a19b34 100644
--- a/libusb/version_nano.h
+++ b/libusb/version_nano.h
@@ -1 +1 @@
-#define LIBUSB_NANO 11622
+#define LIBUSB_NANO 11623
diff --git a/tests/stress.c b/tests/stress.c
index 6dcb8f3..a5c9d50 100644
--- a/tests/stress.c
+++ b/tests/stress.c
@@ -130,10 +130,13 @@
return TEST_STATUS_FAILURE;
}
- /* Enable debug output, to be sure to use the context */
- libusb_set_option(NULL, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_DEBUG);
+ /* Enable debug output on new context, to be sure to use the context */
libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_DEBUG);
+ /* Enable debug outout on the default context. This should work even before
+ * the context has been created. */
+ libusb_set_option(NULL, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_DEBUG);
+
/* Now create a reference to the default context */
r = libusb_init(NULL);
if (r != LIBUSB_SUCCESS) {