diff options
Diffstat (limited to 'libusb/os')
-rw-r--r-- | libusb/os/windows_compat.c | 141 | ||||
-rw-r--r-- | libusb/os/windows_usb.c | 467 | ||||
-rw-r--r-- | libusb/os/windows_usb.h | 49 |
3 files changed, 441 insertions, 216 deletions
diff --git a/libusb/os/windows_compat.c b/libusb/os/windows_compat.c index 58b91d3..4ff0c4f 100644 --- a/libusb/os/windows_compat.c +++ b/libusb/os/windows_compat.c @@ -71,7 +71,6 @@ #include <stdio.h> #include <stdlib.h> #include <stdint.h> -#include <pthread.h> #include <io.h> #include "windows_compat.h" @@ -129,7 +128,7 @@ const struct winfd INVALID_WINFD = {-1, NULL, NULL, RW_NONE, FALSE}; struct winfd poll_fd[MAX_FDS]; // internal fd data struct { - pthread_mutex_t mutex; // thread mutex lock for fds + CRITICAL_SECTION mutex; // lock for fds BYTE marker; // 1st byte of a read_for_poll operation gets stored here } _poll_fd[MAX_FDS]; @@ -137,25 +136,25 @@ struct { // globals BOOLEAN is_polling_set = FALSE; LONG pipe_number = 0; +static volatile LONG compat_spinlock = 0; // Init void init_polling(void) { int i; - if (is_polling_set) { - // The sleep 1 sec is to give enough time for our initialization - // below to finish, should concurrent simultaneous inits be issued - // from multiple threads - we don't want the second init to return - // before the first has had time to complete its job. - Sleep(1000); - return; + + while (InterlockedExchange((LONG *)&compat_spinlock, 1) == 1) { + SleepEx(0, TRUE); } - is_polling_set = TRUE; - for (i=0; i<MAX_FDS; i++) { - poll_fd[i] = INVALID_WINFD; - _poll_fd[i].marker = 0; - pthread_mutex_init(&_poll_fd[i].mutex, NULL); + if (!is_polling_set) { + for (i=0; i<MAX_FDS; i++) { + poll_fd[i] = INVALID_WINFD; + _poll_fd[i].marker = 0; + InitializeCriticalSection(&_poll_fd[i].mutex); + } + is_polling_set = TRUE; } + compat_spinlock = 0; } // Internal function to retrieve the table index (and lock the fd mutex) @@ -168,7 +167,12 @@ int _fd_to_index_and_lock(int fd) for (i=0; i<MAX_FDS; i++) { if (poll_fd[i].fd == fd) { - pthread_mutex_lock(&_poll_fd[i].mutex); + EnterCriticalSection(&_poll_fd[i].mutex); + // fd might have changed before we got to critical + if (poll_fd[i].fd != fd) { + LeaveCriticalSection(&_poll_fd[i].mutex); + continue; + } return i; } } @@ -218,26 +222,31 @@ void reset_overlapped(OVERLAPPED *overlapped) void exit_polling(void) { int i; - if (!is_polling_set) - return; - is_polling_set = FALSE; - for (i=0; i<MAX_FDS; i++) { - // Cancel any async I/O (handle can be invalid) - CancelIo(poll_fd[i].handle); - // If anything was pending on that I/O, it should be - // terminating, and we should be able to access the fd - // mutex lock before too long - pthread_mutex_lock(&_poll_fd[i].mutex); - if ( (poll_fd[i].fd > 0) && (poll_fd[i].handle != INVALID_HANDLE_VALUE) && (poll_fd[i].handle != 0) - && (GetFileType(poll_fd[i].handle) == FILE_TYPE_UNKNOWN) ) { - _close(poll_fd[i].fd); + while (InterlockedExchange((LONG *)&compat_spinlock, 1) == 1) { + SleepEx(0, TRUE); + } + if (is_polling_set) { + is_polling_set = FALSE; + + for (i=0; i<MAX_FDS; i++) { + // Cancel any async I/O (handle can be invalid) + CancelIo(poll_fd[i].handle); + // If anything was pending on that I/O, it should be + // terminating, and we should be able to access the fd + // mutex lock before too long + EnterCriticalSection(&_poll_fd[i].mutex); + if ( (poll_fd[i].fd > 0) && (poll_fd[i].handle != INVALID_HANDLE_VALUE) && (poll_fd[i].handle != 0) + && (GetFileType(poll_fd[i].handle) == FILE_TYPE_UNKNOWN) ) { + _close(poll_fd[i].fd); + } + free_overlapped(poll_fd[i].overlapped); + poll_fd[i] = INVALID_WINFD; + LeaveCriticalSection(&_poll_fd[i].mutex); + DeleteCriticalSection(&_poll_fd[i].mutex); } - free_overlapped(poll_fd[i].overlapped); - poll_fd[i] = INVALID_WINFD; - pthread_mutex_unlock(&_poll_fd[i].mutex); - pthread_mutex_destroy(&_poll_fd[i].mutex); } + compat_spinlock = 0; } /* @@ -325,7 +334,13 @@ int _libusb_pipe(int filedes[2]) for (i=0, j=0; i<MAX_FDS; i++) { if (poll_fd[i].fd < 0) { - pthread_mutex_lock(&_poll_fd[i].mutex); + EnterCriticalSection(&_poll_fd[i].mutex); + // fd might have been allocated before we got to critical + if (poll_fd[i].fd >= 0) { + LeaveCriticalSection(&_poll_fd[i].mutex); + continue; + } + poll_fd[i].fd = filedes[j]; poll_fd[i].handle = handle[j]; poll_fd[i].overlapped = (j==0)?overlapped0:overlapped1; @@ -337,7 +352,7 @@ int _libusb_pipe(int filedes[2]) // so that we get read event notifications _init_read_marker(i); } - pthread_mutex_unlock(&_poll_fd[i].mutex); + LeaveCriticalSection(&_poll_fd[i].mutex); if (j>=2) { return 0; } @@ -409,12 +424,17 @@ struct winfd create_fd_for_poll(HANDLE handle, int access_mode) for (i=0; i<MAX_FDS; i++) { if (poll_fd[i].fd < 0) { - pthread_mutex_lock(&_poll_fd[i].mutex); + EnterCriticalSection(&_poll_fd[i].mutex); + // fd might have been removed before we got to critical + if (poll_fd[i].fd >= 0) { + LeaveCriticalSection(&_poll_fd[i].mutex); + continue; + } wfd.fd = fd; wfd.handle = handle; wfd.overlapped = overlapped; memcpy(&poll_fd[i], &wfd, sizeof(struct winfd)); - pthread_mutex_unlock(&_poll_fd[i].mutex); + LeaveCriticalSection(&_poll_fd[i].mutex); return wfd; } } @@ -452,7 +472,7 @@ void free_fd_for_poll(int fd) return; } _free_index(index); - pthread_mutex_unlock(&_poll_fd[index].mutex); + LeaveCriticalSection(&_poll_fd[index].mutex); } /* @@ -470,9 +490,14 @@ struct winfd fd_to_winfd(int fd) for (i=0; i<MAX_FDS; i++) { if (poll_fd[i].fd == fd) { - pthread_mutex_lock(&_poll_fd[i].mutex); + EnterCriticalSection(&_poll_fd[i].mutex); + // fd might have been deleted before we got to critical + if (poll_fd[i].fd != fd) { + LeaveCriticalSection(&_poll_fd[i].mutex); + continue; + } memcpy(&wfd, &poll_fd[i], sizeof(struct winfd)); - pthread_mutex_unlock(&_poll_fd[i].mutex); + LeaveCriticalSection(&_poll_fd[i].mutex); return wfd; } } @@ -491,9 +516,14 @@ struct winfd handle_to_winfd(HANDLE handle) for (i=0; i<MAX_FDS; i++) { if (poll_fd[i].handle == handle) { - pthread_mutex_lock(&_poll_fd[i].mutex); + EnterCriticalSection(&_poll_fd[i].mutex); + // fd might have been deleted before we got to critical + if (poll_fd[i].handle != handle) { + LeaveCriticalSection(&_poll_fd[i].mutex); + continue; + } memcpy(&wfd, &poll_fd[i], sizeof(struct winfd)); - pthread_mutex_unlock(&_poll_fd[i].mutex); + LeaveCriticalSection(&_poll_fd[i].mutex); return wfd; } } @@ -512,9 +542,14 @@ struct winfd overlapped_to_winfd(OVERLAPPED* overlapped) for (i=0; i<MAX_FDS; i++) { if (poll_fd[i].overlapped == overlapped) { - pthread_mutex_lock(&_poll_fd[i].mutex); + EnterCriticalSection(&_poll_fd[i].mutex); + // fd might have been deleted before we got to critical + if (poll_fd[i].overlapped != overlapped) { + LeaveCriticalSection(&_poll_fd[i].mutex); + continue; + } memcpy(&wfd, &poll_fd[i], sizeof(struct winfd)); - pthread_mutex_unlock(&_poll_fd[i].mutex); + LeaveCriticalSection(&_poll_fd[i].mutex); return wfd; } } @@ -559,7 +594,7 @@ int _libusb_poll(struct pollfd *fds, unsigned int nfds, int timeout) fds[i].revents |= POLLNVAL | POLLERR; errno = EBADF; if (index >= 0) { - pthread_mutex_unlock(&_poll_fd[index].mutex); + LeaveCriticalSection(&_poll_fd[index].mutex); } printb("poll: invalid fd\n"); return -1; @@ -570,7 +605,7 @@ int _libusb_poll(struct pollfd *fds, unsigned int nfds, int timeout) fds[i].revents |= POLLNVAL | POLLERR; errno = EBADF; printb("poll: attempted POLLIN on fd[%d] without READ access\n", i); - pthread_mutex_unlock(&_poll_fd[index].mutex); + LeaveCriticalSection(&_poll_fd[index].mutex); return -1; } @@ -578,7 +613,7 @@ int _libusb_poll(struct pollfd *fds, unsigned int nfds, int timeout) fds[i].revents |= POLLNVAL | POLLERR; errno = EBADF; printb("poll: attempted POLLOUT on fd[%d] without WRITE access\n", i); - pthread_mutex_unlock(&_poll_fd[index].mutex); + LeaveCriticalSection(&_poll_fd[index].mutex); return -1; } @@ -596,7 +631,7 @@ int _libusb_poll(struct pollfd *fds, unsigned int nfds, int timeout) handle_to_index[nb_handles_to_wait_on] = i; nb_handles_to_wait_on++; } - pthread_mutex_unlock(&_poll_fd[index].mutex); + LeaveCriticalSection(&_poll_fd[index].mutex); } if (triggered != 0) @@ -616,7 +651,7 @@ int _libusb_poll(struct pollfd *fds, unsigned int nfds, int timeout) fds[i].revents = fds[i].events; triggered++; if (index >= 0) { - pthread_mutex_unlock(&_poll_fd[index].mutex); + LeaveCriticalSection(&_poll_fd[index].mutex); } } else if (ret == WAIT_TIMEOUT) { printb(" timed out\n"); @@ -654,7 +689,7 @@ int _libusb_close(int fd) } else { errno = 0; } - pthread_mutex_unlock(&_poll_fd[index].mutex); + LeaveCriticalSection(&_poll_fd[index].mutex); } return errno?-1:0; } @@ -684,7 +719,7 @@ ssize_t _libusb_write(int fd, const void *buf, size_t count) || (poll_fd[index].rw != RW_WRITE) ) { errno = EBADF; if (index >= 0) { - pthread_mutex_unlock(&_poll_fd[index].mutex); + LeaveCriticalSection(&_poll_fd[index].mutex); } return -1; } @@ -731,10 +766,10 @@ ssize_t _libusb_write(int fd, const void *buf, size_t count) out: if (errno) { reset_overlapped(poll_fd[index].overlapped); - pthread_mutex_unlock(&_poll_fd[index].mutex); + LeaveCriticalSection(&_poll_fd[index].mutex); return -1; } else { - pthread_mutex_unlock(&_poll_fd[index].mutex); + LeaveCriticalSection(&_poll_fd[index].mutex); return (ssize_t)wr_count; } } @@ -830,7 +865,7 @@ ssize_t _libusb_read(int fd, void *buf, size_t count) out: // Setup pending read I/O for the marker _init_read_marker(index); - pthread_mutex_unlock(&_poll_fd[index].mutex); + LeaveCriticalSection(&_poll_fd[index].mutex); if (errno) return -1; else diff --git a/libusb/os/windows_usb.c b/libusb/os/windows_usb.c index c425004..1b71c42 100644 --- a/libusb/os/windows_usb.c +++ b/libusb/os/windows_usb.c @@ -28,8 +28,7 @@ #include <ctype.h> #include <errno.h> #include <fcntl.h> -#include <sched.h> -#include <pthread.h> +#include <process.h> #include <stdio.h> #include <inttypes.h> #include <objbase.h> // for string to GUID conv. requires libole32.a @@ -42,23 +41,6 @@ #pragma warning(disable:28719) #endif -// These GUIDs appear undefined on MinGW32 -#if !defined(GUID_DEVINTERFACE_USB_HOST_CONTROLLER) - // http://msdn.microsoft.com/en-us/library/bb663109.aspx - const GUID GUID_DEVINTERFACE_USB_HOST_CONTROLLER = { 0x3ABF6F2D, 0x71C4, 0x462A, {0x8A, 0x92, 0x1E, 0x68, 0x61, 0xE6, 0xAF, 0x27} }; -#endif - -#if !defined(GUID_DEVINTERFACE_USB_DEVICE) - // http://msdn.microsoft.com/en-us/library/bb663093.aspx - const GUID GUID_DEVINTERFACE_USB_DEVICE = { 0xA5DCBF10, 0x6530, 0x11D2, {0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED} }; -#endif - -// Additional GUID constants. -const GUID GUID_NULL = { 0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} }; -const GUID GUID_HID = { 0x745A17A0, 0x74D3, 0x11D0, {0xB6, 0xFE, 0x00, 0xA0, 0xC9, 0x0F, 0x57, 0xDA} }; -const GUID GUID_LIBUSB_WINUSB = { 0x78a1c341, 0x4539, 0x11d3, {0xb8, 0x8d, 0x00, 0xc0, 0x4f, 0xad, 0x51, 0x71} }; -const GUID GUID_COMPOSITE = { 0x36fc9e60, 0xc465, 0x11cf, {0x80, 0x56, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00} }; - // The 2 macros below are used in conjunction with safe loops. #define LOOP_CHECK(fcall) { r=fcall; if (r != LIBUSB_SUCCESS) continue; } #define LOOP_BREAK(err) { r=err; continue; } @@ -66,6 +48,7 @@ const GUID GUID_COMPOSITE = { 0x36fc9e60, 0xc465, 0x11cf, {0x80, 0x56, 0x44, // 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); // WinUSB API prototypes static int winusb_init(struct libusb_context *ctx); static int winusb_exit(void); @@ -114,8 +97,17 @@ static int composite_reset_device(struct libusb_device_handle *dev_handle); struct windows_hcd_priv* hcd_root = NULL; uint64_t hires_frequency, hires_ticks_to_ps; const uint64_t epoch_time = 116444736000000000; // 1970.01.01 00:00:000 in MS Filetime -DWORD_PTR old_affinity_mask; enum windows_version windows_version = WINDOWS_UNSUPPORTED; +// Concurrency +static int concurrent_usage = -1; +// Timer thread +// NB: index 0 is for monotonic, 1 is for real-time clock and 2 is for the thread exit event +HANDLE timer_thread = NULL; +HANDLE timer_mutex[2] = {NULL, NULL}; +struct timespec timer_tp[2]; +volatile LONG request_count[3] = {0, 0, 1}; // last one must be > 0 +HANDLE timer_request[3] = { NULL, NULL, NULL }; +HANDLE timer_response[2] = { NULL, NULL }; // API globals bool api_winusb_available = false; #define CHECK_WINUSB_AVAILABLE do { if (!api_winusb_available) return LIBUSB_ERROR_ACCESS; } while (0) @@ -158,7 +150,7 @@ static inline BOOLEAN guid_eq(const GUID *guid1, const GUID *guid2) { #if 0 static char* guid_to_string(const GUID guid) { -static char guid_string[GUID_STRING_LENGTH]; + static char guid_string[MAX_GUID_STRING_LENGTH]; sprintf(guid_string, "{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}", (unsigned int)guid.Data1, guid.Data2, guid.Data3, @@ -365,102 +357,165 @@ static int windows_init(struct libusb_context *ctx) SP_DEVICE_INTERFACE_DETAIL_DATA *dev_interface_details = NULL; GUID guid; libusb_bus_t bus; - int i, r = LIBUSB_SUCCESS; - LARGE_INTEGER li_frequency; + int i, r = LIBUSB_ERROR_OTHER; OSVERSIONINFO os_version; - - // We maintain a chained list of the Host Controllers found - struct windows_hcd_priv** _hcd_cur = &hcd_root; - - // If our HCD list is populated, we don't need to re-init - if (hcd_root != NULL) { - usbi_dbg("init already occured."); - return LIBUSB_SUCCESS; + HANDLE semaphore; + struct windows_hcd_priv** _hcd_cur; + TCHAR sem_name[11+1+8]; // strlen(libusb_init)+'\0'+(32-bit hex PID) + + sprintf(sem_name, "libusb_init%08X", GetCurrentProcessId()&0xFFFFFFFF); + semaphore = CreateSemaphore(NULL, 1, 1, sem_name); + if (semaphore == NULL) { + usbi_err(ctx, "could not create semaphore: %s", windows_error_str(0)); + return LIBUSB_ERROR_OTHER; } - // Detect OS version - memset(&os_version, 0, sizeof(OSVERSIONINFO)); - os_version.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); - windows_version = WINDOWS_UNSUPPORTED; - if ((GetVersionEx(&os_version) != 0) && (os_version.dwPlatformId == VER_PLATFORM_WIN32_NT)) { - if ((os_version.dwMajorVersion == 5) && (os_version.dwMinorVersion == 1)) { - windows_version = WINDOWS_XP; - } else if (os_version.dwMajorVersion >= 6) { - windows_version = WINDOWS_VISTA_AND_LATER; + // 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_OTHER; + } + + // 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 ( ++concurrent_usage == 0 ) { // First init? + _hcd_cur = &hcd_root; + + // Detect OS version + memset(&os_version, 0, sizeof(OSVERSIONINFO)); + os_version.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + windows_version = WINDOWS_UNSUPPORTED; + if ((GetVersionEx(&os_version) != 0) && (os_version.dwPlatformId == VER_PLATFORM_WIN32_NT)) { + if ((os_version.dwMajorVersion == 5) && (os_version.dwMinorVersion == 1)) { + windows_version = WINDOWS_XP; + } else if (os_version.dwMajorVersion >= 6) { + windows_version = WINDOWS_VISTA_AND_LATER; + } + } + if (windows_version == WINDOWS_UNSUPPORTED) { + usbi_err(ctx, "This version of Windows is NOT supported"); + r = LIBUSB_ERROR_NOT_SUPPORTED; + goto init_exit; } - } - if (windows_version == WINDOWS_UNSUPPORTED) { - usbi_err(ctx, "This version of Windows is NOT supported"); - return LIBUSB_ERROR_NOT_SUPPORTED; - } - // Initialize pollable file descriptors - init_polling(); + // Initialize pollable file descriptors + init_polling(); - // Initialize the low level APIs - for (i=0; i<USB_API_MAX; i++) { - r = usb_api_backend[i].init(ctx); - if (r != LIBUSB_SUCCESS) { - return r; + // Initialize the low level APIs + for (i=0; i<USB_API_MAX; i++) { + r = usb_api_backend[i].init(ctx); + if (r != LIBUSB_SUCCESS) { + goto init_exit; + } } - } - // Because QueryPerformanceCounter might report different values when - // running on different cores, we glue the libusb main thread to the - // first core to prevent timing discrepancies. - // TODO? create a seperate thread for timer and detect single core - old_affinity_mask = SetThreadAffinityMask(GetCurrentThread(), 0); + // 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 core always to prevent timing discrepancies. + r = LIBUSB_ERROR_NO_MEM; + for (i = 0; i < 3; i++) { + timer_request[i] = CreateEvent(NULL, TRUE, FALSE, NULL); + if (timer_request[i] == NULL) { + usbi_err(ctx, "could not create timer request event %d - aborting", i); + goto init_exit; + } + if (i==2) + break; // 3rd event has no matching parts, because it's for quitting the thread. + timer_response[i] = CreateSemaphore(NULL, 0, MAX_TIMER_SEMAPHORES, NULL); + if (timer_request[i] == NULL) { + usbi_err(ctx, "could not create timer response semaphore %d - aborting", i); + goto init_exit; + } + timer_mutex[i] = CreateMutex(NULL, FALSE, NULL); + if (timer_mutex[i] == NULL) { + usbi_err(ctx, "could not create timer mutex %d - aborting", i); + goto init_exit; + } + } + timer_thread = (HANDLE)_beginthreadex(NULL, 0, windows_clock_gettime_threaded, NULL, 0, NULL); + if (timer_thread == NULL) { + usbi_err(ctx, "Unable to create timer thread - aborting"); + goto init_exit; + } + SetThreadAffinityMask(timer_thread, 0); - // Find out if we have access to a monotonic (hires) timer - if (!QueryPerformanceFrequency(&li_frequency)) { - usbi_dbg("no hires timer available on this platform"); - hires_frequency = 0; - hires_ticks_to_ps = 0; - } else { - hires_frequency = li_frequency.QuadPart; - // 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_ticks_to_ps = 1000000000000 / hires_frequency; - usbi_dbg("hires timer available (Frequency: %"PRIu64" Hz)", hires_frequency); - } + guid = GUID_DEVINTERFACE_USB_HOST_CONTROLLER; - guid = GUID_DEVINTERFACE_USB_HOST_CONTROLLER; + r = LIBUSB_SUCCESS; + for (bus = 0; ; bus++) + { + // safe loop: free up any (unprotected) dynamic resource + // NB: this is always executed before breaking the loop + safe_free(dev_interface_details); + safe_free(*_hcd_cur); - for (bus = 0; ; bus++) - { - // safe loop: free up any (unprotected) dynamic resource - // NB: this is always executed before breaking the loop - safe_free(dev_interface_details); - safe_free(*_hcd_cur); + dev_interface_details = get_interface_details(ctx, &dev_info, NULL, guid, bus); + // safe loop: end of loop condition + if ((dev_interface_details == NULL) || (r != LIBUSB_SUCCESS)) + break; - dev_interface_details = get_interface_details(ctx, &dev_info, NULL, guid, bus); - // safe loop: end of loop condition - if ((dev_interface_details == NULL) || (r != LIBUSB_SUCCESS)) - break; + // Will need to change storage and size of libusb_bus_t if this ever occurs + if (bus == LIBUSB_BUS_MAX) { + usbi_warn(ctx, "program assertion failed - found more than %d buses, skipping the rest.", LIBUSB_BUS_MAX); + continue; + } - // Will need to change storage and size of libusb_bus_t if this ever occurs - if (bus == LIBUSB_BUS_MAX) { - usbi_warn(ctx, "program assertion failed - found more than %d buses, skipping the rest.", LIBUSB_BUS_MAX); - continue; - } + // Allocate and init a new priv structure to hold our data + if ((*_hcd_cur = malloc(sizeof(struct windows_hcd_priv))) == NULL) { + usbi_err(ctx, "could not allocate private structure for bus %u. aborting.", bus); + LOOP_BREAK(LIBUSB_ERROR_NO_MEM); + } + windows_hcd_priv_init(*_hcd_cur); + (*_hcd_cur)->path = sanitize_path(dev_interface_details->DevicePath); - // Allocate and init a new priv structure to hold our data - if ((*_hcd_cur = malloc(sizeof(struct windows_hcd_priv))) == NULL) { - usbi_err(ctx, "could not allocate private structure for bus %u. aborting.", bus); - LOOP_BREAK(LIBUSB_ERROR_NO_MEM); + _hcd_cur = &((*_hcd_cur)->next); } - windows_hcd_priv_init(*_hcd_cur); - (*_hcd_cur)->path = sanitize_path(dev_interface_details->DevicePath); - - _hcd_cur = &((*_hcd_cur)->next); + // TODO: event thread for hotplug (see darwin source) } - // TODO: event thread for hotplug (see darwin source) - if (hcd_root == NULL) - return LIBUSB_ERROR_NO_DEVICE; + r = LIBUSB_ERROR_NO_DEVICE; else - return LIBUSB_SUCCESS; + r = LIBUSB_SUCCESS; + +init_exit: // Holds semaphore here. + if(!concurrent_usage && r != LIBUSB_SUCCESS) { // First init failed? + if (timer_thread) { + SetEvent(timer_request[2]); // actually the signal to quit the thread. + if (WAIT_OBJECT_0 != WaitForSingleObject(timer_thread, INFINITE)) { + 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; + } + for (i = 0; i < 3; i++) { + if (timer_request[i]) { + CloseHandle(timer_request[i]); + timer_request[i] = NULL; + } + if (i==2) + break; // 3rd event has no matching parts, because it's for quitting the thread. + if (timer_response[i]) { + CloseHandle(timer_response[i]); + timer_response[i] = NULL; + } + if (timer_mutex[i]) { + CloseHandle(timer_mutex[i]); + timer_mutex[i] = NULL; + } + } + } + + if (r != LIBUSB_SUCCESS) + --concurrent_usage; // Not expected to call libusb_exit if we failed. + + ReleaseSemaphore(semaphore, 1, NULL); // increase count back to 1 + CloseHandle(semaphore); + return r; } /* @@ -879,7 +934,7 @@ enum libusb_hid_report_type { SP_DEVINFO_DATA dev_info_data; SP_DEVICE_INTERFACE_DETAIL_DATA *dev_interface_details = NULL; HKEY key; - WCHAR guid_string_w[GUID_STRING_LENGTH]; + WCHAR guid_string_w[MAX_GUID_STRING_LENGTH]; GUID guid, class_guid; GUID guid_table[MAX_USB_DEVICES]; char* sanitized_path[MAX_USB_DEVICES]; @@ -1268,7 +1323,7 @@ static int set_device_paths(struct libusb_context *ctx, struct discovered_devs * if (priv->hid == NULL) { usbi_err(ctx, "could not allocate HID data for %s, skipping", dev_interface_details->DevicePath); - priv->apib = &usb_api_backend[USB_API_TEMPLATE]; + priv->apib = &usb_api_backend[USB_API_UNSUPPORTED]; safe_free(priv->path); } else { set_hid_device(ctx, priv); @@ -1345,23 +1400,68 @@ static void windows_exit(void) { struct windows_hcd_priv* hcd_tmp; int i; + HANDLE semaphore; + TCHAR sem_name[11+1+8]; // strlen(libusb_init)+'\0'+(32-bit hex PID) - while (hcd_root != NULL) - { - hcd_tmp = hcd_root; // Keep a copy for free - hcd_root = hcd_root->next; - windows_hcd_priv_release(hcd_tmp); - safe_free(hcd_tmp); + sprintf(sem_name, "libusb_init%08X", GetCurrentProcessId()&0xFFFFFFFF); + semaphore = CreateSemaphore(NULL, 1, 1, sem_name); + if (semaphore == NULL) { + return; } - for (i=0; i<USB_API_MAX; i++) { - usb_api_backend[i].exit(); - } - exit_polling(); + // 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; + } + + // Only works if exits and inits are balanced exactly + if (--concurrent_usage < 0) { // Last exit + while (hcd_root != NULL) + { + hcd_tmp = hcd_root; // Keep a copy for free + hcd_root = hcd_root->next; + windows_hcd_priv_release(hcd_tmp); + safe_free(hcd_tmp); + } - SetThreadAffinityMask(GetCurrentThread(), old_affinity_mask); + for (i=0; i<USB_API_MAX; i++) { + usb_api_backend[i].exit(); + } + exit_polling(); - // TODO: delete event thread for hotplug (see darwin source) + if (timer_thread) { + SetEvent(timer_request[2]); // actually the signal to quit the thread. + if (WAIT_OBJECT_0 != WaitForSingleObject(timer_thread, INFINITE)) { + usbi_dbg("could not wait for timer thread to quit"); + TerminateThread(timer_thread, 1); + } + CloseHandle(timer_thread); + timer_thread = NULL; + } + for (i = 0; i < 3; i++) { + if (timer_request[i]) { + CloseHandle(timer_request[i]); + timer_request[i] = NULL; + } + if (i==2) + break; // 3rd event has no matching parts, because it's for quitting the thread. + if (timer_response[i]) { + CloseHandle(timer_response[i]); + timer_response[i] = NULL; + } + if (timer_mutex[i]) { + CloseHandle(timer_mutex[i]); + timer_mutex[i] = NULL; + } + } + + // TODO: delete event thread for hotplug (see darwin source) + } + + ReleaseSemaphore(semaphore, 1, NULL); // increase count back to 1 + CloseHandle(semaphore); } static int windows_get_device_descriptor(struct libusb_device *dev, unsigned char *buffer, int *host_endian) @@ -1752,34 +1852,114 @@ static int windows_handle_events(struct libusb_context *ctx, struct pollfd *fds, /* * Monotonic and real time functions */ -static int windows_clock_gettime(int clk_id, struct timespec *tp) +unsigned __stdcall windows_clock_gettime_threaded(void* param) { - LARGE_INTEGER hires_counter; + LARGE_INTEGER hires_counter, li_frequency; FILETIME ftime; ULARGE_INTEGER rtime; + LONG nb_responses; + int i, timer_index; - switch(clk_id) { - case USBI_CLOCK_MONOTONIC: + // Find out if we have access to a monotonic (hires) timer + if (!QueryPerformanceFrequency(&li_frequency)) { + usbi_dbg("no hires timer available on this platform"); + hires_frequency = 0; + hires_ticks_to_ps = 0; + } else { + hires_frequency = li_frequency.QuadPart; + // 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_ticks_to_ps = 1000000000000 / hires_frequency; + usbi_dbg("hires timer available (Frequency: %"PRIu64" Hz)", hires_frequency); + } + while (1) { + timer_index = WaitForMultipleObjects(3, timer_request, FALSE, INFINITE) - WAIT_OBJECT_0; + if (request_count[timer_index] == 0) { + // Request already handled + ResetEvent(timer_request[timer_index]); + // There's still a possiblity that a thread sends a request between the + // time we test request_count[] == 0 and we reset the event, in which case + // the request would be ignored. The simple solution to that is to test + // request_count again and process requests if non zero. + if (request_count[timer_index] == 0) + continue; + } + i = 0; + switch (timer_index) { + case 0: // monotonic clock // If hires_frequency is set, we have an hires monotonic timer available if ((hires_frequency != 0) && (QueryPerformanceCounter(&hires_counter) != 0)) { - tp->tv_sec = (long)(hires_counter.QuadPart / hires_frequency); - tp->tv_nsec = (long)(((hires_counter.QuadPart % hires_frequency)/1000) * hires_ticks_to_ps); - return LIBUSB_SUCCESS; - } - // make sure we fall through to real-time if we can't get hires timer + WaitForSingleObject(timer_mutex[i], INFINITE); + timer_tp[i].tv_sec = (long)(hires_counter.QuadPart / hires_frequency); + timer_tp[i].tv_nsec = (long)(((hires_counter.QuadPart % hires_frequency)/1000) * hires_ticks_to_ps); + ReleaseMutex(timer_mutex[i]); + goto send_response; + } + i--; // make sure we fall through and return real-time if we can't get monotonic + case 1: // real-time clock + i++; + // 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(&ftime); + rtime.LowPart = ftime.dwLowDateTime; + rtime.HighPart = ftime.dwHighDateTime; + rtime.QuadPart -= epoch_time; + WaitForSingleObject(timer_mutex[i], INFINITE); + timer_tp[i].tv_sec = (long)(rtime.QuadPart / 10000000); + timer_tp[i].tv_nsec = (long)((rtime.QuadPart % 10000000)*100); + ReleaseMutex(timer_mutex[i]); + goto send_response; + case 2: // time to quit + usbi_dbg("timer thread quitting"); + return 0; + default: + usbi_dbg("failure to wait on requests: %s", windows_error_str(0)); + continue; + send_response: + nb_responses = InterlockedExchange((LONG*)&request_count[i], 0); + if ( (nb_responses) + && (ReleaseSemaphore(timer_response[i], nb_responses, NULL) == 0) ) { + usbi_dbg("unable to release timer semaphore %d: %s", windows_error_str(0)); + } + continue; + } + } + usbi_dbg("ERROR: broken timer thread"); + return 1; +} + +static int windows_clock_gettime(int clk_id, struct timespec *tp) +{ + int i; + DWORD r; + i = -1; + switch(clk_id) { 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(&ftime); - rtime.LowPart = ftime.dwLowDateTime; - rtime.HighPart = ftime.dwHighDateTime; - rtime.QuadPart -= epoch_time; - tp->tv_sec = (long)(rtime.QuadPart / 10000000); - tp->tv_nsec = (long)((rtime.QuadPart % 10000000)*100); - return LIBUSB_SUCCESS; + i++; + // fall through + case USBI_CLOCK_MONOTONIC: + i++; + while (1) { + InterlockedIncrement((LONG*)&request_count[i]); + SetEvent(timer_request[i]); + // TODO: adjust the timeout/make it dynamic? + r = WaitForSingleObject(timer_response[i], 100); + switch(r) { + case WAIT_OBJECT_0: + WaitForSingleObject(timer_mutex[i], INFINITE); + *tp = timer_tp[i]; + ReleaseMutex(timer_mutex[i]); + return LIBUSB_SUCCESS; + case WAIT_TIMEOUT: + usbi_dbg("could not obtain a timer value within reasonable timeframe - too much load?"); + break; + default: + usbi_dbg("WaitForSingleObject failed: %s", windows_error_str(0)); + return LIBUSB_ERROR_OTHER; + } + } default: return LIBUSB_ERROR_INVALID_PARAM; } @@ -1881,10 +2061,9 @@ static int unsupported_abort_transfers(struct usbi_transfer *itransfer) { const struct windows_usb_api_backend usb_api_backend[USB_API_MAX] = { { - "UNSUPPORTED", + USB_API_UNSUPPORTED, + &CLASS_GUID_UNSUPPORTED, "_UNSUPPORTED_", - &GUID_NULL, - USB_API_TEMPLATE, unsupported_init, unsupported_exit, unsupported_open, @@ -1900,10 +2079,9 @@ const struct windows_usb_api_backend usb_api_backend[USB_API_MAX] = { unsupported_abort_control, unsupported_abort_transfers, }, { - "Composite", - "usbccgp", - &GUID_COMPOSITE, USB_API_COMPOSITE, + &CLASS_GUID_COMPOSITE, + "usbccgp", composite_init, composite_exit, composite_open, @@ -1919,10 +2097,9 @@ const struct windows_usb_api_backend usb_api_backend[USB_API_MAX] = { composite_abort_control, composite_abort_transfers, }, { - "WinUSB", - "WinUSB", - &GUID_LIBUSB_WINUSB, USB_API_WINUSB, + &CLASS_GUID_LIBUSB_WINUSB, + "WinUSB", winusb_init, winusb_exit, winusb_open, @@ -1938,10 +2115,9 @@ const struct windows_usb_api_backend usb_api_backend[USB_API_MAX] = { winusb_abort_control, winusb_abort_transfers, }, { - "HID", - "HidUsb", - &GUID_HID, USB_API_HID, + &CLASS_GUID_HID, + "HidUsb", hid_init, hid_exit, hid_open, @@ -1989,7 +2165,6 @@ static int winusb_init(struct libusb_context *ctx) static int winusb_exit(void) { - api_winusb_available = false; return LIBUSB_SUCCESS; } @@ -2919,7 +3094,6 @@ static int hid_init(struct libusb_context *ctx) static int hid_exit(void) { - api_hid_available = false; return LIBUSB_SUCCESS; } @@ -2951,7 +3125,6 @@ static int hid_open(struct libusb_device_handle *dev_handle) && (priv->usb_interface[i].apib->id == USB_API_HID) ) { hid_handle = CreateFileA(priv->usb_interface[i].path, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); - usbi_dbg("hid_handle = %p", hid_handle); /* * http://www.lvr.com/hidfaq.htm: Why do I receive "Access denied" when attempting to access my HID? * "Windows 2000 and later have exclusive read/write access to HIDs that are configured as a system diff --git a/libusb/os/windows_usb.h b/libusb/os/windows_usb.h index 6921882..e2801db 100644 --- a/libusb/os/windows_usb.h +++ b/libusb/os/windows_usb.h @@ -62,6 +62,7 @@ extern char *_strdup(const char *strSource); #define safe_strdup _strdup #define safe_sprintf _snprintf #define safe_unref_device(dev) do {if (dev != NULL) {libusb_unref_device(dev); dev = NULL;}} while(0) +#define wchar_to_utf8_ms(wstr, str, strlen) WideCharToMultiByte(CP_UTF8, 0, wstr, -1, str, strlen, NULL, NULL) inline void upperize(char* str) { size_t i; if (str == NULL) return; @@ -74,25 +75,45 @@ inline void upperize(char* str) { #define MAX_USB_STRING_LENGTH 128 #define MAX_HID_REPORT_SIZE 1024 #define MAX_HID_DESCRIPTOR_SIZE 256 - +#define MAX_GUID_STRING_LENGTH 40 #define MAX_PATH_LENGTH 128 #define MAX_KEY_LENGTH 256 +#define MAX_TIMER_SEMAPHORES 128 #define ERR_BUFFER_SIZE 256 -#define GUID_STRING_LENGTH 40 // Handle code for HID interface that have been claimed ("dibs") #define INTERFACE_CLAIMED ((HANDLE)0xD1B5) // Additional return code for HID operations that completed synchronously #define LIBUSB_COMPLETED (LIBUSB_SUCCESS + 1) -#define wchar_to_utf8_ms(wstr, str, strlen) WideCharToMultiByte(CP_UTF8, 0, wstr, -1, str, strlen, NULL, NULL) +// http://msdn.microsoft.com/en-us/library/bb663109.aspx +// http://msdn.microsoft.com/en-us/library/bb663093.aspx +#if !defined(GUID_DEVINTERFACE_USB_HOST_CONTROLLER) +const GUID GUID_DEVINTERFACE_USB_HOST_CONTROLLER = { 0x3ABF6F2D, 0x71C4, 0x462A, {0x8A, 0x92, 0x1E, 0x68, 0x61, 0xE6, 0xAF, 0x27} }; +#endif +#if !defined(GUID_DEVINTERFACE_USB_DEVICE) +const GUID GUID_DEVINTERFACE_USB_DEVICE = { 0xA5DCBF10, 0x6530, 0x11D2, {0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED} }; +#endif + + +/* + * Multiple USB API backend support + */ +#define USB_API_UNSUPPORTED 0 +#define USB_API_COMPOSITE 1 +#define USB_API_WINUSB 2 +#define USB_API_HID 3 +#define USB_API_MAX 4 + +const GUID CLASS_GUID_UNSUPPORTED = { 0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x57, 0xDA} }; +const GUID CLASS_GUID_HID = { 0x745A17A0, 0x74D3, 0x11D0, {0xB6, 0xFE, 0x00, 0xA0, 0xC9, 0x0F, 0x57, 0xDA} }; +const GUID CLASS_GUID_LIBUSB_WINUSB = { 0x78A1C341, 0x4539, 0x11D3, {0xB8, 0x8D, 0x00, 0xC0, 0x4F, 0xAD, 0x51, 0x71} }; +const GUID CLASS_GUID_COMPOSITE = { 0x36FC9E60, 0xC465, 0x11cF, {0x80, 0x56, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00} }; -// This is used to support multiple kernel drivers and USB APIs in Windows. struct windows_usb_api_backend { - const char *name; // A human-readable name for your backend, e.g. "WinUSB" - const char *driver_name; // Driver's name, without .sys, e.g. "usbccgp" - const GUID *class_guid; // The Class GUID (for fallback in case the driver name cannot be read) const uint8_t id; + const GUID *class_guid; // The Class GUID (for fallback in case the driver name cannot be read) + const char *driver_name; // Driver name, without .sys, e.g. "usbccgp" int (*init)(struct libusb_context *ctx); int (*exit)(void); int (*open)(struct libusb_device_handle *dev_handle); @@ -109,11 +130,6 @@ struct windows_usb_api_backend { int (*abort_transfers)(struct usbi_transfer *itransfer); }; -#define USB_API_TEMPLATE 0 -#define USB_API_COMPOSITE 1 -#define USB_API_WINUSB 2 -#define USB_API_HID 3 -#define USB_API_MAX 4 extern const struct windows_usb_api_backend usb_api_backend[USB_API_MAX]; #define PRINT_UNSUPPORTED_API(fname) \ @@ -127,6 +143,7 @@ enum windows_version { WINDOWS_VISTA_AND_LATER, }; + /* * private structures definition * with inline pseudo constructors/destructors @@ -219,7 +236,7 @@ static inline void windows_device_priv_init(struct windows_device_priv* p) { p->parent_dev = NULL; p->connection_index = 0; p->path = NULL; - p->apib = &usb_api_backend[USB_API_TEMPLATE]; + p->apib = &usb_api_backend[USB_API_UNSUPPORTED]; p->composite_api_flags = 0; p->hid = NULL; p->active_config = 0; @@ -227,7 +244,7 @@ static inline void windows_device_priv_init(struct windows_device_priv* p) { memset(&(p->dev_descriptor), 0, sizeof(USB_DEVICE_DESCRIPTOR)); for (i=0; i<USB_MAXINTERFACES; i++) { p->usb_interface[i].path = NULL; - p->usb_interface[i].apib = &usb_api_backend[USB_API_TEMPLATE]; + p->usb_interface[i].apib = &usb_api_backend[USB_API_UNSUPPORTED]; p->usb_interface[i].nb_endpoints = 0; p->usb_interface[i].endpoint = NULL; } @@ -254,7 +271,7 @@ static inline struct windows_device_priv *__device_priv(struct libusb_device *de struct interface_handle_t { HANDLE dev_handle; // WinUSB needs an extra handle for the file - HANDLE api_handle; // used by the API to communicate with the device + HANDLE api_handle; // used by the API to communicate with the device }; struct windows_device_handle_priv { @@ -388,7 +405,7 @@ CMAPI CONFIGRET WINAPI CM_Get_Device_IDW( #endif /* UNICODE */ #define IOCTL_USB_GET_HUB_CAPABILITIES_EX \ - CTL_CODE( FILE_DEVICE_USB, USB_GET_HUB_CAPABILITIES_EX, METHOD_BUFFERED, FILE_ANY_ACCESS) + CTL_CODE( FILE_DEVICE_USB, USB_GET_HUB_CAPABILITIES_EX, METHOD_BUFFERED, FILE_ANY_ACCESS) #define IOCTL_USB_GET_HUB_CAPABILITIES \ CTL_CODE(FILE_DEVICE_USB, USB_GET_HUB_CAPABILITIES, METHOD_BUFFERED, FILE_ANY_ACCESS) |