core: update usbi_dbg to take the context as an argument
[platform/upstream/libusb.git] / libusb / os / windows_common.c
index 119ed49..eefd01e 100644 (file)
@@ -24,7 +24,6 @@
 
 #include <config.h>
 
-#include <process.h>
 #include <stdio.h>
 
 #include "libusbi.h"
@@ -153,7 +152,7 @@ static bool htab_create(struct libusb_context *ctx)
        // 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.
@@ -222,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);
@@ -284,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;
        }
 }
@@ -294,15 +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), ULONG_CAST(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);
+       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 */
@@ -399,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;
 }
@@ -416,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;
@@ -435,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;
 }
@@ -450,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 concurrent 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?
@@ -496,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");
@@ -538,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 concurrent 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))
@@ -581,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)
@@ -597,7 +600,7 @@ static int windows_set_option(struct libusb_context *ctx, enum libusb_option opt
                        usbi_err(ctx, "UsbDk backend not available");
                        return LIBUSB_ERROR_NOT_FOUND;
                }
-               usbi_dbg("switching context %p to use UsbDk backend", ctx);
+               usbi_dbg(ctx, "switching context %p to use UsbDk backend", ctx);
                priv->backend = &usbdk_backend;
                return LIBUSB_SUCCESS;
        }
@@ -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:
@@ -881,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),
 };