core: allow libusb_set_option on the default context before libusb_init
authorNathan Hjelm <hjelmn@google.com>
Sun, 6 Jun 2021 18:19:05 +0000 (12:19 -0600)
committerNathan Hjelm <hjelmn@google.com>
Tue, 8 Jun 2021 03:28:44 +0000 (21:28 -0600)
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>
libusb/core.c
libusb/libusb.h
libusb/libusbi.h
libusb/version_nano.h
tests/stress.c

index 6fd0c29..054bc6a 100644 (file)
@@ -43,6 +43,8 @@ static libusb_log_cb log_handler;
 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 @@ 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:
                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 @@ int API_EXPORTED libusb_init(libusb_context **ctx)
        }
 
 #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 @@ int API_EXPORTED libusb_init(libusb_context **ctx)
 
        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:
index 57ca58f..a50e06d 100644 (file)
@@ -2107,7 +2107,9 @@ enum libusb_option {
         *
         * 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, ...);
index 97c894e..31d4917 100644 (file)
@@ -791,6 +791,13 @@ int usbi_add_event_source(struct libusb_context *ctx, usbi_os_handle_t os_handle
        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);
index 6524fe2..9a19b34 100644 (file)
@@ -1 +1 @@
-#define LIBUSB_NANO 11622
+#define LIBUSB_NANO 11623
index 6dcb8f3..a5c9d50 100644 (file)
@@ -130,10 +130,13 @@ static libusb_testlib_result test_default_context_change(void)
                        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) {