aboutsummaryrefslogtreecommitdiff
path: root/libusb/os/windows_common.c
diff options
context:
space:
mode:
Diffstat (limited to 'libusb/os/windows_common.c')
-rw-r--r--libusb/os/windows_common.c155
1 files changed, 92 insertions, 63 deletions
diff --git a/libusb/os/windows_common.c b/libusb/os/windows_common.c
index 119ed49..24ac095 100644
--- a/libusb/os/windows_common.c
+++ b/libusb/os/windows_common.c
@@ -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 */
@@ -344,6 +346,8 @@ static enum windows_version get_windows_version(void)
if ((vi.dwMajorVersion > 6) || ((vi.dwMajorVersion == 6) && (vi.dwMinorVersion >= 2))) {
// Starting with Windows 8.1 Preview, GetVersionEx() does no longer report the actual OS version
// See: http://msdn.microsoft.com/en-us/library/windows/desktop/dn302074.aspx
+ // And starting with Windows 10 Preview 2, Windows enforces the use of the application/supportedOS
+ // manifest in order for VerSetConditionMask() to report the ACTUAL OS major and minor...
major_equal = VerSetConditionMask(0, VER_MAJORVERSION, VER_EQUAL);
for (major = vi.dwMajorVersion; major <= 9; major++) {
@@ -379,6 +383,7 @@ static enum windows_version get_windows_version(void)
ws = (vi.wProductType <= VER_NT_WORKSTATION);
version = vi.dwMajorVersion << 4 | vi.dwMinorVersion;
+
switch (version) {
case 0x50: winver = WINDOWS_2000; w = "2000"; break;
case 0x51: winver = WINDOWS_XP; w = "XP"; break;
@@ -388,22 +393,30 @@ static enum windows_version get_windows_version(void)
case 0x62: winver = WINDOWS_8; w = (ws ? "8" : "2012"); break;
case 0x63: winver = WINDOWS_8_1; w = (ws ? "8.1" : "2012_R2"); break;
case 0x64: // Early Windows 10 Insider Previews and Windows Server 2017 Technical Preview 1 used version 6.4
- case 0xA0: winver = WINDOWS_10; w = (ws ? "10" : "2016"); break;
+ case 0xA0: winver = WINDOWS_10; w = (ws ? "10" : "2016");
+ if (vi.dwBuildNumber < 20000)
+ break;
+ // fallthrough
+ case 0xB0: winver = WINDOWS_11; w = (ws ? "11" : "2022"); break;
default:
if (version < 0x50)
return WINDOWS_UNDEFINED;
- winver = WINDOWS_11_OR_LATER;
- w = "11 or later";
+ winver = WINDOWS_12_OR_LATER;
+ w = "12 or later";
}
+ // We cannot tell if we are on 8, 10, or 11 without "app manifest"
+ if (version == 0x62 && vi.dwBuildNumber == 9200)
+ w = "8 (or later)";
+
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 +429,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 +452,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 +501,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 +530,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 +572,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 +598,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 +611,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 +628,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 +715,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 +741,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 +801,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 +809,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 +910,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),
};