core: update usbi_dbg to take the context as an argument
[platform/upstream/libusb.git] / libusb / os / windows_common.c
index 2f79928..eefd01e 100644 (file)
@@ -24,9 +24,6 @@
 
 #include <config.h>
 
-#include <errno.h>
-#include <inttypes.h>
-#include <process.h>
 #include <stdio.h>
 
 #include "libusbi.h"
@@ -43,12 +40,6 @@ enum windows_version windows_version = WINDOWS_UNDEFINED;
 static unsigned int init_count;
 static bool usbdk_available;
 
-#if !defined(HAVE_CLOCK_GETTIME)
-// Global variables for clock_gettime mechanism
-static uint64_t hires_ticks_to_ps;
-static uint64_t hires_frequency;
-#endif
-
 /*
 * Converts a windows error to human readable string
 * uses retval as errorcode, or, if 0, use GetLastError()
@@ -103,6 +94,35 @@ const char *windows_error_str(DWORD error_code)
 }
 #endif
 
+/*
+ * Dynamically loads a DLL from the Windows system directory.  Unlike the
+ * LoadLibraryA() function, this function will not search through any
+ * directories to try and find the library.
+ */
+HMODULE load_system_library(struct libusb_context *ctx, const char *name)
+{
+       char library_path[MAX_PATH];
+       char *filename_start;
+       UINT length;
+
+       length = GetSystemDirectoryA(library_path, sizeof(library_path));
+       if ((length == 0) || (length >= (UINT)sizeof(library_path))) {
+               usbi_err(ctx, "program assertion failed - could not get system directory");
+               return NULL;
+       }
+
+       filename_start = library_path + length;
+       // Append '\' + name + ".dll" + NUL
+       length += 1 + (UINT)strlen(name) + 4 + 1;
+       if (length >= (UINT)sizeof(library_path)) {
+               usbi_err(ctx, "program assertion failed - library path buffer overflow");
+               return NULL;
+       }
+
+       sprintf(filename_start, "\\%s.dll", name);
+       return LoadLibraryA(library_path);
+}
+
 /* Hash table functions - modified From glibc 2.3.2:
    [Aho,Sethi,Ullman] Compilers: Principles, Techniques and Tools, 1986
    [Knuth]            The Art of Computer Programming, part 3 (6.4)  */
@@ -125,14 +145,14 @@ static unsigned long htab_filled;
 static bool htab_create(struct libusb_context *ctx)
 {
        if (htab_table != NULL) {
-               usbi_err(ctx, "program assertion falied - hash table already allocated");
+               usbi_err(ctx, "program assertion failed - hash table already allocated");
                return true;
        }
 
        // Create a mutex
        usbi_mutex_init(&htab_mutex);
 
-       usbi_dbg("using %lu entries hash table", HTAB_SIZE);
+       usbi_dbg(ctx, "using %lu entries hash table", HTAB_SIZE);
        htab_filled = 0;
 
        // allocate memory and zero out.
@@ -201,7 +221,7 @@ unsigned long htab_hash(const char *str)
                if ((htab_table[idx].used == hval) && (strcmp(str, htab_table[idx].str) == 0))
                        goto out_unlock; // existing hash
 
-               usbi_dbg("hash collision ('%s' vs '%s')", str, htab_table[idx].str);
+               usbi_dbg(NULL, "hash collision ('%s' vs '%s')", str, htab_table[idx].str);
 
                // Second hash function, as suggested in [Knuth]
                hval2 = 1UL + hval % (HTAB_SIZE - 2);
@@ -263,7 +283,7 @@ enum libusb_transfer_status usbd_status_to_libusb_transfer_status(USBD_STATUS st
        case USBD_STATUS_DEVICE_GONE:
                return LIBUSB_TRANSFER_NO_DEVICE;
        default:
-               usbi_dbg("USBD_STATUS 0x%08lx translated to LIBUSB_TRANSFER_ERROR", ULONG_CAST(status));
+               usbi_dbg(NULL, "USBD_STATUS 0x%08lx translated to LIBUSB_TRANSFER_ERROR", ULONG_CAST(status));
                return LIBUSB_TRANSFER_ERROR;
        }
 }
@@ -273,32 +293,18 @@ enum libusb_transfer_status usbd_status_to_libusb_transfer_status(USBD_STATUS st
  */
 void windows_force_sync_completion(struct usbi_transfer *itransfer, ULONG size)
 {
+       struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+       struct windows_context_priv *priv = usbi_get_context_priv(TRANSFER_CTX(transfer));
        struct windows_transfer_priv *transfer_priv = usbi_get_transfer_priv(itransfer);
        OVERLAPPED *overlapped = &transfer_priv->overlapped;
 
-       usbi_dbg("transfer %p, length %lu", USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer), size);
+       usbi_dbg(TRANSFER_CTX(transfer), "transfer %p, length %lu", transfer, ULONG_CAST(size));
 
        overlapped->Internal = (ULONG_PTR)STATUS_SUCCESS;
        overlapped->InternalHigh = (ULONG_PTR)size;
 
-       usbi_signal_transfer_completion(itransfer);
-}
-
-static void windows_init_clock(void)
-{
-#if !defined(HAVE_CLOCK_GETTIME)
-       LARGE_INTEGER li_frequency;
-
-       // Microsoft says that the QueryPerformanceFrequency() and
-       // QueryPerformanceCounter() functions always succeed on XP and later
-       QueryPerformanceFrequency(&li_frequency);
-
-       // The hires frequency can go as high as 4 GHz, so we'll use a conversion
-       // to picoseconds to compute the tv_nsecs part in clock_gettime
-       hires_frequency = li_frequency.QuadPart;
-       hires_ticks_to_ps = UINT64_C(1000000000000) / hires_frequency;
-       usbi_dbg("hires timer frequency: %"PRIu64" Hz", hires_frequency);
-#endif
+       if (!PostQueuedCompletionStatus(priv->completion_port, (DWORD)size, (ULONG_PTR)transfer->dev_handle, overlapped))
+               usbi_err(TRANSFER_CTX(transfer), "failed to post I/O completion: %s", windows_error_str(0));
 }
 
 /* Windows version detection */
@@ -395,11 +401,11 @@ static enum windows_version get_windows_version(void)
        arch = is_x64() ? "64-bit" : "32-bit";
 
        if (vi.wServicePackMinor)
-               usbi_dbg("Windows %s SP%u.%u %s", w, vi.wServicePackMajor, vi.wServicePackMinor, arch);
+               usbi_dbg(NULL, "Windows %s SP%u.%u %s", w, vi.wServicePackMajor, vi.wServicePackMinor, arch);
        else if (vi.wServicePackMajor)
-               usbi_dbg("Windows %s SP%u %s", w, vi.wServicePackMajor, arch);
+               usbi_dbg(NULL, "Windows %s SP%u %s", w, vi.wServicePackMajor, arch);
        else
-               usbi_dbg("Windows %s %s", w, arch);
+               usbi_dbg(NULL, "Windows %s %s", w, arch);
 
        return winver;
 }
@@ -412,10 +418,14 @@ static unsigned __stdcall windows_iocp_thread(void *arg)
        DWORD num_bytes;
        ULONG_PTR completion_key;
        OVERLAPPED *overlapped;
+       struct libusb_device_handle *dev_handle;
+       struct libusb_device_handle *opened_device_handle;
+       struct windows_device_handle_priv *handle_priv;
        struct windows_transfer_priv *transfer_priv;
        struct usbi_transfer *itransfer;
+       bool found;
 
-       usbi_dbg("I/O completion thread started");
+       usbi_dbg(ctx, "I/O completion thread started");
 
        while (true) {
                overlapped = NULL;
@@ -431,14 +441,48 @@ static unsigned __stdcall windows_iocp_thread(void *arg)
                        break;
                }
 
-               transfer_priv = container_of(overlapped, struct windows_transfer_priv, overlapped);
+               // Find the transfer associated with the OVERLAPPED that just completed.
+               // If we cannot find a match, the I/O operation originated from outside of libusb
+               // (e.g. within libusbK) and we need to ignore it.
+               dev_handle = (struct libusb_device_handle *)completion_key;
+
+               found = false;
+               transfer_priv = NULL;
+
+               // Issue 912: lock opened device handles in context to search the current device handle
+               // to avoid accessing unallocated memory after device has been closed
+               usbi_mutex_lock(&ctx->open_devs_lock);
+               for_each_open_device(ctx, opened_device_handle) {
+                       if (dev_handle == opened_device_handle) {
+                               handle_priv = usbi_get_device_handle_priv(dev_handle);
+
+                               usbi_mutex_lock(&dev_handle->lock);
+                               list_for_each_entry(transfer_priv, &handle_priv->active_transfers, list, struct windows_transfer_priv) {
+                                       if (overlapped == &transfer_priv->overlapped) {
+                                               // This OVERLAPPED belongs to us, remove the transfer from the device handle's list
+                                               list_del(&transfer_priv->list);
+                                               found = true;
+                                               break;
+                                       }
+                               }
+                               usbi_mutex_unlock(&dev_handle->lock);
+                       }
+               }
+               usbi_mutex_unlock(&ctx->open_devs_lock);
+
+               if (!found) {
+                       usbi_dbg(ctx, "ignoring overlapped %p for handle %p (device %u.%u)",
+                               overlapped, dev_handle, dev_handle->dev->bus_number, dev_handle->dev->device_address);
+                       continue;
+               }
+
                itransfer = (struct usbi_transfer *)((unsigned char *)transfer_priv + PTR_ALIGN(sizeof(*transfer_priv)));
-               usbi_dbg("transfer %p completed, length %lu",
+               usbi_dbg(ctx, "transfer %p completed, length %lu",
                         USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer), ULONG_CAST(num_bytes));
                usbi_signal_transfer_completion(itransfer);
        }
 
-       usbi_dbg("I/O completion thread exiting");
+       usbi_dbg(ctx, "I/O completion thread exiting");
 
        return 0;
 }
@@ -446,26 +490,9 @@ static unsigned __stdcall windows_iocp_thread(void *arg)
 static int windows_init(struct libusb_context *ctx)
 {
        struct windows_context_priv *priv = usbi_get_context_priv(ctx);
-       char mutex_name[11 + 8 + 1]; // strlen("libusb_init") + (32-bit hex PID) + '\0'
-       HANDLE mutex;
        bool winusb_backend_init = false;
        int r;
 
-       sprintf(mutex_name, "libusb_init%08lX", ULONG_CAST(GetCurrentProcessId() & 0xFFFFFFFFU));
-       mutex = CreateMutexA(NULL, FALSE, mutex_name);
-       if (mutex == NULL) {
-               usbi_err(ctx, "could not create mutex: %s", windows_error_str(0));
-               return LIBUSB_ERROR_NO_MEM;
-       }
-
-       // A successful wait gives this thread ownership of the mutex
-       // => any concurent wait stalls until the mutex is released
-       if (WaitForSingleObject(mutex, INFINITE) != WAIT_OBJECT_0) {
-               usbi_err(ctx, "failure to access mutex: %s", windows_error_str(0));
-               CloseHandle(mutex);
-               return LIBUSB_ERROR_NO_MEM;
-       }
-
        // NB: concurrent usage supposes that init calls are equally balanced with
        // exit calls. If init is called more than exit, we will not exit properly
        if (++init_count == 1) { // First init?
@@ -480,8 +507,6 @@ static int windows_init(struct libusb_context *ctx)
                        goto init_exit;
                }
 
-               windows_init_clock();
-
                if (!htab_create(ctx)) {
                        r = LIBUSB_ERROR_NO_MEM;
                        goto init_exit;
@@ -494,7 +519,7 @@ static int windows_init(struct libusb_context *ctx)
 
                r = usbdk_backend.init(ctx);
                if (r == LIBUSB_SUCCESS) {
-                       usbi_dbg("UsbDk backend is available");
+                       usbi_dbg(ctx, "UsbDk backend is available");
                        usbdk_available = true;
                } else {
                        usbi_info(ctx, "UsbDk backend is not available");
@@ -536,29 +561,12 @@ init_exit: // Holds semaphore here
                --init_count;
        }
 
-       ReleaseMutex(mutex);
-       CloseHandle(mutex);
        return r;
 }
 
 static void windows_exit(struct libusb_context *ctx)
 {
        struct windows_context_priv *priv = usbi_get_context_priv(ctx);
-       char mutex_name[11 + 8 + 1]; // strlen("libusb_init") + (32-bit hex PID) + '\0'
-       HANDLE mutex;
-
-       sprintf(mutex_name, "libusb_init%08lX", ULONG_CAST(GetCurrentProcessId() & 0xFFFFFFFFU));
-       mutex = CreateMutexA(NULL, FALSE, mutex_name);
-       if (mutex == NULL)
-               return;
-
-       // A successful wait gives this thread ownership of the mutex
-       // => any concurent wait stalls until the mutex is released
-       if (WaitForSingleObject(mutex, INFINITE) != WAIT_OBJECT_0) {
-               usbi_err(ctx, "failed to access mutex: %s", windows_error_str(0));
-               CloseHandle(mutex);
-               return;
-       }
 
        // A NULL completion status will indicate to the thread that it is time to exit
        if (!PostQueuedCompletionStatus(priv->completion_port, 0, (ULONG_PTR)ctx, NULL))
@@ -579,9 +587,6 @@ static void windows_exit(struct libusb_context *ctx)
                winusb_backend.exit(ctx);
                htab_destroy();
        }
-
-       ReleaseMutex(mutex);
-       CloseHandle(mutex);
 }
 
 static int windows_set_option(struct libusb_context *ctx, enum libusb_option option, va_list ap)
@@ -590,19 +595,17 @@ static int windows_set_option(struct libusb_context *ctx, enum libusb_option opt
 
        UNUSED(ap);
 
-       switch ((int)option) {
-       case LIBUSB_OPTION_USE_USBDK:
-               if (usbdk_available) {
-                       usbi_dbg("switching context %p to use UsbDk backend", ctx);
-                       priv->backend = &usbdk_backend;
-               } else {
+       if (option == LIBUSB_OPTION_USE_USBDK) {
+               if (!usbdk_available) {
                        usbi_err(ctx, "UsbDk backend not available");
                        return LIBUSB_ERROR_NOT_FOUND;
                }
+               usbi_dbg(ctx, "switching context %p to use UsbDk backend", ctx);
+               priv->backend = &usbdk_backend;
                return LIBUSB_SUCCESS;
-       default:
-               return LIBUSB_ERROR_NOT_SUPPORTED;
        }
+
+       return LIBUSB_ERROR_NOT_SUPPORTED;
 }
 
 static int windows_get_device_list(struct libusb_context *ctx, struct discovered_devs **discdevs)
@@ -614,6 +617,9 @@ static int windows_get_device_list(struct libusb_context *ctx, struct discovered
 static int windows_open(struct libusb_device_handle *dev_handle)
 {
        struct windows_context_priv *priv = usbi_get_context_priv(HANDLE_CTX(dev_handle));
+       struct windows_device_handle_priv *handle_priv = usbi_get_device_handle_priv(dev_handle);
+
+       list_init(&handle_priv->active_transfers);
        return priv->backend->open(dev_handle);
 }
 
@@ -698,8 +704,10 @@ static void windows_destroy_device(struct libusb_device *dev)
 static int windows_submit_transfer(struct usbi_transfer *itransfer)
 {
        struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
-       struct libusb_context *ctx = TRANSFER_CTX(transfer);
+       struct libusb_device_handle *dev_handle = transfer->dev_handle;
+       struct libusb_context *ctx = HANDLE_CTX(dev_handle);
        struct windows_context_priv *priv = usbi_get_context_priv(ctx);
+       struct windows_device_handle_priv *handle_priv = usbi_get_device_handle_priv(dev_handle);
        struct windows_transfer_priv *transfer_priv = usbi_get_transfer_priv(itransfer);
        int r;
 
@@ -722,8 +730,18 @@ static int windows_submit_transfer(struct usbi_transfer *itransfer)
                transfer_priv->handle = NULL;
        }
 
+       // Add transfer to the device handle's list
+       usbi_mutex_lock(&dev_handle->lock);
+       list_add_tail(&transfer_priv->list, &handle_priv->active_transfers);
+       usbi_mutex_unlock(&dev_handle->lock);
+
        r = priv->backend->submit_transfer(itransfer);
        if (r != LIBUSB_SUCCESS) {
+               // Remove the unsuccessful transfer from the device handle's list
+               usbi_mutex_lock(&dev_handle->lock);
+               list_del(&transfer_priv->list);
+               usbi_mutex_unlock(&dev_handle->lock);
+
                // Always call the backend's clear_transfer_priv() function on failure
                priv->backend->clear_transfer_priv(itransfer);
                transfer_priv->handle = NULL;
@@ -772,7 +790,7 @@ static int windows_handle_transfer_completion(struct usbi_transfer *itransfer)
        else
                result = GetLastError();
 
-       usbi_dbg("handling transfer %p completion with errcode %lu, length %lu",
+       usbi_dbg(ctx, "handling transfer %p completion with errcode %lu, length %lu",
                 USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer), ULONG_CAST(result), ULONG_CAST(bytes_transferred));
 
        switch (result) {
@@ -780,25 +798,25 @@ static int windows_handle_transfer_completion(struct usbi_transfer *itransfer)
                status = backend->copy_transfer_data(itransfer, bytes_transferred);
                break;
        case ERROR_GEN_FAILURE:
-               usbi_dbg("detected endpoint stall");
+               usbi_dbg(ctx, "detected endpoint stall");
                status = LIBUSB_TRANSFER_STALL;
                break;
        case ERROR_SEM_TIMEOUT:
-               usbi_dbg("detected semaphore timeout");
+               usbi_dbg(ctx, "detected semaphore timeout");
                status = LIBUSB_TRANSFER_TIMED_OUT;
                break;
        case ERROR_OPERATION_ABORTED:
                istatus = backend->copy_transfer_data(itransfer, bytes_transferred);
                if (istatus != LIBUSB_TRANSFER_COMPLETED)
-                       usbi_dbg("failed to copy partial data in aborted operation: %d", (int)istatus);
+                       usbi_dbg(ctx, "failed to copy partial data in aborted operation: %d", (int)istatus);
 
-               usbi_dbg("detected operation aborted");
+               usbi_dbg(ctx, "detected operation aborted");
                status = LIBUSB_TRANSFER_CANCELLED;
                break;
        case ERROR_FILE_NOT_FOUND:
        case ERROR_DEVICE_NOT_CONNECTED:
        case ERROR_NO_SUCH_DEVICE:
-               usbi_dbg("detected device removed");
+               usbi_dbg(ctx, "detected device removed");
                status = LIBUSB_TRANSFER_NO_DEVICE;
                break;
        default:
@@ -819,50 +837,30 @@ static int windows_handle_transfer_completion(struct usbi_transfer *itransfer)
                return usbi_handle_transfer_completion(itransfer, status);
 }
 
-#if !defined(HAVE_CLOCK_GETTIME)
-int usbi_clock_gettime(int clk_id, struct timespec *tp)
+void usbi_get_monotonic_time(struct timespec *tp)
 {
+       static LONG hires_counter_init;
+       static uint64_t hires_ticks_to_ps;
+       static uint64_t hires_frequency;
        LARGE_INTEGER hires_counter;
-#if !defined(_MSC_VER) || (_MSC_VER < 1900)
-       FILETIME filetime;
-       ULARGE_INTEGER rtime;
-#endif
 
-       switch (clk_id) {
-       case USBI_CLOCK_MONOTONIC:
-               if (hires_frequency) {
-                       QueryPerformanceCounter(&hires_counter);
-                       tp->tv_sec = (long)(hires_counter.QuadPart / hires_frequency);
-                       tp->tv_nsec = (long)(((hires_counter.QuadPart % hires_frequency) * hires_ticks_to_ps) / UINT64_C(1000));
-                       return 0;
-               }
-               // Return real-time if monotonic was not detected @ timer init
-               // Fall through
-       case USBI_CLOCK_REALTIME:
-#if defined(_MSC_VER) && (_MSC_VER >= 1900)
-               if (!timespec_get(tp, TIME_UTC)) {
-                       errno = EIO;
-                       return -1;
-               }
-#else
-               // We follow http://msdn.microsoft.com/en-us/library/ms724928%28VS.85%29.aspx
-               // with a predef epoch time to have an epoch that starts at 1970.01.01 00:00
-               // Note however that our resolution is bounded by the Windows system time
-               // functions and is at best of the order of 1 ms (or, usually, worse)
-               GetSystemTimeAsFileTime(&filetime);
-               rtime.LowPart = filetime.dwLowDateTime;
-               rtime.HighPart = filetime.dwHighDateTime;
-               rtime.QuadPart -= EPOCH_TIME;
-               tp->tv_sec = (long)(rtime.QuadPart / 10000000);
-               tp->tv_nsec = (long)((rtime.QuadPart % 10000000) * 100);
-#endif
-               return 0;
-       default:
-               errno = EINVAL;
-               return -1;
+       if (InterlockedExchange(&hires_counter_init, 1L) == 0L) {
+               LARGE_INTEGER li_frequency;
+
+               // Microsoft says that the QueryPerformanceFrequency() and
+               // QueryPerformanceCounter() functions always succeed on XP and later
+               QueryPerformanceFrequency(&li_frequency);
+
+               // The hires frequency can go as high as 4 GHz, so we'll use a conversion
+               // to picoseconds to compute the tv_nsecs part
+               hires_frequency = li_frequency.QuadPart;
+               hires_ticks_to_ps = UINT64_C(1000000000000) / hires_frequency;
        }
+
+       QueryPerformanceCounter(&hires_counter);
+       tp->tv_sec = (long)(hires_counter.QuadPart / hires_frequency);
+       tp->tv_nsec = (long)(((hires_counter.QuadPart % hires_frequency) * hires_ticks_to_ps) / UINT64_C(1000));
 }
-#endif
 
 // NB: MSVC6 does not support named initializers.
 const struct usbi_os_backend usbi_backend = {
@@ -901,6 +899,6 @@ const struct usbi_os_backend usbi_backend = {
        windows_handle_transfer_completion,
        sizeof(struct windows_context_priv),
        sizeof(union windows_device_priv),
-       sizeof(union windows_device_handle_priv),
+       sizeof(struct windows_device_handle_priv),
        sizeof(struct windows_transfer_priv),
 };