diff options
author | Dmitry Fleytman <dfleytma@redhat.com> | 2016-01-20 17:05:52 +0200 |
---|---|---|
committer | Chris Dickens <christopher.a.dickens@gmail.com> | 2016-01-23 22:58:10 -0800 |
commit | 92a327815bb46a2dd0b38782873df2e54ff13b7c (patch) | |
tree | 64f3c9ba5ec4925c2ed799afc945bd47d2daaa49 | |
parent | ba73390b74cd6af292017b88e3a95fb5b8223c85 (diff) | |
download | libusb-92a327815bb46a2dd0b38782873df2e54ff13b7c.tar.gz |
windows: Move common definitions to a separate file
New files windows_nt_common.h/c introduced.
Signed-off-by: Dmitry Fleytman <dfleytma@redhat.com>
Signed-off-by: Chris Dickens <christopher.a.dickens@gmail.com>
-rw-r--r-- | libusb/Makefile.am | 6 | ||||
-rw-r--r-- | libusb/os/windows_nt_common.c | 616 | ||||
-rw-r--r-- | libusb/os/windows_nt_common.h | 58 | ||||
-rw-r--r-- | libusb/os/windows_usb.c | 547 | ||||
-rw-r--r-- | libusb/os/windows_usb.h | 28 | ||||
-rw-r--r-- | libusb/version_nano.h | 2 | ||||
-rw-r--r-- | msvc/libusb_dll_2013.vcxproj | 2 | ||||
-rw-r--r-- | msvc/libusb_dll_2015.vcxproj | 2 | ||||
-rw-r--r-- | msvc/libusb_sources | 1 | ||||
-rw-r--r-- | msvc/libusb_static_2013.vcxproj | 2 | ||||
-rw-r--r-- | msvc/libusb_static_2015.vcxproj | 2 |
11 files changed, 711 insertions, 555 deletions
diff --git a/libusb/Makefile.am b/libusb/Makefile.am index 0223400..bd495ce 100644 --- a/libusb/Makefile.am +++ b/libusb/Makefile.am @@ -12,7 +12,11 @@ LINUX_USBFS_SRC = os/linux_usbfs.h os/linux_usbfs.c DARWIN_USB_SRC = os/darwin_usb.h os/darwin_usb.c OPENBSD_USB_SRC = os/openbsd_usb.c NETBSD_USB_SRC = os/netbsd_usb.c -WINDOWS_USB_SRC = os/windows_common.h os/windows_usb.h os/windows_usb.c libusb-1.0.rc libusb-1.0.def +COMMON_WINDOWS_SRC = os/windows_nt_common.h \ + os/windows_nt_common.c \ + os/windows_common.h \ + libusb-1.0.rc libusb-1.0.def +WINDOWS_USB_SRC = os/windows_usb.h os/windows_usb.c $(COMMON_WINDOWS_SRC) WINCE_USB_SRC = os/wince_usb.h os/wince_usb.c HAIKU_USB_SRC = os/haiku_usb.h os/haiku_usb_backend.cpp \ os/haiku_usb_raw.h os/haiku_usb_raw.cpp os/haiku_pollfs.cpp diff --git a/libusb/os/windows_nt_common.c b/libusb/os/windows_nt_common.c new file mode 100644 index 0000000..d99e3b8 --- /dev/null +++ b/libusb/os/windows_nt_common.c @@ -0,0 +1,616 @@ +/* + * windows backend for libusb 1.0 + * Copyright © 2009-2012 Pete Batard <pete@akeo.ie> + * With contributions from Michael Plante, Orin Eman et al. + * Parts of this code adapted from libusb-win32-v1 by Stephan Meyer + * HID Reports IOCTLs inspired from HIDAPI by Alan Ott, Signal 11 Software + * Hash table functions adapted from glibc, by Ulrich Drepper et al. + * Major code testing contribution by Xiaofan Chen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <config.h> +#include <stdio.h> +#include <inttypes.h> +#include <process.h> + + +#include "libusbi.h" +#include "windows_common.h" +#include "windows_nt_common.h" + +// Global variables +const uint64_t epoch_time = UINT64_C(116444736000000000); // 1970.01.01 00:00:000 in MS Filetime + +// Global variables for clock_gettime mechanism +uint64_t hires_ticks_to_ps; +uint64_t hires_frequency; + +#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 +HANDLE timer_thread = NULL; +DWORD timer_thread_id = 0; + +/* User32 dependencies */ +DLL_DECLARE_PREFIXED(WINAPI, BOOL, p, GetMessageA, (LPMSG, HWND, UINT, UINT)); +DLL_DECLARE_PREFIXED(WINAPI, BOOL, p, PeekMessageA, (LPMSG, HWND, UINT, UINT, UINT)); +DLL_DECLARE_PREFIXED(WINAPI, BOOL, p, PostThreadMessageA, (DWORD, UINT, WPARAM, LPARAM)); + +static unsigned __stdcall windows_clock_gettime_threaded(void* param); + +/* +* Converts a windows error to human readable string +* uses retval as errorcode, or, if 0, use GetLastError() +*/ +#if defined(ENABLE_LOGGING) +char *windows_error_str(uint32_t retval) +{ + static char err_string[ERR_BUFFER_SIZE]; + + DWORD size; + ssize_t i; + uint32_t error_code, format_error; + + error_code = retval ? retval : GetLastError(); + + safe_sprintf(err_string, ERR_BUFFER_SIZE, "[%u] ", 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. + // See http://msdn.microsoft.com/en-us/library/windows/hardware/ff545011.aspx + switch (error_code & 0xE0000000) { + case 0: + error_code = HRESULT_FROM_WIN32(error_code); // Still leaves ERROR_SUCCESS unmodified + break; + case 0xE0000000: + error_code = 0x80000000 | (FACILITY_SETUPAPI << 16) | (error_code & 0x0000FFFF); + break; + default: + break; + } + + size = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, error_code, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), &err_string[safe_strlen(err_string)], + ERR_BUFFER_SIZE - (DWORD)safe_strlen(err_string), NULL); + if (size == 0) { + format_error = GetLastError(); + if (format_error) + safe_sprintf(err_string, ERR_BUFFER_SIZE, + "Windows error code %u (FormatMessage error code %u)", error_code, format_error); + else + safe_sprintf(err_string, ERR_BUFFER_SIZE, "Unknown error code %u", error_code); + } + else { + // Remove CR/LF terminators + for (i = safe_strlen(err_string) - 1; (i >= 0) && ((err_string[i] == 0x0A) || (err_string[i] == 0x0D)); i--) { + err_string[i] = 0; + } + } + return err_string; +} +#endif + +/* 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) */ +typedef struct htab_entry { + unsigned long used; + char* str; +} htab_entry; +htab_entry* htab_table = NULL; +usbi_mutex_t htab_write_mutex = NULL; +unsigned long htab_size, htab_filled; + +/* For the used double hash method the table size has to be a prime. To + correct the user given table size we need a prime test. This trivial + algorithm is adequate because the code is called only during init and + the number is likely to be small */ +static int isprime(unsigned long number) +{ + // no even number will be passed + unsigned int divider = 3; + + while((divider * divider < number) && (number % divider != 0)) + divider += 2; + + return (number % divider != 0); +} + +/* Before using the hash table we must allocate memory for it. + We allocate one element more as the found prime number says. + This is done for more effective indexing as explained in the + comment for the hash function. */ +int htab_create(struct libusb_context *ctx, unsigned long nel) +{ + if (htab_table != NULL) { + usbi_err(ctx, "hash table already allocated"); + } + + // Create a mutex + usbi_mutex_init(&htab_write_mutex, NULL); + + // Change nel to the first prime number not smaller as nel. + nel |= 1; + while(!isprime(nel)) + nel += 2; + + htab_size = nel; + usbi_dbg("using %lu entries hash table", nel); + htab_filled = 0; + + // allocate memory and zero out. + htab_table = (htab_entry*) calloc(htab_size + 1, sizeof(htab_entry)); + if (htab_table == NULL) { + usbi_err(ctx, "could not allocate space for hash table"); + return 0; + } + + return 1; +} + +/* After using the hash table it has to be destroyed. */ +void htab_destroy(void) +{ + size_t i; + if (htab_table == NULL) { + return; + } + + for (i=0; i<htab_size; i++) { + if (htab_table[i].used) { + safe_free(htab_table[i].str); + } + } + usbi_mutex_destroy(&htab_write_mutex); + safe_free(htab_table); +} + +/* This is the search function. It uses double hashing with open addressing. + We use an trick to speed up the lookup. The table is created with one + more element available. This enables us to use the index zero special. + This index will never be used because we store the first hash index in + the field used where zero means not used. Every other value means used. + The used field can be used as a first fast comparison for equality of + the stored and the parameter value. This helps to prevent unnecessary + expensive calls of strcmp. */ +unsigned long htab_hash(char* str) +{ + unsigned long hval, hval2; + unsigned long idx; + unsigned long r = 5381; + int c; + char* sz = str; + + if (str == NULL) + return 0; + + // Compute main hash value (algorithm suggested by Nokia) + while ((c = *sz++) != 0) + r = ((r << 5) + r) + c; + if (r == 0) + ++r; + + // compute table hash: simply take the modulus + hval = r % htab_size; + if (hval == 0) + ++hval; + + // Try the first index + idx = hval; + + if (htab_table[idx].used) { + if ( (htab_table[idx].used == hval) + && (safe_strcmp(str, htab_table[idx].str) == 0) ) { + // existing hash + return idx; + } + usbi_dbg("hash collision ('%s' vs '%s')", str, htab_table[idx].str); + + // Second hash function, as suggested in [Knuth] + hval2 = 1 + hval % (htab_size - 2); + + do { + // Because size is prime this guarantees to step through all available indexes + if (idx <= hval2) { + idx = htab_size + idx - hval2; + } else { + idx -= hval2; + } + + // If we visited all entries leave the loop unsuccessfully + if (idx == hval) { + break; + } + + // If entry is found use it. + if ( (htab_table[idx].used == hval) + && (safe_strcmp(str, htab_table[idx].str) == 0) ) { + return idx; + } + } + while (htab_table[idx].used); + } + + // Not found => New entry + + // If the table is full return an error + if (htab_filled >= htab_size) { + usbi_err(NULL, "hash table is full (%d entries)", htab_size); + return 0; + } + + // Concurrent threads might be storing the same entry at the same time + // (eg. "simultaneous" enums from different threads) => use a mutex + usbi_mutex_lock(&htab_write_mutex); + // Just free any previously allocated string (which should be the same as + // new one). The possibility of concurrent threads storing a collision + // string (same hash, different string) at the same time is extremely low + safe_free(htab_table[idx].str); + htab_table[idx].used = hval; + htab_table[idx].str = (char*) malloc(safe_strlen(str)+1); + if (htab_table[idx].str == NULL) { + usbi_err(NULL, "could not duplicate string for hash table"); + usbi_mutex_unlock(&htab_write_mutex); + return 0; + } + memcpy(htab_table[idx].str, str, safe_strlen(str)+1); + ++htab_filled; + usbi_mutex_unlock(&htab_write_mutex); + + return idx; +} + +static int windows_init_dlls(void) +{ + DLL_LOAD_PREFIXED(User32.dll, p, GetMessageA, TRUE); + DLL_LOAD_PREFIXED(User32.dll, p, PeekMessageA, TRUE); + DLL_LOAD_PREFIXED(User32.dll, p, PostThreadMessageA, TRUE); + return LIBUSB_SUCCESS; +} + +bool windows_init_clock(struct libusb_context *ctx) +{ + DWORD_PTR affinity, dummy; + HANDLE event = NULL; + LARGE_INTEGER li_frequency; + int i; + + if (QueryPerformanceFrequency(&li_frequency)) { + // Load DLL imports + if (windows_init_dlls() != LIBUSB_SUCCESS) { + usbi_err(ctx, "could not resolve DLL functions"); + return false; + } + + // 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; + } + 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; + } + if (!SetThreadAffinityMask(timer_thread, affinity)) { + usbi_warn(ctx, "unable to set timer thread affinity, timer discrepancies may arise"); + } + + // 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; +} + +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; + } +} + +/* +* Monotonic and real time functions +*/ +static unsigned __stdcall windows_clock_gettime_threaded(void* param) +{ + 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() 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; + } + + 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)); + } + break; + + case WM_TIMER_EXIT: + usbi_dbg("timer thread quitting"); + return 0; + } + + } +} + +int windows_clock_gettime(int clk_id, struct timespec *tp) +{ + struct timer_request request; + FILETIME filetime; + ULARGE_INTEGER rtime; + 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; + } + + 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: + // 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); + return LIBUSB_SUCCESS; + default: + return LIBUSB_ERROR_INVALID_PARAM; + } +} + +static void windows_transfer_callback(struct usbi_transfer *itransfer, uint32_t io_result, uint32_t io_size) +{ + int status, istatus; + + usbi_dbg("handling I/O completion with errcode %u, size %u", io_result, io_size); + + switch (io_result) { + case NO_ERROR: + status = windows_copy_transfer_data(itransfer, 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 = windows_copy_transfer_data(itransfer, io_size); + if (istatus != LIBUSB_TRANSFER_COMPLETED) { + usbi_dbg("Failed to copy partial data in aborted operation: %d", istatus); + } + if (itransfer->flags & USBI_TRANSFER_TIMED_OUT) { + usbi_dbg("detected timeout"); + status = LIBUSB_TRANSFER_TIMED_OUT; + } + else { + usbi_dbg("detected operation aborted"); + status = LIBUSB_TRANSFER_CANCELLED; + } + break; + default: + usbi_err(ITRANSFER_CTX(itransfer), "detected I/O error %u: %s", io_result, windows_error_str(io_result)); + status = LIBUSB_TRANSFER_ERROR; + break; + } + windows_clear_transfer_priv(itransfer); // Cancel polling + usbi_handle_transfer_completion(itransfer, (enum libusb_transfer_status)status); +} + +void windows_handle_callback(struct usbi_transfer *itransfer, uint32_t io_result, uint32_t io_size) +{ + struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_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: + windows_transfer_callback(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); + } +} + +int windows_handle_events(struct libusb_context *ctx, struct pollfd *fds, POLL_NFDS_TYPE nfds, int num_ready) +{ + POLL_NFDS_TYPE i = 0; + bool found = false; + struct usbi_transfer *transfer; + struct winfd *pollable_fd = NULL; + DWORD io_size, io_result; + + usbi_mutex_lock(&ctx->open_devs_lock); + for (i = 0; i < nfds && num_ready > 0; i++) { + + usbi_dbg("checking fd %d with revents = %04x", fds[i].fd, fds[i].revents); + + if (!fds[i].revents) { + continue; + } + + num_ready--; + + // Because a Windows OVERLAPPED is used for poll emulation, + // a pollable fd is created and stored with each transfer + usbi_mutex_lock(&ctx->flying_transfers_lock); + found = false; + list_for_each_entry(transfer, &ctx->flying_transfers, list, struct usbi_transfer) { + pollable_fd = windows_get_fd(transfer); + if (pollable_fd->fd == fds[i].fd) { + found = true; + break; + } + } + usbi_mutex_unlock(&ctx->flying_transfers_lock); + + if (found) { + windows_get_overlapped_result(transfer, pollable_fd, &io_result, &io_size); + + usbi_remove_pollfd(ctx, pollable_fd->fd); + // 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(transfer, io_result, io_size); + } else { + usbi_mutex_unlock(&ctx->open_devs_lock); + usbi_err(ctx, "could not find a matching transfer for fd %d", fds[i]); + return LIBUSB_ERROR_NOT_FOUND; + } + } + + usbi_mutex_unlock(&ctx->open_devs_lock); + return LIBUSB_SUCCESS; +} + +int windows_common_init(struct libusb_context *ctx) +{ + static const unsigned long HTAB_SIZE = 1021; + + if (!windows_init_clock(ctx)){ + goto error_roll_back; + } + + if (!htab_create(ctx, HTAB_SIZE)) { + goto error_roll_back; + } + + return LIBUSB_SUCCESS; + +error_roll_back: + windows_common_exit(); + + return LIBUSB_ERROR_NO_MEM; +} + +void windows_common_exit(void) +{ + htab_destroy(); + windows_destroy_clock(); +} diff --git a/libusb/os/windows_nt_common.h b/libusb/os/windows_nt_common.h new file mode 100644 index 0000000..ac907ce --- /dev/null +++ b/libusb/os/windows_nt_common.h @@ -0,0 +1,58 @@ +/* + * Windows backend common header for libusb 1.0 + * + * This file brings together header code common between + * the desktop Windows backends. + * Copyright © 2012-2013 RealVNC Ltd. + * Copyright © 2009-2012 Pete Batard <pete@akeo.ie> + * With contributions from Michael Plante, Orin Eman et al. + * Parts of this code adapted from libusb-win32-v1 by Stephan Meyer + * Major code testing contribution by Xiaofan Chen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#pragma once + +typedef struct USB_CONFIGURATION_DESCRIPTOR { + UCHAR bLength; + UCHAR bDescriptorType; + USHORT wTotalLength; + UCHAR bNumInterfaces; + UCHAR bConfigurationValue; + UCHAR iConfiguration; + UCHAR bmAttributes; + UCHAR MaxPower; +} USB_CONFIGURATION_DESCRIPTOR, *PUSB_CONFIGURATION_DESCRIPTOR; + +typedef struct libusb_device_descriptor USB_DEVICE_DESCRIPTOR, *PUSB_DEVICE_DESCRIPTOR; + +int windows_common_init(struct libusb_context *ctx); +void windows_common_exit(void); + +unsigned long htab_hash(char* str); +int windows_clock_gettime(int clk_id, struct timespec *tp); + +void windows_clear_transfer_priv(struct usbi_transfer *itransfer); +int windows_copy_transfer_data(struct usbi_transfer *itransfer, uint32_t io_size); +struct winfd *windows_get_fd(struct usbi_transfer *transfer); +void windows_get_overlapped_result(struct usbi_transfer *transfer, struct winfd *pollable_fd, DWORD *io_result, DWORD *io_size); + +void windows_handle_callback(struct usbi_transfer *itransfer, uint32_t io_result, uint32_t io_size); +int windows_handle_events(struct libusb_context *ctx, struct pollfd *fds, POLL_NFDS_TYPE nfds, int num_ready); + +#if defined(ENABLE_LOGGING) +char *windows_error_str(uint32_t retval); +#endif diff --git a/libusb/os/windows_usb.c b/libusb/os/windows_usb.c index 95fbb32..f73ad6f 100644 --- a/libusb/os/windows_usb.c +++ b/libusb/os/windows_usb.c @@ -45,8 +45,6 @@ // Helper prototypes static int windows_get_active_config_descriptor(struct libusb_device *dev, unsigned char *buffer, size_t len, int *host_endian); -static int windows_clock_gettime(int clk_id, struct timespec *tp); -unsigned __stdcall windows_clock_gettime_threaded(void* param); // Common calls static int common_configure_endpoints(int sub_api, struct libusb_device_handle *dev_handle, int iface); @@ -100,15 +98,11 @@ static int composite_copy_transfer_data(int sub_api, struct usbi_transfer *itran // Global variables uint64_t hires_frequency, hires_ticks_to_ps; -const uint64_t epoch_time = UINT64_C(116444736000000000); // 1970.01.01 00:00:000 in MS Filetime int windows_version = WINDOWS_UNDEFINED; static char windows_version_str[128] = "Windows Undefined"; // Concurrency static int concurrent_usage = -1; usbi_mutex_t autoclaim_lock; -// Timer thread -HANDLE timer_thread = NULL; -DWORD timer_thread_id = 0; // API globals #define CHECK_WINUSBX_AVAILABLE(sub_api) do { if (sub_api == SUB_API_NOTSET) sub_api = priv->sub_api; \ if (!WinUSBX[sub_api].initialized) return LIBUSB_ERROR_ACCESS; } while(0) @@ -139,57 +133,6 @@ static char* guid_to_string(const GUID* guid) #endif /* - * Converts a windows error to human readable string - * uses retval as errorcode, or, if 0, use GetLastError() - */ -#if defined(ENABLE_LOGGING) -static char *windows_error_str(uint32_t retval) -{ -static char err_string[ERR_BUFFER_SIZE]; - - DWORD size; - ssize_t i; - uint32_t error_code, format_error; - - error_code = retval?retval:GetLastError(); - - safe_sprintf(err_string, ERR_BUFFER_SIZE, "[%u] ", 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. - // See http://msdn.microsoft.com/en-us/library/windows/hardware/ff545011.aspx - switch (error_code & 0xE0000000) { - case 0: - error_code = HRESULT_FROM_WIN32(error_code); // Still leaves ERROR_SUCCESS unmodified - break; - case 0xE0000000: - error_code = 0x80000000 | (FACILITY_SETUPAPI << 16) | (error_code & 0x0000FFFF); - break; - default: - break; - } - - size = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, error_code, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), &err_string[safe_strlen(err_string)], - ERR_BUFFER_SIZE - (DWORD)safe_strlen(err_string), NULL); - if (size == 0) { - format_error = GetLastError(); - if (format_error) - safe_sprintf(err_string, ERR_BUFFER_SIZE, - "Windows error code %u (FormatMessage error code %u)", error_code, format_error); - else - safe_sprintf(err_string, ERR_BUFFER_SIZE, "Unknown error code %u", error_code); - } else { - // Remove CR/LF terminators - for (i=safe_strlen(err_string)-1; (i>=0) && ((err_string[i]==0x0A) || (err_string[i]==0x0D)); i--) { - err_string[i] = 0; - } - } - return err_string; -} -#endif - -/* * Sanitize Microsoft's paths: convert to uppercase, add prefix and fix backslashes. * Return an allocated sanitized string or NULL on error. */ @@ -253,9 +196,6 @@ static int init_dlls(void) DLL_LOAD_PREFIXED(SetupAPI.dll, p, SetupDiOpenDeviceInterfaceRegKey, TRUE); DLL_LOAD_PREFIXED(AdvAPI32.dll, p, RegQueryValueExW, TRUE); DLL_LOAD_PREFIXED(AdvAPI32.dll, p, RegCloseKey, TRUE); - DLL_LOAD_PREFIXED(User32.dll, p, GetMessageA, TRUE); - DLL_LOAD_PREFIXED(User32.dll, p, PeekMessageA, TRUE); - DLL_LOAD_PREFIXED(User32.dll, p, PostThreadMessageA, TRUE); return LIBUSB_SUCCESS; } @@ -458,176 +398,6 @@ err_exit: *dev_info = INVALID_HANDLE_VALUE; return NULL;} -/* 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) */ -typedef struct htab_entry { - unsigned long used; - char* str; -} htab_entry; -htab_entry* htab_table = NULL; -usbi_mutex_t htab_write_mutex = NULL; -unsigned long htab_size, htab_filled; - -/* For the used double hash method the table size has to be a prime. To - correct the user given table size we need a prime test. This trivial - algorithm is adequate because the code is called only during init and - the number is likely to be small */ -static int isprime(unsigned long number) -{ - // no even number will be passed - unsigned int divider = 3; - - while((divider * divider < number) && (number % divider != 0)) - divider += 2; - - return (number % divider != 0); -} - -/* Before using the hash table we must allocate memory for it. - We allocate one element more as the found prime number says. - This is done for more effective indexing as explained in the - comment for the hash function. */ -static int htab_create(struct libusb_context *ctx, unsigned long nel) -{ - if (htab_table != NULL) { - usbi_err(ctx, "hash table already allocated"); - } - - // Create a mutex - usbi_mutex_init(&htab_write_mutex, NULL); - - // Change nel to the first prime number not smaller as nel. - nel |= 1; - while(!isprime(nel)) - nel += 2; - - htab_size = nel; - usbi_dbg("using %lu entries hash table", nel); - htab_filled = 0; - - // allocate memory and zero out. - htab_table = (htab_entry*) calloc(htab_size + 1, sizeof(htab_entry)); - if (htab_table == NULL) { - usbi_err(ctx, "could not allocate space for hash table"); - return 0; - } - - return 1; -} - -/* After using the hash table it has to be destroyed. */ -static void htab_destroy(void) -{ - size_t i; - if (htab_table == NULL) { - return; - } - - for (i=0; i<htab_size; i++) { - if (htab_table[i].used) { - safe_free(htab_table[i].str); - } - } - usbi_mutex_destroy(&htab_write_mutex); - safe_free(htab_table); -} - -/* This is the search function. It uses double hashing with open addressing. - We use an trick to speed up the lookup. The table is created with one - more element available. This enables us to use the index zero special. - This index will never be used because we store the first hash index in - the field used where zero means not used. Every other value means used. - The used field can be used as a first fast comparison for equality of - the stored and the parameter value. This helps to prevent unnecessary - expensive calls of strcmp. */ -static unsigned long htab_hash(char* str) -{ - unsigned long hval, hval2; - unsigned long idx; - unsigned long r = 5381; - int c; - char* sz = str; - - if (str == NULL) - return 0; - - // Compute main hash value (algorithm suggested by Nokia) - while ((c = *sz++) != 0) - r = ((r << 5) + r) + c; - if (r == 0) - ++r; - - // compute table hash: simply take the modulus - hval = r % htab_size; - if (hval == 0) - ++hval; - - // Try the first index - idx = hval; - - if (htab_table[idx].used) { - if ( (htab_table[idx].used == hval) - && (safe_strcmp(str, htab_table[idx].str) == 0) ) { - // existing hash - return idx; - } - usbi_dbg("hash collision ('%s' vs '%s')", str, htab_table[idx].str); - - // Second hash function, as suggested in [Knuth] - hval2 = 1 + hval % (htab_size - 2); - - do { - // Because size is prime this guarantees to step through all available indexes - if (idx <= hval2) { - idx = htab_size + idx - hval2; - } else { - idx -= hval2; - } - - // If we visited all entries leave the loop unsuccessfully - if (idx == hval) { - break; - } - - // If entry is found use it. - if ( (htab_table[idx].used == hval) - && (safe_strcmp(str, htab_table[idx].str) == 0) ) { - return idx; - } - } - while (htab_table[idx].used); - } - - // Not found => New entry - - // If the table is full return an error - if (htab_filled >= htab_size) { - usbi_err(NULL, "hash table is full (%lu entries)", htab_size); - return 0; - } - - // Concurrent threads might be storing the same entry at the same time - // (eg. "simultaneous" enums from different threads) => use a mutex - usbi_mutex_lock(&htab_write_mutex); - // Just free any previously allocated string (which should be the same as - // new one). The possibility of concurrent threads storing a collision - // string (same hash, different string) at the same time is extremely low - safe_free(htab_table[idx].str); - htab_table[idx].used = hval; - htab_table[idx].str = (char*) malloc(safe_strlen(str)+1); - if (htab_table[idx].str == NULL) { - usbi_err(NULL, "could not duplicate string for hash table"); - usbi_mutex_unlock(&htab_write_mutex); - return 0; - } - memcpy(htab_table[idx].str, str, safe_strlen(str)+1); - ++htab_filled; - usbi_mutex_unlock(&htab_write_mutex); - - return idx; -} - /* * Returns the session ID of a device's nth level ancestor * If there's no device at the nth level, return 0 @@ -954,10 +724,7 @@ static void get_windows_version(void) static int windows_init(struct libusb_context *ctx) { int i, r = LIBUSB_ERROR_OTHER; - DWORD_PTR affinity, dummy; - HANDLE event = NULL; HANDLE semaphore; - LARGE_INTEGER li_frequency; char sem_name[11+1+8]; // strlen(libusb_init)+'\0'+(32-bit hex PID) sprintf(sem_name, "libusb_init%08X", (unsigned int)GetCurrentProcessId()); @@ -1003,85 +770,23 @@ static int windows_init(struct libusb_context *ctx) usb_api_backend[i].init(SUB_API_NOTSET, ctx); } - 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)); - goto init_exit; - } - // 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); - - r = LIBUSB_ERROR_NO_MEM; - event = CreateEvent(NULL, FALSE, FALSE, NULL); - if (event == NULL) { - usbi_err(ctx, "could not create event: %s", windows_error_str(0)); - goto init_exit; - } - 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"); - goto init_exit; - } - if (!SetThreadAffinityMask(timer_thread, affinity)) { - usbi_warn(ctx, "unable to set timer thread affinity, timer discrepancies may arise"); - } - - // 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"); - goto init_exit; - } - } - else { - usbi_dbg("no hires timer available on this platform"); - hires_frequency = 0; - hires_ticks_to_ps = UINT64_C(0); + r = windows_common_init(ctx); + if (r) { + goto init_exit; } - - // Create a hash table to store session ids. Second parameter is better if prime - htab_create(ctx, HTAB_SIZE); } // At this stage, either we went through full init successfully, or didn't need to r = LIBUSB_SUCCESS; init_exit: // Holds semaphore here. if (!concurrent_usage && r != LIBUSB_SUCCESS) { // First init failed? - 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_warn(ctx, "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; - } - htab_destroy(); + windows_common_exit(); usbi_mutex_destroy(&autoclaim_lock); } if (r != LIBUSB_SUCCESS) --concurrent_usage; // Not expected to call libusb_exit if we failed. - if (event) - CloseHandle(event); ReleaseSemaphore(semaphore, 1, NULL); // increase count back to 1 CloseHandle(semaphore); return r; @@ -1891,19 +1596,7 @@ static void windows_exit(void) usb_api_backend[i].exit(SUB_API_NOTSET); } exit_polling(); - - 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); - } - CloseHandle(timer_thread); - timer_thread = NULL; - timer_thread_id = 0; - } - htab_destroy(); + windows_common_exit(); usbi_mutex_destroy(&autoclaim_lock); } @@ -2088,7 +1781,7 @@ static void windows_destroy_device(struct libusb_device *dev) windows_device_priv_release(dev); } -static void windows_clear_transfer_priv(struct usbi_transfer *itransfer) +void windows_clear_transfer_priv(struct usbi_transfer *itransfer) { struct windows_transfer_priv *transfer_priv = (struct windows_transfer_priv*)usbi_transfer_get_os_priv(itransfer); @@ -2213,232 +1906,34 @@ static int windows_cancel_transfer(struct usbi_transfer *itransfer) } } -static void windows_transfer_callback(struct usbi_transfer *itransfer, uint32_t io_result, uint32_t io_size) +int windows_copy_transfer_data(struct usbi_transfer *itransfer, uint32_t io_size) { struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev); - int status, istatus; - - usbi_dbg("handling I/O completion with errcode %u, size %u", io_result, io_size); - - switch(io_result) { - case NO_ERROR: - status = priv->apib->copy_transfer_data(SUB_API_NOTSET, itransfer, 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 = priv->apib->copy_transfer_data(SUB_API_NOTSET, itransfer, io_size); - if (istatus != LIBUSB_TRANSFER_COMPLETED) { - usbi_dbg("Failed to copy partial data in aborted operation: %d", istatus); - } - if (itransfer->flags & USBI_TRANSFER_TIMED_OUT) { - usbi_dbg("detected timeout"); - status = LIBUSB_TRANSFER_TIMED_OUT; - } else { - usbi_dbg("detected operation aborted"); - status = LIBUSB_TRANSFER_CANCELLED; - } - break; - default: - usbi_err(ITRANSFER_CTX(itransfer), "detected I/O error %u: %s", io_result, windows_error_str(io_result)); - status = LIBUSB_TRANSFER_ERROR; - break; - } - windows_clear_transfer_priv(itransfer); // Cancel polling - usbi_handle_transfer_completion(itransfer, (enum libusb_transfer_status)status); + return priv->apib->copy_transfer_data(SUB_API_NOTSET, itransfer, io_size); } -static void windows_handle_callback (struct usbi_transfer *itransfer, uint32_t io_result, uint32_t io_size) +struct winfd *windows_get_fd(struct usbi_transfer *transfer) { - struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_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: - windows_transfer_callback (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); - } + struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(transfer); + return &transfer_priv->pollable_fd; } -static int windows_handle_events(struct libusb_context *ctx, struct pollfd *fds, POLL_NFDS_TYPE nfds, int num_ready) +void windows_get_overlapped_result(struct usbi_transfer *transfer, struct winfd *pollable_fd, DWORD *io_result, DWORD *io_size) { - struct windows_transfer_priv* transfer_priv = NULL; - POLL_NFDS_TYPE i = 0; - bool found; - struct usbi_transfer *transfer; - DWORD io_size, io_result; - - usbi_mutex_lock(&ctx->open_devs_lock); - for (i = 0; i < nfds && num_ready > 0; i++) { - - usbi_dbg("checking fd %d with revents = %04x", fds[i].fd, fds[i].revents); - - if (!fds[i].revents) { - continue; - } - - num_ready--; - - // Because a Windows OVERLAPPED is used for poll emulation, - // a pollable fd is created and stored with each transfer - usbi_mutex_lock(&ctx->flying_transfers_lock); - found = false; - list_for_each_entry(transfer, &ctx->flying_transfers, list, struct usbi_transfer) { - transfer_priv = usbi_transfer_get_os_priv(transfer); - if (transfer_priv->pollable_fd.fd == fds[i].fd) { - found = true; - break; - } - } - usbi_mutex_unlock(&ctx->flying_transfers_lock); - - if (found) { - // Handle async requests that completed synchronously first - if (HasOverlappedIoCompletedSync(transfer_priv->pollable_fd.overlapped)) { - io_result = NO_ERROR; - io_size = (DWORD)transfer_priv->pollable_fd.overlapped->InternalHigh; - // Regular async overlapped - } else if (GetOverlappedResult(transfer_priv->pollable_fd.handle, - transfer_priv->pollable_fd.overlapped, &io_size, false)) { - io_result = NO_ERROR; - } else { - io_result = GetLastError(); - } - usbi_remove_pollfd(ctx, transfer_priv->pollable_fd.fd); - // 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(transfer, io_result, io_size); - } else { - usbi_mutex_unlock(&ctx->open_devs_lock); - usbi_err(ctx, "could not find a matching transfer for fd %d", fds[i]); - return LIBUSB_ERROR_NOT_FOUND; - } - } - - usbi_mutex_unlock(&ctx->open_devs_lock); - return LIBUSB_SUCCESS; -} - -/* - * Monotonic and real time functions - */ -unsigned __stdcall windows_clock_gettime_threaded(void* param) -{ - 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() that we're ready to service requests - if (!SetEvent((HANDLE)param)) { - usbi_dbg("SetEvent failed for timer init event: %s", windows_error_str(0)); + if (HasOverlappedIoCompletedSync(pollable_fd->overlapped)) { + *io_result = NO_ERROR; + *io_size = (DWORD)pollable_fd->overlapped->InternalHigh; + // Regular async overlapped } - 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; - } - - 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)); - } - break; - - case WM_TIMER_EXIT: - usbi_dbg("timer thread quitting"); - return 0; - } + else if (GetOverlappedResult(pollable_fd->handle, pollable_fd->overlapped, io_size, false)) { + *io_result = NO_ERROR; } -} - -static int windows_clock_gettime(int clk_id, struct timespec *tp) -{ - struct timer_request request; - FILETIME filetime; - ULARGE_INTEGER rtime; - 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; - } - - 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: - // 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); - return LIBUSB_SUCCESS; - default: - return LIBUSB_ERROR_INVALID_PARAM; + else { + *io_result = GetLastError(); } } - // NB: MSVC6 does not support named initializers. const struct usbi_os_backend windows_backend = { "Windows", @@ -2479,7 +1974,7 @@ const struct usbi_os_backend windows_backend = { windows_clear_transfer_priv, windows_handle_events, - NULL, /* handle_transfer_completion() */ + NULL, windows_clock_gettime, #if defined(USBI_TIMERFD_AVAILABLE) diff --git a/libusb/os/windows_usb.h b/libusb/os/windows_usb.h index edf267a..e201762 100644 --- a/libusb/os/windows_usb.h +++ b/libusb/os/windows_usb.h @@ -23,6 +23,7 @@ #pragma once #include "windows_common.h" +#include "windows_nt_common.h" #if defined(_MSC_VER) // disable /W4 MSVC warnings that are benign @@ -64,7 +65,6 @@ #define MAX_PATH_LENGTH 128 #define MAX_KEY_LENGTH 256 #define LIST_SEPARATOR ';' -#define HTAB_SIZE 1021 // Handle code for HID interface that have been claimed ("dibs") #define INTERFACE_CLAIMED ((HANDLE)(intptr_t)0xD1B5) @@ -209,7 +209,6 @@ struct hid_device_priv { uint8_t string_index[3]; // man, prod, ser }; -typedef struct libusb_device_descriptor USB_DEVICE_DESCRIPTOR, *PUSB_DEVICE_DESCRIPTOR; struct windows_device_priv { uint8_t depth; // distance to HCD uint8_t port; // port number on the hub @@ -308,15 +307,6 @@ struct driver_lookup { const char* designation; // internal designation (for debug output) }; -#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; -}; - /* OLE32 dependency */ DLL_DECLARE_PREFIXED(WINAPI, HRESULT, p, CLSIDFromString, (LPCOLESTR, LPCLSID)); @@ -338,11 +328,6 @@ DLL_DECLARE_PREFIXED(WINAPI, HKEY, p, SetupDiOpenDeviceInterfaceRegKey, (HDEVINF DLL_DECLARE_PREFIXED(WINAPI, LONG, p, RegQueryValueExW, (HKEY, LPCWSTR, LPDWORD, LPDWORD, LPBYTE, LPDWORD)); DLL_DECLARE_PREFIXED(WINAPI, LONG, p, RegCloseKey, (HKEY)); -/* User32 dependencies */ -DLL_DECLARE_PREFIXED(WINAPI, BOOL, p, GetMessageA, (LPMSG, HWND, UINT, UINT)); -DLL_DECLARE_PREFIXED(WINAPI, BOOL, p, PeekMessageA, (LPMSG, HWND, UINT, UINT, UINT)); -DLL_DECLARE_PREFIXED(WINAPI, BOOL, p, PostThreadMessageA, (DWORD, UINT, WPARAM, LPARAM)); - /* * Windows DDK API definitions. Most of it copied from MinGW's includes */ @@ -469,17 +454,6 @@ typedef struct USB_INTERFACE_DESCRIPTOR { UCHAR iInterface; } USB_INTERFACE_DESCRIPTOR, *PUSB_INTERFACE_DESCRIPTOR; -typedef struct USB_CONFIGURATION_DESCRIPTOR { - UCHAR bLength; - UCHAR bDescriptorType; - USHORT wTotalLength; - UCHAR bNumInterfaces; - UCHAR bConfigurationValue; - UCHAR iConfiguration; - UCHAR bmAttributes; - UCHAR MaxPower; -} USB_CONFIGURATION_DESCRIPTOR, *PUSB_CONFIGURATION_DESCRIPTOR; - typedef struct USB_CONFIGURATION_DESCRIPTOR_SHORT { struct { ULONG ConnectionIndex; diff --git a/libusb/version_nano.h b/libusb/version_nano.h index ecc5375..523f71c 100644 --- a/libusb/version_nano.h +++ b/libusb/version_nano.h @@ -1 +1 @@ -#define LIBUSB_NANO 11027 +#define LIBUSB_NANO 11028 diff --git a/msvc/libusb_dll_2013.vcxproj b/msvc/libusb_dll_2013.vcxproj index c1b2a0a..d2ee675 100644 --- a/msvc/libusb_dll_2013.vcxproj +++ b/msvc/libusb_dll_2013.vcxproj @@ -147,6 +147,7 @@ <ClCompile Include="..\libusb\hotplug.c" /> <ClCompile Include="..\libusb\io.c" /> <ClCompile Include="..\libusb\os\poll_windows.c" /> + <ClCompile Include="..\libusb\os\windows_nt_common.c" /> <ClCompile Include="..\libusb\strerror.c" /> <ClCompile Include="..\libusb\sync.c" /> <ClCompile Include="..\libusb\os\threads_windows.c" /> @@ -154,6 +155,7 @@ </ItemGroup> <ItemGroup> <ClInclude Include="..\libusb\hotplug.h" /> + <ClInclude Include="..\libusb\os\windows_nt_common.h" /> <ClInclude Include="..\libusb\version.h" /> <ClInclude Include="..\libusb\version_nano.h" /> <ClInclude Include=".\config.h" /> diff --git a/msvc/libusb_dll_2015.vcxproj b/msvc/libusb_dll_2015.vcxproj index cd560c0..85d9823 100644 --- a/msvc/libusb_dll_2015.vcxproj +++ b/msvc/libusb_dll_2015.vcxproj @@ -147,6 +147,7 @@ <ClCompile Include="..\libusb\hotplug.c" /> <ClCompile Include="..\libusb\io.c" /> <ClCompile Include="..\libusb\os\poll_windows.c" /> + <ClCompile Include="..\libusb\os\windows_nt_common.c" /> <ClCompile Include="..\libusb\strerror.c" /> <ClCompile Include="..\libusb\sync.c" /> <ClCompile Include="..\libusb\os\threads_windows.c" /> @@ -154,6 +155,7 @@ </ItemGroup> <ItemGroup> <ClInclude Include="..\libusb\hotplug.h" /> + <ClInclude Include="..\libusb\os\windows_nt_common.h" /> <ClInclude Include="..\libusb\version.h" /> <ClInclude Include="..\libusb\version_nano.h" /> <ClInclude Include=".\config.h" /> diff --git a/msvc/libusb_sources b/msvc/libusb_sources index 308a666..f9718cf 100644 --- a/msvc/libusb_sources +++ b/msvc/libusb_sources @@ -35,4 +35,5 @@ SOURCES=..\core.c \ threads_windows.c \ poll_windows.c \ windows_usb.c \ + windows_nt_common.c \ ..\libusb-1.0.rc diff --git a/msvc/libusb_static_2013.vcxproj b/msvc/libusb_static_2013.vcxproj index 05b7bd8..f980ae3 100644 --- a/msvc/libusb_static_2013.vcxproj +++ b/msvc/libusb_static_2013.vcxproj @@ -137,6 +137,7 @@ <ClCompile Include="..\libusb\hotplug.c" /> <ClCompile Include="..\libusb\io.c" /> <ClCompile Include="..\libusb\os\poll_windows.c" /> + <ClCompile Include="..\libusb\os\windows_nt_common.c" /> <ClCompile Include="..\libusb\strerror.c" /> <ClCompile Include="..\libusb\sync.c" /> <ClCompile Include="..\libusb\os\threads_windows.c" /> @@ -144,6 +145,7 @@ </ItemGroup> <ItemGroup> <ClInclude Include="..\libusb\hotplug.h" /> + <ClInclude Include="..\libusb\os\windows_nt_common.h" /> <ClInclude Include="..\libusb\version.h" /> <ClInclude Include="..\libusb\version_nano.h" /> <ClInclude Include=".\config.h" /> diff --git a/msvc/libusb_static_2015.vcxproj b/msvc/libusb_static_2015.vcxproj index 0576705..b50cdf2 100644 --- a/msvc/libusb_static_2015.vcxproj +++ b/msvc/libusb_static_2015.vcxproj @@ -141,6 +141,7 @@ <ClCompile Include="..\libusb\hotplug.c" /> <ClCompile Include="..\libusb\io.c" /> <ClCompile Include="..\libusb\os\poll_windows.c" /> + <ClCompile Include="..\libusb\os\windows_nt_common.c" /> <ClCompile Include="..\libusb\strerror.c" /> <ClCompile Include="..\libusb\sync.c" /> <ClCompile Include="..\libusb\os\threads_windows.c" /> @@ -148,6 +149,7 @@ </ItemGroup> <ItemGroup> <ClInclude Include="..\libusb\hotplug.h" /> + <ClInclude Include="..\libusb\os\windows_nt_common.h" /> <ClInclude Include="..\libusb\version.h" /> <ClInclude Include="..\libusb\version_nano.h" /> <ClInclude Include=".\config.h" /> |