core: Add provisions for per-context private backend data
[platform/upstream/libusb.git] / libusb / core.c
index d57edad..06da8d0 100644 (file)
 #include "libusbi.h"
 #include "hotplug.h"
 
-#if defined(OS_LINUX)
-const struct usbi_os_backend * const usbi_backend = &linux_usbfs_backend;
-#elif defined(OS_DARWIN)
-const struct usbi_os_backend * const usbi_backend = &darwin_backend;
-#elif defined(OS_OPENBSD)
-const struct usbi_os_backend * const usbi_backend = &openbsd_backend;
-#elif defined(OS_NETBSD)
-const struct usbi_os_backend * const usbi_backend = &netbsd_backend;
-#elif defined(OS_WINDOWS)
-
-#if defined(USE_USBDK)
-const struct usbi_os_backend * const usbi_backend = &usbdk_backend;
-#else
-const struct usbi_os_backend * const usbi_backend = &windows_backend;
-#endif
-
-#elif defined(OS_WINCE)
-const struct usbi_os_backend * const usbi_backend = &wince_backend;
-#elif defined(OS_HAIKU)
-const struct usbi_os_backend * const usbi_backend = &haiku_usb_raw_backend;
-#else
-#error "Unsupported OS"
-#endif
-
 struct libusb_context *usbi_default_context = NULL;
 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 = 0;
 static usbi_mutex_static_t default_context_lock = USBI_MUTEX_INITIALIZER;
-static struct timeval timestamp_origin = { 0, 0 };
+static struct timespec timestamp_origin = { 0, 0 };
 
 usbi_mutex_static_t active_contexts_lock = USBI_MUTEX_INITIALIZER;
 struct list_head active_contexts_list;
@@ -185,6 +161,20 @@ struct list_head active_contexts_list;
 /**
  * \page libusb_caveats Caveats
  *
+ * \section fork Fork considerations
+ *
+ * libusb is <em>not</em> designed to work across fork() calls. Depending on
+ * the platform, there may be resources in the parent process that are not
+ * available to the child (e.g. the hotplug monitor thread on Linux). In
+ * addition, since the parent and child will share libusb's internal file
+ * descriptors, using libusb in any way from the child could cause the parent
+ * process's \ref libusb_context to get into an inconsistent state.
+ *
+ * On Linux, libusb's file descriptors will be marked as CLOEXEC, which means
+ * that it is safe to fork() and exec() without worrying about the child
+ * process needing to clean up state or having access to these file descriptors.
+ * Other platforms may not be so forgiving, so consider yourself warned!
+ *
  * \section devresets Device resets
  *
  * The libusb_reset_device() function allows you to reset a device. If your
@@ -289,7 +279,6 @@ if (cfg != desired)
  * information about the end of the short packet, and the user probably wanted
  * that surplus data to arrive in the next logical transfer.
  *
- *
  * \section zlp Zero length packets
  *
  * - libusb is able to send a packet of zero length to an endpoint simply by
@@ -627,6 +616,16 @@ static struct discovered_devs *discovered_devs_alloc(void)
        return ret;
 }
 
+static void discovered_devs_free(struct discovered_devs *discdevs)
+{
+       size_t i;
+
+       for (i = 0; i < discdevs->len; i++)
+               libusb_unref_device(discdevs->devices[i]);
+
+       free(discdevs);
+}
+
 /* append a device to the discovered devices collection. may realloc itself,
  * returning new discdevs. returns NULL on realloc failure. */
 struct discovered_devs *discovered_devs_append(
@@ -634,6 +633,7 @@ struct discovered_devs *discovered_devs_append(
 {
        size_t len = discdevs->len;
        size_t capacity;
+       struct discovered_devs *new_discdevs;
 
        /* if there is space, just append the device */
        if (len < discdevs->capacity) {
@@ -645,25 +645,21 @@ struct discovered_devs *discovered_devs_append(
        /* exceeded capacity, need to grow */
        usbi_dbg("need to increase capacity");
        capacity = discdevs->capacity + DISCOVERED_DEVICES_SIZE_STEP;
-       discdevs = usbi_reallocf(discdevs,
+       /* can't use usbi_reallocf here because in failure cases it would
+        * free the existing discdevs without unreferencing its devices. */
+       new_discdevs = realloc(discdevs,
                sizeof(*discdevs) + (sizeof(void *) * capacity));
-       if (discdevs) {
-               discdevs->capacity = capacity;
-               discdevs->devices[len] = libusb_ref_device(dev);
-               discdevs->len++;
+       if (!new_discdevs) {
+               discovered_devs_free(discdevs);
+               return NULL;
        }
 
-       return discdevs;
-}
+       discdevs = new_discdevs;
+       discdevs->capacity = capacity;
+       discdevs->devices[len] = libusb_ref_device(dev);
+       discdevs->len++;
 
-static void discovered_devs_free(struct discovered_devs *discdevs)
-{
-       size_t i;
-
-       for (i = 0; i < discdevs->len; i++)
-               libusb_unref_device(discdevs->devices[i]);
-
-       free(discdevs);
+       return discdevs;
 }
 
 /* Allocate a new device with a specific session ID. The returned device has
@@ -671,7 +667,7 @@ static void discovered_devs_free(struct discovered_devs *discdevs)
 struct libusb_device *usbi_alloc_device(struct libusb_context *ctx,
        unsigned long session_id)
 {
-       size_t priv_size = usbi_backend->device_priv_size;
+       size_t priv_size = usbi_backend.device_priv_size;
        struct libusb_device *dev = calloc(1, sizeof(*dev) + priv_size);
        int r;
 
@@ -815,8 +811,8 @@ ssize_t API_EXPORTED libusb_get_device_list(libusb_context *ctx,
                /* backend provides hotplug support */
                struct libusb_device *dev;
 
-               if (usbi_backend->hotplug_poll)
-                       usbi_backend->hotplug_poll();
+               if (usbi_backend.hotplug_poll)
+                       usbi_backend.hotplug_poll();
 
                usbi_mutex_lock(&ctx->usb_devs_lock);
                list_for_each_entry(dev, &ctx->usb_devs, list, struct libusb_device) {
@@ -830,7 +826,7 @@ ssize_t API_EXPORTED libusb_get_device_list(libusb_context *ctx,
                usbi_mutex_unlock(&ctx->usb_devs_lock);
        } else {
                /* backend does not provide hotplug support */
-               r = usbi_backend->get_device_list(ctx, &discdevs);
+               r = usbi_backend.get_device_list(ctx, &discdevs);
        }
 
        if (r < 0) {
@@ -854,7 +850,8 @@ ssize_t API_EXPORTED libusb_get_device_list(libusb_context *ctx,
        *list = ret;
 
 out:
-       discovered_devs_free(discdevs);
+       if (discdevs)
+               discovered_devs_free(discdevs);
        return len;
 }
 
@@ -1157,8 +1154,8 @@ void API_EXPORTED libusb_unref_device(libusb_device *dev)
 
                libusb_unref_device(dev->parent_dev);
 
-               if (usbi_backend->destroy_device)
-                       usbi_backend->destroy_device(dev);
+               if (usbi_backend.destroy_device)
+                       usbi_backend.destroy_device(dev);
 
                if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
                        /* backend does not support hotplug */
@@ -1232,7 +1229,7 @@ int API_EXPORTED libusb_open(libusb_device *dev,
 {
        struct libusb_context *ctx = DEVICE_CTX(dev);
        struct libusb_device_handle *_dev_handle;
-       size_t priv_size = usbi_backend->device_handle_priv_size;
+       size_t priv_size = usbi_backend.device_handle_priv_size;
        int r;
        usbi_dbg("open %d.%d", dev->bus_number, dev->device_address);
 
@@ -1255,7 +1252,7 @@ int API_EXPORTED libusb_open(libusb_device *dev,
        _dev_handle->claimed_interfaces = 0;
        memset(&_dev_handle->os_priv, 0, priv_size);
 
-       r = usbi_backend->open(_dev_handle);
+       r = usbi_backend.open(_dev_handle);
        if (r < 0) {
                usbi_dbg("open %d.%d returns %d", dev->bus_number, dev->device_address, r);
                libusb_unref_device(dev);
@@ -1330,8 +1327,6 @@ static void do_close(struct libusb_context *ctx,
        struct usbi_transfer *itransfer;
        struct usbi_transfer *tmp;
 
-       libusb_lock_events(ctx);
-
        /* remove any transfers in flight that are for this device */
        usbi_mutex_lock(&ctx->flying_transfers_lock);
 
@@ -1343,23 +1338,23 @@ static void do_close(struct libusb_context *ctx,
                if (transfer->dev_handle != dev_handle)
                        continue;
 
-               if (!(itransfer->flags & USBI_TRANSFER_DEVICE_DISAPPEARED)) {
+               usbi_mutex_lock(&itransfer->lock);
+               if (!(itransfer->state_flags & USBI_TRANSFER_DEVICE_DISAPPEARED)) {
                        usbi_err(ctx, "Device handle closed while transfer was still being processed, but the device is still connected as far as we know");
 
-                       if (itransfer->flags & USBI_TRANSFER_CANCELLING)
+                       if (itransfer->state_flags & USBI_TRANSFER_CANCELLING)
                                usbi_warn(ctx, "A cancellation for an in-flight transfer hasn't completed but closing the device handle");
                        else
                                usbi_err(ctx, "A cancellation hasn't even been scheduled on the transfer for which the device is closing");
                }
+               usbi_mutex_unlock(&itransfer->lock);
 
                /* remove from the list of in-flight transfers and make sure
                 * we don't accidentally use the device handle in the future
                 * (or that such accesses will be easily caught and identified as a crash)
                 */
-               usbi_mutex_lock(&itransfer->lock);
                list_del(&itransfer->list);
                transfer->dev_handle = NULL;
-               usbi_mutex_unlock(&itransfer->lock);
 
                /* it is up to the user to free up the actual transfer struct.  this is
                 * just making sure that we don't attempt to process the transfer after
@@ -1370,13 +1365,11 @@ static void do_close(struct libusb_context *ctx,
        }
        usbi_mutex_unlock(&ctx->flying_transfers_lock);
 
-       libusb_unlock_events(ctx);
-
        usbi_mutex_lock(&ctx->open_devs_lock);
        list_del(&dev_handle->list);
        usbi_mutex_unlock(&ctx->open_devs_lock);
 
-       usbi_backend->close(dev_handle);
+       usbi_backend.close(dev_handle);
        libusb_unref_device(dev_handle->dev);
        usbi_mutex_destroy(&dev_handle->lock);
        free(dev_handle);
@@ -1396,6 +1389,7 @@ static void do_close(struct libusb_context *ctx,
 void API_EXPORTED libusb_close(libusb_device_handle *dev_handle)
 {
        struct libusb_context *ctx;
+       int handling_events;
        int pending_events;
 
        if (!dev_handle)
@@ -1403,39 +1397,46 @@ void API_EXPORTED libusb_close(libusb_device_handle *dev_handle)
        usbi_dbg("");
 
        ctx = HANDLE_CTX(dev_handle);
+       handling_events = usbi_handling_events(ctx);
 
        /* Similarly to libusb_open(), we want to interrupt all event handlers
         * at this point. More importantly, we want to perform the actual close of
         * the device while holding the event handling lock (preventing any other
         * thread from doing event handling) because we will be removing a file
-        * descriptor from the polling loop. */
-
-       /* Record that we are closing a device.
-        * Only signal an event if there are no prior pending events. */
-       usbi_mutex_lock(&ctx->event_data_lock);
-       pending_events = usbi_pending_events(ctx);
-       ctx->device_close++;
-       if (!pending_events)
-               usbi_signal_event(ctx);
-       usbi_mutex_unlock(&ctx->event_data_lock);
-
-       /* take event handling lock */
-       libusb_lock_events(ctx);
+        * descriptor from the polling loop. If this is being called by the current
+        * event handler, we can bypass the interruption code because we already
+        * hold the event handling lock. */
+
+       if (!handling_events) {
+               /* Record that we are closing a device.
+                * Only signal an event if there are no prior pending events. */
+               usbi_mutex_lock(&ctx->event_data_lock);
+               pending_events = usbi_pending_events(ctx);
+               ctx->device_close++;
+               if (!pending_events)
+                       usbi_signal_event(ctx);
+               usbi_mutex_unlock(&ctx->event_data_lock);
+
+               /* take event handling lock */
+               libusb_lock_events(ctx);
+       }
 
        /* Close the device */
        do_close(ctx, dev_handle);
 
-       /* We're done with closing this device.
-        * Clear the event pipe if there are no further pending events. */
-       usbi_mutex_lock(&ctx->event_data_lock);
-       ctx->device_close--;
-       pending_events = usbi_pending_events(ctx);
-       if (!pending_events)
-               usbi_clear_event(ctx);
-       usbi_mutex_unlock(&ctx->event_data_lock);
-
-       /* Release event handling lock and wake up event waiters */
-       libusb_unlock_events(ctx);
+       if (!handling_events) {
+               /* We're done with closing this device.
+                * Clear the event pipe if there are no further pending events. */
+               usbi_mutex_lock(&ctx->event_data_lock);
+               ctx->device_close--;
+               pending_events = usbi_pending_events(ctx);
+               if (!pending_events)
+                       usbi_clear_event(ctx);
+               usbi_mutex_unlock(&ctx->event_data_lock);
+
+               /* Release event handling lock and wake up event waiters */
+               libusb_unlock_events(ctx);
+       }
 }
 
 /** \ingroup libusb_dev
@@ -1477,8 +1478,8 @@ int API_EXPORTED libusb_get_configuration(libusb_device_handle *dev_handle,
        int r = LIBUSB_ERROR_NOT_SUPPORTED;
 
        usbi_dbg("");
-       if (usbi_backend->get_configuration)
-               r = usbi_backend->get_configuration(dev_handle, config);
+       if (usbi_backend.get_configuration)
+               r = usbi_backend.get_configuration(dev_handle, config);
 
        if (r == LIBUSB_ERROR_NOT_SUPPORTED) {
                uint8_t tmp = 0;
@@ -1553,7 +1554,7 @@ int API_EXPORTED libusb_set_configuration(libusb_device_handle *dev_handle,
        int configuration)
 {
        usbi_dbg("configuration %d", configuration);
-       return usbi_backend->set_configuration(dev_handle, configuration);
+       return usbi_backend.set_configuration(dev_handle, configuration);
 }
 
 /** \ingroup libusb_dev
@@ -1600,7 +1601,7 @@ int API_EXPORTED libusb_claim_interface(libusb_device_handle *dev_handle,
        if (dev_handle->claimed_interfaces & (1 << interface_number))
                goto out;
 
-       r = usbi_backend->claim_interface(dev_handle, interface_number);
+       r = usbi_backend.claim_interface(dev_handle, interface_number);
        if (r == 0)
                dev_handle->claimed_interfaces |= 1 << interface_number;
 
@@ -1643,7 +1644,7 @@ int API_EXPORTED libusb_release_interface(libusb_device_handle *dev_handle,
                goto out;
        }
 
-       r = usbi_backend->release_interface(dev_handle, interface_number);
+       r = usbi_backend.release_interface(dev_handle, interface_number);
        if (r == 0)
                dev_handle->claimed_interfaces &= ~(1 << interface_number);
 
@@ -1693,7 +1694,7 @@ int API_EXPORTED libusb_set_interface_alt_setting(libusb_device_handle *dev_hand
        }
        usbi_mutex_unlock(&dev_handle->lock);
 
-       return usbi_backend->set_interface_altsetting(dev_handle, interface_number,
+       return usbi_backend.set_interface_altsetting(dev_handle, interface_number,
                alternate_setting);
 }
 
@@ -1720,7 +1721,7 @@ int API_EXPORTED libusb_clear_halt(libusb_device_handle *dev_handle,
        if (!dev_handle->dev->attached)
                return LIBUSB_ERROR_NO_DEVICE;
 
-       return usbi_backend->clear_halt(dev_handle, endpoint);
+       return usbi_backend.clear_halt(dev_handle, endpoint);
 }
 
 /** \ingroup libusb_dev
@@ -1748,7 +1749,7 @@ int API_EXPORTED libusb_reset_device(libusb_device_handle *dev_handle)
        if (!dev_handle->dev->attached)
                return LIBUSB_ERROR_NO_DEVICE;
 
-       return usbi_backend->reset_device(dev_handle);
+       return usbi_backend.reset_device(dev_handle);
 }
 
 /** \ingroup libusb_asyncio
@@ -1780,8 +1781,8 @@ int API_EXPORTED libusb_alloc_streams(libusb_device_handle *dev_handle,
        if (!dev_handle->dev->attached)
                return LIBUSB_ERROR_NO_DEVICE;
 
-       if (usbi_backend->alloc_streams)
-               return usbi_backend->alloc_streams(dev_handle, num_streams, endpoints,
+       if (usbi_backend.alloc_streams)
+               return usbi_backend.alloc_streams(dev_handle, num_streams, endpoints,
                                                   num_endpoints);
        else
                return LIBUSB_ERROR_NOT_SUPPORTED;
@@ -1807,8 +1808,8 @@ int API_EXPORTED libusb_free_streams(libusb_device_handle *dev_handle,
        if (!dev_handle->dev->attached)
                return LIBUSB_ERROR_NO_DEVICE;
 
-       if (usbi_backend->free_streams)
-               return usbi_backend->free_streams(dev_handle, endpoints,
+       if (usbi_backend.free_streams)
+               return usbi_backend.free_streams(dev_handle, endpoints,
                                                  num_endpoints);
        else
                return LIBUSB_ERROR_NOT_SUPPORTED;
@@ -1845,8 +1846,8 @@ unsigned char * LIBUSB_CALL libusb_dev_mem_alloc(libusb_device_handle *dev_handl
        if (!dev_handle->dev->attached)
                return NULL;
 
-       if (usbi_backend->dev_mem_alloc)
-               return usbi_backend->dev_mem_alloc(dev_handle, length);
+       if (usbi_backend.dev_mem_alloc)
+               return usbi_backend.dev_mem_alloc(dev_handle, length);
        else
                return NULL;
 }
@@ -1862,8 +1863,8 @@ unsigned char * LIBUSB_CALL libusb_dev_mem_alloc(libusb_device_handle *dev_handl
 int API_EXPORTED libusb_dev_mem_free(libusb_device_handle *dev_handle,
        unsigned char *buffer, size_t length)
 {
-       if (usbi_backend->dev_mem_free)
-               return usbi_backend->dev_mem_free(dev_handle, buffer, length);
+       if (usbi_backend.dev_mem_free)
+               return usbi_backend.dev_mem_free(dev_handle, buffer, length);
        else
                return LIBUSB_ERROR_NOT_SUPPORTED;
 }
@@ -1893,8 +1894,8 @@ int API_EXPORTED libusb_kernel_driver_active(libusb_device_handle *dev_handle,
        if (!dev_handle->dev->attached)
                return LIBUSB_ERROR_NO_DEVICE;
 
-       if (usbi_backend->kernel_driver_active)
-               return usbi_backend->kernel_driver_active(dev_handle, interface_number);
+       if (usbi_backend.kernel_driver_active)
+               return usbi_backend.kernel_driver_active(dev_handle, interface_number);
        else
                return LIBUSB_ERROR_NOT_SUPPORTED;
 }
@@ -1928,8 +1929,8 @@ int API_EXPORTED libusb_detach_kernel_driver(libusb_device_handle *dev_handle,
        if (!dev_handle->dev->attached)
                return LIBUSB_ERROR_NO_DEVICE;
 
-       if (usbi_backend->detach_kernel_driver)
-               return usbi_backend->detach_kernel_driver(dev_handle, interface_number);
+       if (usbi_backend.detach_kernel_driver)
+               return usbi_backend.detach_kernel_driver(dev_handle, interface_number);
        else
                return LIBUSB_ERROR_NOT_SUPPORTED;
 }
@@ -1962,8 +1963,8 @@ int API_EXPORTED libusb_attach_kernel_driver(libusb_device_handle *dev_handle,
        if (!dev_handle->dev->attached)
                return LIBUSB_ERROR_NO_DEVICE;
 
-       if (usbi_backend->attach_kernel_driver)
-               return usbi_backend->attach_kernel_driver(dev_handle, interface_number);
+       if (usbi_backend.attach_kernel_driver)
+               return usbi_backend.attach_kernel_driver(dev_handle, interface_number);
        else
                return LIBUSB_ERROR_NOT_SUPPORTED;
 }
@@ -1993,7 +1994,7 @@ int API_EXPORTED libusb_attach_kernel_driver(libusb_device_handle *dev_handle,
 int API_EXPORTED libusb_set_auto_detach_kernel_driver(
        libusb_device_handle *dev_handle, int enable)
 {
-       if (!(usbi_backend->caps & USBI_CAP_SUPPORTS_DETACH_KERNEL_DRIVER))
+       if (!(usbi_backend.caps & USBI_CAP_SUPPORTS_DETACH_KERNEL_DRIVER))
                return LIBUSB_ERROR_NOT_SUPPORTED;
 
        dev_handle->auto_detach_kernel_driver = enable;
@@ -2049,6 +2050,7 @@ int API_EXPORTED libusb_init(libusb_context **context)
 {
        struct libusb_device *dev, *next;
        char *dbg = getenv("LIBUSB_DEBUG");
+       size_t priv_size = usbi_backend.context_priv_size;
        struct libusb_context *ctx;
        static int first_init = 1;
        int r = 0;
@@ -2056,7 +2058,7 @@ int API_EXPORTED libusb_init(libusb_context **context)
        usbi_mutex_static_lock(&default_context_lock);
 
        if (!timestamp_origin.tv_sec) {
-               usbi_gettimeofday(&timestamp_origin, NULL);
+               usbi_backend.clock_gettime(USBI_CLOCK_REALTIME, &timestamp_origin);
        }
 
        if (!context && usbi_default_context) {
@@ -2066,7 +2068,7 @@ int API_EXPORTED libusb_init(libusb_context **context)
                return 0;
        }
 
-       ctx = calloc(1, sizeof(*ctx));
+       ctx = calloc(1, sizeof(*ctx) + priv_size);
        if (!ctx) {
                r = LIBUSB_ERROR_NO_MEM;
                goto err_unlock;
@@ -2107,8 +2109,8 @@ int API_EXPORTED libusb_init(libusb_context **context)
        list_add (&ctx->list, &active_contexts_list);
        usbi_mutex_static_unlock(&active_contexts_lock);
 
-       if (usbi_backend->init) {
-               r = usbi_backend->init(ctx);
+       if (usbi_backend.init) {
+               r = usbi_backend.init(ctx);
                if (r)
                        goto err_free_ctx;
        }
@@ -2125,8 +2127,8 @@ int API_EXPORTED libusb_init(libusb_context **context)
        return 0;
 
 err_backend_exit:
-       if (usbi_backend->exit)
-               usbi_backend->exit();
+       if (usbi_backend.exit)
+               usbi_backend.exit();
 err_free_ctx:
        if (ctx == usbi_default_context) {
                usbi_default_context = NULL;
@@ -2216,8 +2218,8 @@ void API_EXPORTED libusb_exit(struct libusb_context *ctx)
                usbi_warn(ctx, "application left some devices open");
 
        usbi_io_exit(ctx);
-       if (usbi_backend->exit)
-               usbi_backend->exit();
+       if (usbi_backend.exit)
+               usbi_backend.exit();
 
        usbi_mutex_destroy(&ctx->open_devs_lock);
        usbi_mutex_destroy(&ctx->usb_devs_lock);
@@ -2239,64 +2241,53 @@ int API_EXPORTED libusb_has_capability(uint32_t capability)
        case LIBUSB_CAP_HAS_CAPABILITY:
                return 1;
        case LIBUSB_CAP_HAS_HOTPLUG:
-               return !(usbi_backend->get_device_list);
+               return !(usbi_backend.get_device_list);
        case LIBUSB_CAP_HAS_HID_ACCESS:
-               return (usbi_backend->caps & USBI_CAP_HAS_HID_ACCESS);
+               return (usbi_backend.caps & USBI_CAP_HAS_HID_ACCESS);
        case LIBUSB_CAP_SUPPORTS_DETACH_KERNEL_DRIVER:
-               return (usbi_backend->caps & USBI_CAP_SUPPORTS_DETACH_KERNEL_DRIVER);
+               return (usbi_backend.caps & USBI_CAP_SUPPORTS_DETACH_KERNEL_DRIVER);
        }
        return 0;
 }
 
 /* this is defined in libusbi.h if needed */
-#ifdef LIBUSB_GETTIMEOFDAY_WIN32
-/*
- * gettimeofday
- * Implementation according to:
- * The Open Group Base Specifications Issue 6
- * IEEE Std 1003.1, 2004 Edition
- */
-
+#ifdef LIBUSB_PRINTF_WIN32
 /*
- *  THIS SOFTWARE IS NOT COPYRIGHTED
- *
- *  This source code is offered for use in the public domain. You may
- *  use, modify or distribute it freely.
- *
- *  This code is distributed in the hope that it will be useful but
- *  WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY
- *  DISCLAIMED. This includes but is not limited to warranties of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * Prior to VS2015, Microsoft did not provide the snprintf() function and
+ * provided a vsnprintf() that did not guarantee NULL-terminated output.
+ * Microsoft did provide a _snprintf() function, but again it did not
+ * guarantee NULL-terminated output.
  *
- *  Contributed by:
- *  Danny Smith <dannysmith@users.sourceforge.net>
+ * The below implementations guarantee NULL-terminated output and are
+ * C99 compliant.
  */
 
-/* Offset between 1/1/1601 and 1/1/1970 in 100 nanosec units */
-#define _W32_FT_OFFSET (116444736000000000)
+int usbi_snprintf(char *str, size_t size, const char *format, ...)
+{
+       va_list ap;
+       int ret;
+
+       va_start(ap, format);
+       ret = usbi_vsnprintf(str, size, format, ap);
+       va_end(ap);
 
-int usbi_gettimeofday(struct timeval *tp, void *tzp)
+       return ret;
+}
+
+int usbi_vsnprintf(char *str, size_t size, const char *format, va_list ap)
 {
-       union {
-               unsigned __int64 ns100; /* Time since 1 Jan 1601, in 100ns units */
-               FILETIME ft;
-       } _now;
-       UNUSED(tzp);
-
-       if(tp) {
-#if defined(OS_WINCE)
-               SYSTEMTIME st;
-               GetSystemTime(&st);
-               SystemTimeToFileTime(&st, &_now.ft);
-#else
-               GetSystemTimeAsFileTime (&_now.ft);
-#endif
-               tp->tv_usec=(long)((_now.ns100 / 10) % 1000000 );
-               tp->tv_sec= (long)((_now.ns100 - _W32_FT_OFFSET) / 10000000);
+       int ret;
+
+       ret = _vsnprintf(str, size, format, ap);
+       if (ret < 0 || ret == (int)size) {
+               /* Output is truncated, ensure buffer is NULL-terminated and
+                * determine how many characters would have been written. */
+               str[size - 1] = '\0';
+               if (ret < 0)
+                       ret = _vsnprintf(NULL, 0, format, ap);
        }
-       /* Always return 0 as per Open Group Base Specifications Issue 6.
-          Do not set errno on error.  */
-       return 0;
+
+       return ret;
 }
 #endif
 
@@ -2304,7 +2295,9 @@ static void usbi_log_str(struct libusb_context *ctx,
        enum libusb_log_level level, const char * str)
 {
 #if defined(USE_SYSTEM_LOGGING_FACILITY)
-#if defined(OS_WINDOWS) || defined(OS_WINCE)
+#if defined(OS_WINDOWS)
+       OutputDebugString(str);
+#elif defined(OS_WINCE)
        /* Windows CE only supports the Unicode version of OutputDebugString. */
        WCHAR wbuf[USBI_MAX_LOG_LEN];
        MultiByteToWideChar(CP_UTF8, 0, str, -1, wbuf, sizeof(wbuf));
@@ -2316,6 +2309,7 @@ static void usbi_log_str(struct libusb_context *ctx,
        case LIBUSB_LOG_LEVEL_WARNING: priority = ANDROID_LOG_WARN; break;
        case LIBUSB_LOG_LEVEL_ERROR: priority = ANDROID_LOG_ERROR; break;
        case LIBUSB_LOG_LEVEL_DEBUG: priority = ANDROID_LOG_DEBUG; break;
+       case LIBUSB_LOG_LEVEL_NONE: return;
        }
        __android_log_write(priority, "libusb", str);
 #elif defined(HAVE_SYSLOG_FUNC)
@@ -2325,6 +2319,7 @@ static void usbi_log_str(struct libusb_context *ctx,
        case LIBUSB_LOG_LEVEL_WARNING: syslog_level = LOG_WARNING; break;
        case LIBUSB_LOG_LEVEL_ERROR: syslog_level = LOG_ERR; break;
        case LIBUSB_LOG_LEVEL_DEBUG: syslog_level = LOG_DEBUG; break;
+       case LIBUSB_LOG_LEVEL_NONE: return;
        }
        syslog(syslog_level, "%s", str);
 #else /* All of gcc, Clang, XCode seem to use #warning */
@@ -2343,7 +2338,7 @@ void usbi_log_v(struct libusb_context *ctx, enum libusb_log_level level,
 {
        const char *prefix = "";
        char buf[USBI_MAX_LOG_LEN];
-       struct timeval now;
+       struct timespec now;
        int global_debug, header_len, text_len;
        static int has_debug_header_been_displayed = 0;
 
@@ -2372,18 +2367,18 @@ void usbi_log_v(struct libusb_context *ctx, enum libusb_log_level level,
                return;
 #endif
 
-       usbi_gettimeofday(&now, NULL);
+       usbi_backend.clock_gettime(USBI_CLOCK_REALTIME, &now);
        if ((global_debug) && (!has_debug_header_been_displayed)) {
                has_debug_header_been_displayed = 1;
                usbi_log_str(ctx, LIBUSB_LOG_LEVEL_DEBUG, "[timestamp] [threadID] facility level [function call] <message>" USBI_LOG_LINE_END);
                usbi_log_str(ctx, LIBUSB_LOG_LEVEL_DEBUG, "--------------------------------------------------------------------------------" USBI_LOG_LINE_END);
        }
-       if (now.tv_usec < timestamp_origin.tv_usec) {
+       if (now.tv_nsec < timestamp_origin.tv_nsec) {
                now.tv_sec--;
-               now.tv_usec += 1000000;
+               now.tv_nsec += 1000000000L;
        }
        now.tv_sec -= timestamp_origin.tv_sec;
-       now.tv_usec -= timestamp_origin.tv_usec;
+       now.tv_nsec -= timestamp_origin.tv_nsec;
 
        switch (level) {
        case LIBUSB_LOG_LEVEL_INFO:
@@ -2408,7 +2403,7 @@ void usbi_log_v(struct libusb_context *ctx, enum libusb_log_level level,
        if (global_debug) {
                header_len = snprintf(buf, sizeof(buf),
                        "[%2d.%06d] [%08x] libusb: %s [%s] ",
-                       (int)now.tv_sec, (int)now.tv_usec, usbi_get_tid(), prefix, function);
+                       (int)now.tv_sec, (int)(now.tv_nsec / 1000L), usbi_get_tid(), prefix, function);
        } else {
                header_len = snprintf(buf, sizeof(buf),
                        "libusb: %s [%s] ", prefix, function);