#include <config.h>
-#include <inttypes.h>
-#include <process.h>
#include <stdio.h>
#include "libusbi.h"
#define EPOCH_TIME UINT64_C(116444736000000000) // 1970.01.01 00:00:000 in MS Filetime
+#define STATUS_SUCCESS ((ULONG_PTR)0UL)
+
// Public
enum windows_version windows_version = WINDOWS_UNDEFINED;
- // Global variables for init/exit
-static unsigned int init_count = 0;
-static bool usbdk_available = false;
-
-// Global variables for clock_gettime mechanism
-static uint64_t hires_ticks_to_ps;
-static uint64_t hires_frequency;
-
-#define TIMER_REQUEST_RETRY_MS 100
-#define WM_TIMER_REQUEST (WM_USER + 1)
-#define WM_TIMER_EXIT (WM_USER + 2)
-
-// used for monotonic clock_gettime()
-struct timer_request {
- struct timespec *tp;
- HANDLE event;
-};
-
-// Timer thread
-static HANDLE timer_thread = NULL;
-static DWORD timer_thread_id = 0;
-
-/* User32 dependencies */
-DLL_DECLARE_HANDLE(User32);
-DLL_DECLARE_FUNC_PREFIXED(WINAPI, BOOL, p, GetMessageA, (LPMSG, HWND, UINT, UINT));
-DLL_DECLARE_FUNC_PREFIXED(WINAPI, BOOL, p, PeekMessageA, (LPMSG, HWND, UINT, UINT, UINT));
-DLL_DECLARE_FUNC_PREFIXED(WINAPI, BOOL, p, PostThreadMessageA, (DWORD, UINT, WPARAM, LPARAM));
-
-static unsigned __stdcall windows_clock_gettime_threaded(void *param);
+// Global variables for init/exit
+static unsigned int init_count;
+static bool usbdk_available;
/*
* Converts a windows error to human readable string
if (error_code == 0)
error_code = GetLastError();
- len = sprintf(err_string, "[%u] ", (unsigned int)error_code);
+ len = sprintf(err_string, "[%lu] ", ULONG_CAST(error_code));
// Translate codes returned by SetupAPI. The ones we are dealing with are either
// in 0x0000xxxx or 0xE000xxxx and can be distinguished from standard error codes.
DWORD format_error = GetLastError();
if (format_error)
snprintf(err_string, sizeof(err_string),
- "Windows error code %u (FormatMessage error code %u)",
- (unsigned int)error_code, (unsigned int)format_error);
+ "Windows error code %lu (FormatMessage error code %lu)",
+ ULONG_CAST(error_code), ULONG_CAST(format_error));
else
- snprintf(err_string, sizeof(err_string), "Unknown error code %u", (unsigned int)error_code);
+ snprintf(err_string, sizeof(err_string), "Unknown error code %lu",
+ ULONG_CAST(error_code));
} else {
// Remove CRLF from end of message, if present
size_t pos = len + size - 2;
}
#endif
-static inline struct windows_context_priv *_context_priv(struct libusb_context *ctx)
+/*
+ * 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)
{
- return (struct windows_context_priv *)ctx->os_priv;
+ 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:
char *str;
} htab_entry;
-static htab_entry *htab_table = NULL;
+static htab_entry *htab_table;
static usbi_mutex_t htab_mutex;
static unsigned long htab_filled;
static bool htab_create(struct libusb_context *ctx)
{
if (htab_table != NULL) {
- usbi_err(ctx, "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.
{
unsigned long hval, hval2;
unsigned long idx;
- unsigned long r = 5381;
+ unsigned long r = 5381UL;
int c;
const char *sz = 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 = 1 + hval % (HTAB_SIZE - 2);
+ hval2 = 1UL + hval % (HTAB_SIZE - 2);
do {
// Because size is prime this guarantees to step through all available indexes
// If the table is full return an error
if (htab_filled >= HTAB_SIZE) {
usbi_err(NULL, "hash table is full (%lu entries)", HTAB_SIZE);
- idx = 0;
+ idx = 0UL;
goto out_unlock;
}
htab_table[idx].str = _strdup(str);
if (htab_table[idx].str == NULL) {
usbi_err(NULL, "could not duplicate string for hash table");
- idx = 0;
+ idx = 0UL;
goto out_unlock;
}
return idx;
}
-/*
-* Make a transfer complete synchronously
-*/
-void windows_force_sync_completion(OVERLAPPED *overlapped, ULONG size)
+enum libusb_transfer_status usbd_status_to_libusb_transfer_status(USBD_STATUS status)
{
- overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY;
- overlapped->InternalHigh = size;
- SetEvent(overlapped->hEvent);
-}
-
-static BOOL windows_init_dlls(void)
-{
- DLL_GET_HANDLE(User32);
- DLL_LOAD_FUNC_PREFIXED(User32, p, GetMessageA, TRUE);
- DLL_LOAD_FUNC_PREFIXED(User32, p, PeekMessageA, TRUE);
- DLL_LOAD_FUNC_PREFIXED(User32, p, PostThreadMessageA, TRUE);
-
- return TRUE;
-}
+ if (USBD_SUCCESS(status))
+ return LIBUSB_TRANSFER_COMPLETED;
-static void windows_exit_dlls(void)
-{
- DLL_FREE_HANDLE(User32);
+ switch (status) {
+ case USBD_STATUS_TIMEOUT:
+ return LIBUSB_TRANSFER_TIMED_OUT;
+ case USBD_STATUS_CANCELED:
+ return LIBUSB_TRANSFER_CANCELLED;
+ case USBD_STATUS_ENDPOINT_HALTED:
+ return LIBUSB_TRANSFER_STALL;
+ case USBD_STATUS_DEVICE_GONE:
+ return LIBUSB_TRANSFER_NO_DEVICE;
+ default:
+ usbi_dbg(NULL, "USBD_STATUS 0x%08lx translated to LIBUSB_TRANSFER_ERROR", ULONG_CAST(status));
+ return LIBUSB_TRANSFER_ERROR;
+ }
}
-static bool windows_init_clock(struct libusb_context *ctx)
+/*
+ * Make a transfer complete synchronously
+ */
+void windows_force_sync_completion(struct usbi_transfer *itransfer, ULONG size)
{
- DWORD_PTR affinity, dummy;
- HANDLE event;
- LARGE_INTEGER li_frequency;
- int i;
-
- if (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 available (Frequency: %"PRIu64" Hz)", hires_frequency);
-
- // Because QueryPerformanceCounter might report different values when
- // running on different cores, we create a separate thread for the timer
- // calls, which we glue to the first available core always to prevent timing discrepancies.
- if (!GetProcessAffinityMask(GetCurrentProcess(), &affinity, &dummy) || (affinity == 0)) {
- usbi_err(ctx, "could not get process affinity: %s", windows_error_str(0));
- return false;
- }
-
- // The process affinity mask is a bitmask where each set bit represents a core on
- // which this process is allowed to run, so we find the first set bit
- for (i = 0; !(affinity & (DWORD_PTR)(1 << i)); i++);
- affinity = (DWORD_PTR)(1 << i);
-
- usbi_dbg("timer thread will run on core #%d", i);
-
- event = CreateEvent(NULL, FALSE, FALSE, NULL);
- if (event == NULL) {
- usbi_err(ctx, "could not create event: %s", windows_error_str(0));
- return false;
- }
+ 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;
- timer_thread = (HANDLE)_beginthreadex(NULL, 0, windows_clock_gettime_threaded, (void *)event,
- 0, (unsigned int *)&timer_thread_id);
- if (timer_thread == NULL) {
- usbi_err(ctx, "unable to create timer thread - aborting");
- CloseHandle(event);
- return false;
- }
+ usbi_dbg(TRANSFER_CTX(transfer), "transfer %p, length %lu", transfer, ULONG_CAST(size));
- if (!SetThreadAffinityMask(timer_thread, affinity))
- usbi_warn(ctx, "unable to set timer thread affinity, timer discrepancies may arise");
+ overlapped->Internal = (ULONG_PTR)STATUS_SUCCESS;
+ overlapped->InternalHigh = (ULONG_PTR)size;
- // Wait for timer thread to init before continuing.
- if (WaitForSingleObject(event, INFINITE) != WAIT_OBJECT_0) {
- usbi_err(ctx, "failed to wait for timer thread to become ready - aborting");
- CloseHandle(event);
- return false;
- }
-
- CloseHandle(event);
- } else {
- usbi_dbg("no hires timer available on this platform");
- hires_frequency = 0;
- hires_ticks_to_ps = UINT64_C(0);
- }
-
- return true;
-}
-
-static void windows_destroy_clock(void)
-{
- if (timer_thread) {
- // actually the signal to quit the thread.
- if (!pPostThreadMessageA(timer_thread_id, WM_TIMER_EXIT, 0, 0)
- || (WaitForSingleObject(timer_thread, INFINITE) != WAIT_OBJECT_0)) {
- usbi_dbg("could not wait for timer thread to quit");
- TerminateThread(timer_thread, 1);
- // shouldn't happen, but we're destroying
- // all objects it might have held anyway.
- }
- CloseHandle(timer_thread);
- timer_thread = NULL;
- timer_thread_id = 0;
- }
+ 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 */
return ret;
}
-static void get_windows_version(void)
+static enum windows_version get_windows_version(void)
{
+ enum windows_version winver;
OSVERSIONINFOEXA vi, vi2;
- const char *arch, *w = NULL;
unsigned major, minor, version;
ULONGLONG major_equal, minor_equal;
- BOOL ws;
-
- windows_version = WINDOWS_UNDEFINED;
+ const char *w, *arch;
+ bool ws;
memset(&vi, 0, sizeof(vi));
vi.dwOSVersionInfoSize = sizeof(vi);
memset(&vi, 0, sizeof(vi));
vi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
if (!GetVersionExA((OSVERSIONINFOA *)&vi))
- return;
+ return WINDOWS_UNDEFINED;
}
if (vi.dwPlatformId != VER_PLATFORM_WIN32_NT)
- return;
+ return WINDOWS_UNDEFINED;
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
}
if ((vi.dwMajorVersion > 0xf) || (vi.dwMinorVersion > 0xf))
- return;
+ return WINDOWS_UNDEFINED;
ws = (vi.wProductType <= VER_NT_WORKSTATION);
version = vi.dwMajorVersion << 4 | vi.dwMinorVersion;
switch (version) {
- case 0x50: windows_version = WINDOWS_2000; w = "2000"; break;
- case 0x51: windows_version = WINDOWS_XP; w = "XP"; break;
- case 0x52: windows_version = WINDOWS_2003; w = "2003"; break;
- case 0x60: windows_version = WINDOWS_VISTA; w = (ws ? "Vista" : "2008"); break;
- case 0x61: windows_version = WINDOWS_7; w = (ws ? "7" : "2008_R2"); break;
- case 0x62: windows_version = WINDOWS_8; w = (ws ? "8" : "2012"); break;
- case 0x63: windows_version = WINDOWS_8_1; w = (ws ? "8.1" : "2012_R2"); break;
+ case 0x50: winver = WINDOWS_2000; w = "2000"; break;
+ case 0x51: winver = WINDOWS_XP; w = "XP"; break;
+ case 0x52: winver = WINDOWS_2003; w = "2003"; break;
+ case 0x60: winver = WINDOWS_VISTA; w = (ws ? "Vista" : "2008"); break;
+ case 0x61: winver = WINDOWS_7; w = (ws ? "7" : "2008_R2"); break;
+ 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: windows_version = WINDOWS_10; w = (ws ? "10" : "2016"); break;
+ case 0xA0: winver = WINDOWS_10; w = (ws ? "10" : "2016"); break;
default:
- if (version < 0x50) {
- return;
- } else {
- windows_version = WINDOWS_11_OR_LATER;
- w = "11 or later";
- }
+ if (version < 0x50)
+ return WINDOWS_UNDEFINED;
+ winver = WINDOWS_11_OR_LATER;
+ w = "11 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;
}
-/*
-* Monotonic and real time functions
-*/
-static unsigned __stdcall windows_clock_gettime_threaded(void *param)
+static unsigned __stdcall windows_iocp_thread(void *arg)
{
- struct timer_request *request;
- LARGE_INTEGER hires_counter;
- MSG msg;
-
- // The following call will create this thread's message queue
- // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms644946.aspx
- pPeekMessageA(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
-
- // Signal windows_init_clock() that we're ready to service requests
- if (!SetEvent((HANDLE)param))
- usbi_dbg("SetEvent failed for timer init event: %s", windows_error_str(0));
- param = NULL;
-
- // Main loop - wait for requests
- while (1) {
- if (pGetMessageA(&msg, NULL, WM_TIMER_REQUEST, WM_TIMER_EXIT) == -1) {
- usbi_err(NULL, "GetMessage failed for timer thread: %s", windows_error_str(0));
- return 1;
+ struct libusb_context *ctx = arg;
+ struct windows_context_priv *priv = usbi_get_context_priv(ctx);
+ HANDLE iocp = priv->completion_port;
+ 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(ctx, "I/O completion thread started");
+
+ while (true) {
+ overlapped = NULL;
+ if (!GetQueuedCompletionStatus(iocp, &num_bytes, &completion_key, &overlapped, INFINITE) && (overlapped == NULL)) {
+ usbi_err(ctx, "GetQueuedCompletionStatus failed: %s", windows_error_str(0));
+ break;
}
- switch (msg.message) {
- case WM_TIMER_REQUEST:
- // Requests to this thread are for hires always
- // Microsoft says that this function always succeeds on XP and later
- // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms644904.aspx
- request = (struct timer_request *)msg.lParam;
- QueryPerformanceCounter(&hires_counter);
- request->tp->tv_sec = (long)(hires_counter.QuadPart / hires_frequency);
- request->tp->tv_nsec = (long)(((hires_counter.QuadPart % hires_frequency) / 1000) * hires_ticks_to_ps);
- if (!SetEvent(request->event))
- usbi_err(NULL, "SetEvent failed for timer request: %s", windows_error_str(0));
+ if (overlapped == NULL) {
+ // Signal to quit
+ if (completion_key != (ULONG_PTR)ctx)
+ usbi_err(ctx, "program assertion failed - overlapped is NULL");
break;
- case WM_TIMER_EXIT:
- usbi_dbg("timer thread quitting");
- return 0;
}
- }
-}
-static void windows_transfer_callback(const struct windows_backend *backend,
- struct usbi_transfer *itransfer, DWORD io_result, DWORD io_size)
-{
- int status, istatus;
+ // 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;
- usbi_dbg("handling I/O completion with errcode %u, size %u", (unsigned int)io_result, (unsigned int)io_size);
+ 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);
- switch (io_result) {
- case NO_ERROR:
- status = backend->copy_transfer_data(itransfer, (uint32_t)io_size);
- break;
- case ERROR_GEN_FAILURE:
- usbi_dbg("detected endpoint stall");
- status = LIBUSB_TRANSFER_STALL;
- break;
- case ERROR_SEM_TIMEOUT:
- usbi_dbg("detected semaphore timeout");
- status = LIBUSB_TRANSFER_TIMED_OUT;
- break;
- case ERROR_OPERATION_ABORTED:
- istatus = backend->copy_transfer_data(itransfer, (uint32_t)io_size);
- if (istatus != LIBUSB_TRANSFER_COMPLETED)
- usbi_dbg("Failed to copy partial data in aborted operation: %d", istatus);
+ 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;
+ }
- usbi_dbg("detected operation aborted");
- status = LIBUSB_TRANSFER_CANCELLED;
- break;
- case ERROR_FILE_NOT_FOUND:
- case ERROR_DEVICE_NOT_CONNECTED:
- usbi_dbg("detected device removed");
- status = LIBUSB_TRANSFER_NO_DEVICE;
- break;
- default:
- usbi_err(ITRANSFER_CTX(itransfer), "detected I/O error %u: %s", (unsigned int)io_result, windows_error_str(io_result));
- status = LIBUSB_TRANSFER_ERROR;
- break;
+ itransfer = (struct usbi_transfer *)((unsigned char *)transfer_priv + PTR_ALIGN(sizeof(*transfer_priv)));
+ usbi_dbg(ctx, "transfer %p completed, length %lu",
+ USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer), ULONG_CAST(num_bytes));
+ usbi_signal_transfer_completion(itransfer);
}
- backend->clear_transfer_priv(itransfer); // Cancel polling
- if (status == LIBUSB_TRANSFER_CANCELLED)
- usbi_handle_transfer_cancellation(itransfer);
- else
- usbi_handle_transfer_completion(itransfer, (enum libusb_transfer_status)status);
-}
-static void windows_handle_callback(const struct windows_backend *backend,
- struct usbi_transfer *itransfer, DWORD io_result, DWORD io_size)
-{
- struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ usbi_dbg(ctx, "I/O completion thread exiting");
- switch (transfer->type) {
- case LIBUSB_TRANSFER_TYPE_CONTROL:
- case LIBUSB_TRANSFER_TYPE_BULK:
- case LIBUSB_TRANSFER_TYPE_INTERRUPT:
- case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
- windows_transfer_callback(backend, itransfer, io_result, io_size);
- break;
- case LIBUSB_TRANSFER_TYPE_BULK_STREAM:
- usbi_warn(ITRANSFER_CTX(itransfer), "bulk stream transfers are not yet supported on this platform");
- break;
- default:
- usbi_err(ITRANSFER_CTX(itransfer), "unknown endpoint type %d", transfer->type);
- }
+ return 0;
}
static int windows_init(struct libusb_context *ctx)
{
- struct windows_context_priv *priv = _context_priv(ctx);
- HANDLE semaphore;
- char sem_name[11 + 8 + 1]; // strlen("libusb_init") + (32-bit hex PID) + '\0'
- int r = LIBUSB_ERROR_OTHER;
+ struct windows_context_priv *priv = usbi_get_context_priv(ctx);
bool winusb_backend_init = false;
-
- sprintf(sem_name, "libusb_init%08X", (unsigned int)(GetCurrentProcessId() & 0xFFFFFFFF));
- semaphore = CreateSemaphoreA(NULL, 1, 1, sem_name);
- if (semaphore == NULL) {
- usbi_err(ctx, "could not create semaphore: %s", windows_error_str(0));
- return LIBUSB_ERROR_NO_MEM;
- }
-
- // A successful wait brings our semaphore count to 0 (unsignaled)
- // => any concurent wait stalls until the semaphore's release
- if (WaitForSingleObject(semaphore, INFINITE) != WAIT_OBJECT_0) {
- usbi_err(ctx, "failure to access semaphore: %s", windows_error_str(0));
- CloseHandle(semaphore);
- return LIBUSB_ERROR_NO_MEM;
- }
+ int r;
// 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?
- // Load DLL imports
- if (!windows_init_dlls()) {
- usbi_err(ctx, "could not resolve DLL functions");
- goto init_exit;
- }
-
- get_windows_version();
-
+ windows_version = get_windows_version();
if (windows_version == WINDOWS_UNDEFINED) {
usbi_err(ctx, "failed to detect Windows version");
r = LIBUSB_ERROR_NOT_SUPPORTED;
goto init_exit;
- }
-
- if (!windows_init_clock(ctx))
+ } else if (windows_version < WINDOWS_VISTA) {
+ usbi_err(ctx, "Windows version is too old");
+ r = LIBUSB_ERROR_NOT_SUPPORTED;
goto init_exit;
+ }
- if (!htab_create(ctx))
+ if (!htab_create(ctx)) {
+ r = LIBUSB_ERROR_NO_MEM;
goto init_exit;
+ }
r = winusb_backend.init(ctx);
if (r != LIBUSB_SUCCESS)
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");
// Do not report this as an error
- r = LIBUSB_SUCCESS;
}
}
// By default, new contexts will use the WinUSB backend
priv->backend = &winusb_backend;
+ r = LIBUSB_ERROR_NO_MEM;
+
+ // Use an I/O completion port to manage all transfers for this context
+ priv->completion_port = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1);
+ if (priv->completion_port == NULL) {
+ usbi_err(ctx, "failed to create I/O completion port: %s", windows_error_str(0));
+ goto init_exit;
+ }
+
+ // And a dedicated thread to wait for I/O completions
+ priv->completion_port_thread = (HANDLE)_beginthreadex(NULL, 0, windows_iocp_thread, ctx, 0, NULL);
+ if (priv->completion_port_thread == NULL) {
+ usbi_err(ctx, "failed to create I/O completion port thread");
+ CloseHandle(priv->completion_port);
+ goto init_exit;
+ }
+
r = LIBUSB_SUCCESS;
init_exit: // Holds semaphore here
if ((init_count == 1) && (r != LIBUSB_SUCCESS)) { // First init failed?
+ if (usbdk_available) {
+ usbdk_backend.exit(ctx);
+ usbdk_available = false;
+ }
if (winusb_backend_init)
winusb_backend.exit(ctx);
htab_destroy();
- windows_destroy_clock();
- windows_exit_dlls();
--init_count;
}
- ReleaseSemaphore(semaphore, 1, NULL); // increase count back to 1
- CloseHandle(semaphore);
return r;
}
static void windows_exit(struct libusb_context *ctx)
{
- HANDLE semaphore;
- char sem_name[11 + 8 + 1]; // strlen("libusb_init") + (32-bit hex PID) + '\0'
- UNUSED(ctx);
+ struct windows_context_priv *priv = usbi_get_context_priv(ctx);
- sprintf(sem_name, "libusb_init%08X", (unsigned int)(GetCurrentProcessId() & 0xFFFFFFFF));
- semaphore = CreateSemaphoreA(NULL, 1, 1, sem_name);
- if (semaphore == NULL)
- 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))
+ usbi_err(ctx, "failed to post I/O completion: %s", windows_error_str(0));
- // A successful wait brings our semaphore count to 0 (unsignaled)
- // => any concurent wait stalls until the semaphore release
- if (WaitForSingleObject(semaphore, INFINITE) != WAIT_OBJECT_0) {
- CloseHandle(semaphore);
- return;
- }
+ if (WaitForSingleObject(priv->completion_port_thread, INFINITE) == WAIT_FAILED)
+ usbi_err(ctx, "failed to wait for I/O completion port thread: %s", windows_error_str(0));
+
+ CloseHandle(priv->completion_port_thread);
+ CloseHandle(priv->completion_port);
// Only works if exits and inits are balanced exactly
if (--init_count == 0) { // Last exit
}
winusb_backend.exit(ctx);
htab_destroy();
- windows_destroy_clock();
- windows_exit_dlls();
}
-
- ReleaseSemaphore(semaphore, 1, NULL); // increase count back to 1
- CloseHandle(semaphore);
}
static int windows_set_option(struct libusb_context *ctx, enum libusb_option option, va_list ap)
{
- struct windows_context_priv *priv = _context_priv(ctx);
+ struct windows_context_priv *priv = usbi_get_context_priv(ctx);
UNUSED(ap);
- switch (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)
{
- struct windows_context_priv *priv = _context_priv(ctx);
+ struct windows_context_priv *priv = usbi_get_context_priv(ctx);
return priv->backend->get_device_list(ctx, discdevs);
}
static int windows_open(struct libusb_device_handle *dev_handle)
{
- struct windows_context_priv *priv = _context_priv(HANDLE_CTX(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);
}
static void windows_close(struct libusb_device_handle *dev_handle)
{
- struct windows_context_priv *priv = _context_priv(HANDLE_CTX(dev_handle));
+ struct windows_context_priv *priv = usbi_get_context_priv(HANDLE_CTX(dev_handle));
priv->backend->close(dev_handle);
}
-static int windows_get_device_descriptor(struct libusb_device *dev,
- unsigned char *buffer, int *host_endian)
-{
- struct windows_context_priv *priv = _context_priv(DEVICE_CTX(dev));
- *host_endian = 0;
- return priv->backend->get_device_descriptor(dev, buffer);
-}
-
static int windows_get_active_config_descriptor(struct libusb_device *dev,
- unsigned char *buffer, size_t len, int *host_endian)
+ void *buffer, size_t len)
{
- struct windows_context_priv *priv = _context_priv(DEVICE_CTX(dev));
- *host_endian = 0;
+ struct windows_context_priv *priv = usbi_get_context_priv(DEVICE_CTX(dev));
return priv->backend->get_active_config_descriptor(dev, buffer, len);
}
static int windows_get_config_descriptor(struct libusb_device *dev,
- uint8_t config_index, unsigned char *buffer, size_t len, int *host_endian)
+ uint8_t config_index, void *buffer, size_t len)
{
- struct windows_context_priv *priv = _context_priv(DEVICE_CTX(dev));
- *host_endian = 0;
+ struct windows_context_priv *priv = usbi_get_context_priv(DEVICE_CTX(dev));
return priv->backend->get_config_descriptor(dev, config_index, buffer, len);
}
static int windows_get_config_descriptor_by_value(struct libusb_device *dev,
- uint8_t bConfigurationValue, unsigned char **buffer, int *host_endian)
+ uint8_t bConfigurationValue, void **buffer)
{
- struct windows_context_priv *priv = _context_priv(DEVICE_CTX(dev));
- *host_endian = 0;
+ struct windows_context_priv *priv = usbi_get_context_priv(DEVICE_CTX(dev));
return priv->backend->get_config_descriptor_by_value(dev, bConfigurationValue, buffer);
}
-static int windows_get_configuration(struct libusb_device_handle *dev_handle, int *config)
+static int windows_get_configuration(struct libusb_device_handle *dev_handle, uint8_t *config)
{
- struct windows_context_priv *priv = _context_priv(HANDLE_CTX(dev_handle));
+ struct windows_context_priv *priv = usbi_get_context_priv(HANDLE_CTX(dev_handle));
return priv->backend->get_configuration(dev_handle, config);
}
static int windows_set_configuration(struct libusb_device_handle *dev_handle, int config)
{
- struct windows_context_priv *priv = _context_priv(HANDLE_CTX(dev_handle));
- return priv->backend->set_configuration(dev_handle, config);
+ struct windows_context_priv *priv = usbi_get_context_priv(HANDLE_CTX(dev_handle));
+ if (config == -1)
+ config = 0;
+ return priv->backend->set_configuration(dev_handle, (uint8_t)config);
}
-static int windows_claim_interface(struct libusb_device_handle *dev_handle, int interface_number)
+static int windows_claim_interface(struct libusb_device_handle *dev_handle, uint8_t interface_number)
{
- struct windows_context_priv *priv = _context_priv(HANDLE_CTX(dev_handle));
+ struct windows_context_priv *priv = usbi_get_context_priv(HANDLE_CTX(dev_handle));
return priv->backend->claim_interface(dev_handle, interface_number);
}
-static int windows_release_interface(struct libusb_device_handle *dev_handle, int interface_number)
+static int windows_release_interface(struct libusb_device_handle *dev_handle, uint8_t interface_number)
{
- struct windows_context_priv *priv = _context_priv(HANDLE_CTX(dev_handle));
+ struct windows_context_priv *priv = usbi_get_context_priv(HANDLE_CTX(dev_handle));
return priv->backend->release_interface(dev_handle, interface_number);
}
static int windows_set_interface_altsetting(struct libusb_device_handle *dev_handle,
- int interface_number, int altsetting)
+ uint8_t interface_number, uint8_t altsetting)
{
- struct windows_context_priv *priv = _context_priv(HANDLE_CTX(dev_handle));
+ struct windows_context_priv *priv = usbi_get_context_priv(HANDLE_CTX(dev_handle));
return priv->backend->set_interface_altsetting(dev_handle, interface_number, altsetting);
}
static int windows_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint)
{
- struct windows_context_priv *priv = _context_priv(HANDLE_CTX(dev_handle));
+ struct windows_context_priv *priv = usbi_get_context_priv(HANDLE_CTX(dev_handle));
return priv->backend->clear_halt(dev_handle, endpoint);
}
static int windows_reset_device(struct libusb_device_handle *dev_handle)
{
- struct windows_context_priv *priv = _context_priv(HANDLE_CTX(dev_handle));
+ struct windows_context_priv *priv = usbi_get_context_priv(HANDLE_CTX(dev_handle));
return priv->backend->reset_device(dev_handle);
}
static void windows_destroy_device(struct libusb_device *dev)
{
- struct windows_context_priv *priv = _context_priv(DEVICE_CTX(dev));
+ struct windows_context_priv *priv = usbi_get_context_priv(DEVICE_CTX(dev));
priv->backend->destroy_device(dev);
}
static int windows_submit_transfer(struct usbi_transfer *itransfer)
{
- struct windows_context_priv *priv = _context_priv(ITRANSFER_CTX(itransfer));
- return priv->backend->submit_transfer(itransfer);
-}
+ struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+ 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;
-static int windows_cancel_transfer(struct usbi_transfer *itransfer)
-{
- struct windows_context_priv *priv = _context_priv(ITRANSFER_CTX(itransfer));
- return priv->backend->cancel_transfer(itransfer);
-}
+ switch (transfer->type) {
+ case LIBUSB_TRANSFER_TYPE_CONTROL:
+ case LIBUSB_TRANSFER_TYPE_BULK:
+ case LIBUSB_TRANSFER_TYPE_INTERRUPT:
+ case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
+ break;
+ case LIBUSB_TRANSFER_TYPE_BULK_STREAM:
+ usbi_warn(ctx, "bulk stream transfers are not yet supported on this platform");
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+ default:
+ usbi_err(ctx, "unknown endpoint type %d", transfer->type);
+ return LIBUSB_ERROR_INVALID_PARAM;
+ }
-static void windows_clear_transfer_priv(struct usbi_transfer *itransfer)
-{
- struct windows_context_priv *priv = _context_priv(ITRANSFER_CTX(itransfer));
- priv->backend->clear_transfer_priv(itransfer);
+ if (transfer_priv->handle != NULL) {
+ usbi_err(ctx, "program assertion failed - transfer HANDLE is not NULL");
+ 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;
+ return r;
+ }
+
+ // The backend should set the HANDLE used for each submitted transfer
+ // by calling set_transfer_priv_handle()
+ if (transfer_priv->handle == NULL)
+ usbi_err(ctx, "program assertion failed - transfer HANDLE is NULL after transfer was submitted");
+
+ return r;
}
-static int windows_handle_events(struct libusb_context *ctx, struct pollfd *fds, POLL_NFDS_TYPE nfds, int num_ready)
+static int windows_cancel_transfer(struct usbi_transfer *itransfer)
{
- struct windows_context_priv *priv = _context_priv(ctx);
- struct usbi_transfer *itransfer;
- DWORD io_size, io_result;
- POLL_NFDS_TYPE i;
- bool found;
- int transfer_fd;
- int r = LIBUSB_SUCCESS;
+ struct windows_context_priv *priv = usbi_get_context_priv(ITRANSFER_CTX(itransfer));
+ struct windows_transfer_priv *transfer_priv = usbi_get_transfer_priv(itransfer);
- usbi_mutex_lock(&ctx->open_devs_lock);
- for (i = 0; i < nfds && num_ready > 0; i++) {
+ // Try CancelIoEx() on the transfer
+ // If that fails, fall back to the backend's cancel_transfer()
+ // function if it is available
+ if (CancelIoEx(transfer_priv->handle, &transfer_priv->overlapped))
+ return LIBUSB_SUCCESS;
+ else if (GetLastError() == ERROR_NOT_FOUND)
+ return LIBUSB_ERROR_NOT_FOUND;
- usbi_dbg("checking fd %d with revents = %04x", fds[i].fd, fds[i].revents);
+ if (priv->backend->cancel_transfer)
+ return priv->backend->cancel_transfer(itransfer);
- if (!fds[i].revents)
- continue;
+ usbi_warn(ITRANSFER_CTX(itransfer), "cancellation not supported for this transfer's driver");
+ return LIBUSB_ERROR_NOT_SUPPORTED;
+}
- num_ready--;
+static int windows_handle_transfer_completion(struct usbi_transfer *itransfer)
+{
+ struct libusb_context *ctx = ITRANSFER_CTX(itransfer);
+ struct windows_context_priv *priv = usbi_get_context_priv(ctx);
+ const struct windows_backend *backend = priv->backend;
+ struct windows_transfer_priv *transfer_priv = usbi_get_transfer_priv(itransfer);
+ enum libusb_transfer_status status, istatus;
+ DWORD result, bytes_transferred;
- // Because a Windows OVERLAPPED is used for poll emulation,
- // a pollable fd is created and stored with each transfer
- found = false;
- transfer_fd = -1;
- usbi_mutex_lock(&ctx->flying_transfers_lock);
- list_for_each_entry(itransfer, &ctx->flying_transfers, list, struct usbi_transfer) {
- transfer_fd = priv->backend->get_transfer_fd(itransfer);
- if (transfer_fd == fds[i].fd) {
- found = true;
- break;
- }
- }
- usbi_mutex_unlock(&ctx->flying_transfers_lock);
+ if (GetOverlappedResult(transfer_priv->handle, &transfer_priv->overlapped, &bytes_transferred, FALSE))
+ result = NO_ERROR;
+ else
+ result = GetLastError();
- if (found) {
- priv->backend->get_overlapped_result(itransfer, &io_result, &io_size);
+ 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));
- usbi_remove_pollfd(ctx, transfer_fd);
+ switch (result) {
+ case NO_ERROR:
+ status = backend->copy_transfer_data(itransfer, bytes_transferred);
+ break;
+ case ERROR_GEN_FAILURE:
+ usbi_dbg(ctx, "detected endpoint stall");
+ status = LIBUSB_TRANSFER_STALL;
+ break;
+ case ERROR_SEM_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(ctx, "failed to copy partial data in aborted operation: %d", (int)istatus);
- // let handle_callback free the event using the transfer wfd
- // If you don't use the transfer wfd, you run a risk of trying to free a
- // newly allocated wfd that took the place of the one from the transfer.
- windows_handle_callback(priv->backend, itransfer, io_result, io_size);
- } else {
- usbi_err(ctx, "could not find a matching transfer for fd %d", fds[i].fd);
- r = LIBUSB_ERROR_NOT_FOUND;
- break;
- }
+ 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(ctx, "detected device removed");
+ status = LIBUSB_TRANSFER_NO_DEVICE;
+ break;
+ default:
+ usbi_err(ctx, "detected I/O error %lu: %s",
+ ULONG_CAST(result), windows_error_str(result));
+ status = LIBUSB_TRANSFER_ERROR;
+ break;
}
- usbi_mutex_unlock(&ctx->open_devs_lock);
- return r;
+ transfer_priv->handle = NULL;
+
+ // Backend-specific cleanup
+ backend->clear_transfer_priv(itransfer);
+
+ if (status == LIBUSB_TRANSFER_CANCELLED)
+ return usbi_handle_transfer_cancellation(itransfer);
+ else
+ return usbi_handle_transfer_completion(itransfer, status);
}
-static int windows_clock_gettime(int clk_id, struct timespec *tp)
+void usbi_get_monotonic_time(struct timespec *tp)
{
- struct timer_request request;
-#if !defined(_MSC_VER) || (_MSC_VER < 1900)
- FILETIME filetime;
- ULARGE_INTEGER rtime;
-#endif
- DWORD r;
-
- switch (clk_id) {
- case USBI_CLOCK_MONOTONIC:
- if (timer_thread) {
- request.tp = tp;
- request.event = CreateEvent(NULL, FALSE, FALSE, NULL);
- if (request.event == NULL)
- return LIBUSB_ERROR_NO_MEM;
-
- if (!pPostThreadMessageA(timer_thread_id, WM_TIMER_REQUEST, 0, (LPARAM)&request)) {
- usbi_err(NULL, "PostThreadMessage failed for timer thread: %s", windows_error_str(0));
- CloseHandle(request.event);
- return LIBUSB_ERROR_OTHER;
- }
+ static LONG hires_counter_init;
+ static uint64_t hires_ticks_to_ps;
+ static uint64_t hires_frequency;
+ LARGE_INTEGER hires_counter;
- do {
- r = WaitForSingleObject(request.event, TIMER_REQUEST_RETRY_MS);
- if (r == WAIT_TIMEOUT)
- usbi_dbg("could not obtain a timer value within reasonable timeframe - too much load?");
- else if (r == WAIT_FAILED)
- usbi_err(NULL, "WaitForSingleObject failed: %s", windows_error_str(0));
- } while (r == WAIT_TIMEOUT);
- CloseHandle(request.event);
-
- if (r == WAIT_OBJECT_0)
- return LIBUSB_SUCCESS;
- else
- return LIBUSB_ERROR_OTHER;
- }
- // Fall through and return real-time if monotonic was not detected @ timer init
- case USBI_CLOCK_REALTIME:
-#if defined(_MSC_VER) && (_MSC_VER >= 1900)
- timespec_get(tp, TIME_UTC);
-#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 LIBUSB_SUCCESS;
- default:
- return LIBUSB_ERROR_INVALID_PARAM;
+ 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));
}
// NB: MSVC6 does not support named initializers.
NULL, /* wrap_sys_device */
windows_open,
windows_close,
- windows_get_device_descriptor,
windows_get_active_config_descriptor,
windows_get_config_descriptor,
windows_get_config_descriptor_by_value,
windows_destroy_device,
windows_submit_transfer,
windows_cancel_transfer,
- windows_clear_transfer_priv,
- windows_handle_events,
- NULL, /* handle_transfer_completion */
- windows_clock_gettime,
+ NULL, /* clear_transfer_priv */
+ NULL, /* handle_events */
+ windows_handle_transfer_completion,
sizeof(struct windows_context_priv),
sizeof(union windows_device_priv),
- sizeof(union windows_device_handle_priv),
- sizeof(union windows_transfer_priv),
+ sizeof(struct windows_device_handle_priv),
+ sizeof(struct windows_transfer_priv),
};