#include <config.h>
-#include <errno.h>
-#include <process.h>
#include <stdio.h>
#include "libusbi.h"
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()
}
#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) */
// 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.
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);
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;
}
}
*/
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 */
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;
}
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;
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;
}
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?
goto init_exit;
}
- windows_init_clock();
-
if (!htab_create(ctx)) {
r = LIBUSB_ERROR_NO_MEM;
goto init_exit;
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");
--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))
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)
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)
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);
}
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;
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;
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) {
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:
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 = {
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),
};