Windows: Rework poll() emulation to a much simpler and effective design
authorChris Dickens <christopher.a.dickens@gmail.com>
Sun, 7 Jan 2018 05:43:12 +0000 (21:43 -0800)
committerChris Dickens <christopher.a.dickens@gmail.com>
Sun, 7 Jan 2018 05:43:12 +0000 (21:43 -0800)
The previous poll() implementation worked okay but had some issues. It
was inefficient, had a large footprint, and there were simply some use
cases that didn't work (e.g. a synchronous transfer that completes when
no other event or transfer is pending would not be processed until the
next poll() timeout).

This commit introduces a new, simpler design that simply associates an
OVERLAPPED structure to an integer that acts as a file descriptor. The
poll() emulation now solely cares about the OVERLAPPED structure, not
transfers or HANDLEs or cancelation functions. These details have been
moved up into the higher OS-specific layers.

For Windows NT environments, several deficiencies have been addressed:

1) It was previously possible to successfully submit a transfer but fail
   to add the "file descriptor" to the pollfd set. This was silently
   ignored and would result in the user never seeing the transfer being
   completed.

2) Synchronously completed transfers would previously not be processed
   unless another event (such as a timeout or other transfer completion)
   was processed.

3) Canceling any one transfer on an endpoint would previously result in
   *all* transfers on that endpoint being canceled, due to the use of
   the AbortPipe() function.

This commit addresses all of these issues. In particular, run-time
detection of the CancelIoEx() function will allow the user to cancel a
single outstanding transfer without affecting any others still in
process.

Signed-off-by: Chris Dickens <christopher.a.dickens@gmail.com>
libusb/os/poll_windows.c
libusb/os/poll_windows.h
libusb/os/threads_windows.h
libusb/os/wince_usb.c
libusb/os/windows_nt_common.c
libusb/os/windows_nt_common.h
libusb/os/windows_usbdk.c
libusb/os/windows_winusb.c
libusb/os/windows_winusb.h
libusb/version_nano.h

index 9825607..654965b 100644 (file)
@@ -1,9 +1,6 @@
 /*
  * poll_windows: poll compatibility wrapper for Windows
- * Copyright © 2012-2013 RealVNC Ltd.
- * Copyright © 2009-2010 Pete Batard <pete@akeo.ie>
- * With contributions from Michael Plante, Orin Eman et al.
- * Parts of poll implementation from libusb-win32, by Stephan Meyer et al.
+ * Copyright © 2017 Chris Dickens <christopher.a.dickens@gmail.com>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -31,9 +28,6 @@
  * - obtain a Windows HANDLE to a file or device that has been opened in
  *   OVERLAPPED mode
  * - call usbi_create_fd with this handle to obtain a custom fd.
- *   Note that if you need simultaneous R/W access, you need to call create_fd
- *   twice, once in RW_READ and once in RW_WRITE mode to obtain 2 separate
- *   pollable fds
  * - leave the core functions call the poll routine and flag POLLIN/POLLOUT
  *
  * The pipe pollable synchronous I/O works using the overlapped event associated
  */
 #include <config.h>
 
+#include <assert.h>
 #include <errno.h>
-#include <stdio.h>
 #include <stdlib.h>
 
 #include "libusbi.h"
-
-// Uncomment to debug the polling layer
-//#define DEBUG_POLL_WINDOWS
-#if defined(DEBUG_POLL_WINDOWS)
-#define poll_dbg usbi_dbg
-#else
-// MSVC++ < 2005 cannot use a variadic argument and non MSVC
-// compilers produce warnings if parenthesis are omitted.
-#if defined(_MSC_VER) && (_MSC_VER < 1400)
-#define poll_dbg
-#else
-#define poll_dbg(...)
-#endif
-#endif
-
-#if defined(_PREFAST_)
-#pragma warning(disable:28719)
-#endif
-
-#define CHECK_INIT_POLLING do {if(!is_polling_set) init_polling();} while(0)
+#include "windows_common.h"
 
 // public fd data
-const struct winfd INVALID_WINFD = {-1, INVALID_HANDLE_VALUE, NULL, NULL, NULL, RW_NONE};
-struct winfd poll_fd[MAX_FDS];
-// internal fd data
-struct {
-       CRITICAL_SECTION mutex; // lock for fds
-       // Additional variables for XP CancelIoEx partial emulation
-       HANDLE original_handle;
-       DWORD thread_id;
-} _poll_fd[MAX_FDS];
-
-// globals
-BOOLEAN is_polling_set = FALSE;
-LONG pipe_number = 0;
-static volatile LONG compat_spinlock = 0;
-
-#if !defined(_WIN32_WCE)
-// CancelIoEx, available on Vista and later only, provides the ability to cancel
-// a single transfer (OVERLAPPED) when used. As it may not be part of any of the
-// platform headers, we hook into the Kernel32 system DLL directly to seek it.
-static BOOL (__stdcall *pCancelIoEx)(HANDLE, LPOVERLAPPED) = NULL;
-#define Use_Duplicate_Handles (pCancelIoEx == NULL)
-
-static inline void setup_cancel_io(void)
-{
-       HMODULE hKernel32 = GetModuleHandleA("KERNEL32");
-       if (hKernel32 != NULL) {
-               pCancelIoEx = (BOOL (__stdcall *)(HANDLE,LPOVERLAPPED))
-                       GetProcAddress(hKernel32, "CancelIoEx");
-       }
-       usbi_dbg("Will use CancelIo%s for I/O cancellation",
-               Use_Duplicate_Handles?"":"Ex");
-}
-
-static inline BOOL cancel_io(int _index)
-{
-       if ((_index < 0) || (_index >= MAX_FDS)) {
-               return FALSE;
-       }
-
-       if ( (poll_fd[_index].fd < 0) || (poll_fd[_index].handle == INVALID_HANDLE_VALUE)
-         || (poll_fd[_index].handle == 0) || (poll_fd[_index].overlapped == NULL) ) {
-               return TRUE;
-       }
-       if (poll_fd[_index].itransfer && poll_fd[_index].cancel_fn) {
-               // Cancel outstanding transfer via the specific callback
-               (*poll_fd[_index].cancel_fn)(poll_fd[_index].itransfer);
-               return TRUE;
-       }
-       if (pCancelIoEx != NULL) {
-               return (*pCancelIoEx)(poll_fd[_index].handle, poll_fd[_index].overlapped);
-       }
-       if (_poll_fd[_index].thread_id == GetCurrentThreadId()) {
-               return CancelIo(poll_fd[_index].handle);
-       }
-       usbi_warn(NULL, "Unable to cancel I/O that was started from another thread");
-       return FALSE;
-}
-#else
-#define Use_Duplicate_Handles FALSE
-
-static __inline void setup_cancel_io()
-{
-       // No setup needed on WinCE
-}
-
-static __inline BOOL cancel_io(int _index)
-{
-       if ((_index < 0) || (_index >= MAX_FDS)) {
-               return FALSE;
-       }
-       if ( (poll_fd[_index].fd < 0) || (poll_fd[_index].handle == INVALID_HANDLE_VALUE)
-         || (poll_fd[_index].handle == 0) || (poll_fd[_index].overlapped == NULL) ) {
-               return TRUE;
-       }
-       if (poll_fd[_index].itransfer && poll_fd[_index].cancel_fn) {
-               // Cancel outstanding transfer via the specific callback
-               (*poll_fd[_index].cancel_fn)(poll_fd[_index].itransfer);
-       }
-       return TRUE;
-}
-#endif
-
-// Init
-void init_polling(void)
-{
-       int i;
-
-       while (InterlockedExchange((LONG *)&compat_spinlock, 1) == 1) {
-               SleepEx(0, TRUE);
-       }
-       if (!is_polling_set) {
-               setup_cancel_io();
-               for (i=0; i<MAX_FDS; i++) {
-                       poll_fd[i] = INVALID_WINFD;
-                       _poll_fd[i].original_handle = INVALID_HANDLE_VALUE;
-                       _poll_fd[i].thread_id = 0;
-                       InitializeCriticalSection(&_poll_fd[i].mutex);
-               }
-               is_polling_set = TRUE;
-       }
-       InterlockedExchange((LONG *)&compat_spinlock, 0);
-}
+const struct winfd INVALID_WINFD = { -1, NULL };
 
-// Internal function to retrieve the table index (and lock the fd mutex)
-static int _fd_to_index_and_lock(int fd)
-{
-       int i;
+// private data
+struct file_descriptor {
+       enum fd_type { FD_TYPE_PIPE, FD_TYPE_TRANSFER } type;
+       OVERLAPPED overlapped;
+};
 
-       if (fd < 0)
-               return -1;
+static usbi_mutex_static_t fd_table_lock = USBI_MUTEX_INITIALIZER;
+static struct file_descriptor *fd_table[MAX_FDS];
 
-       for (i=0; i<MAX_FDS; i++) {
-               if (poll_fd[i].fd == fd) {
-                       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;
-               }
-       }
-       return -1;
-}
-
-static OVERLAPPED *create_overlapped(void)
+static struct file_descriptor *create_fd(enum fd_type type)
 {
-       OVERLAPPED *overlapped = (OVERLAPPED*) calloc(1, sizeof(OVERLAPPED));
-       if (overlapped == NULL) {
+       struct file_descriptor *fd = calloc(1, sizeof(*fd));
+       if (fd == NULL)
                return NULL;
-       }
-       overlapped->hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
-       if(overlapped->hEvent == NULL) {
-               free (overlapped);
+       fd->overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+       if (fd->overlapped.hEvent == NULL) {
+               free(fd);
                return NULL;
        }
-       return overlapped;
+       fd->type = type;
+       return fd;
 }
 
-static void free_overlapped(OVERLAPPED *overlapped)
+static void free_fd(struct file_descriptor *fd)
 {
-       if (overlapped == NULL)
-               return;
-
-       if ( (overlapped->hEvent != 0)
-         && (overlapped->hEvent != INVALID_HANDLE_VALUE) ) {
-               CloseHandle(overlapped->hEvent);
-       }
-       free(overlapped);
-}
-
-void exit_polling(void)
-{
-       int i;
-
-       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)
-                       cancel_io(i);
-                       // 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);
-                       free_overlapped(poll_fd[i].overlapped);
-                       if (Use_Duplicate_Handles) {
-                               // Close duplicate handle
-                               if (_poll_fd[i].original_handle != INVALID_HANDLE_VALUE) {
-                                       CloseHandle(poll_fd[i].handle);
-                               }
-                       }
-                       poll_fd[i] = INVALID_WINFD;
-                       LeaveCriticalSection(&_poll_fd[i].mutex);
-                       DeleteCriticalSection(&_poll_fd[i].mutex);
-               }
-       }
-       InterlockedExchange((LONG *)&compat_spinlock, 0);
+       CloseHandle(fd->overlapped.hEvent);
+       free(fd);
 }
 
 /*
- * Create a fake pipe.
- * As libusb only uses pipes for signaling, all we need from a pipe is an
- * event. To that extent, we create a single wfd and overlapped as a means
- * to access that event.
- */
-int usbi_pipe(int filedes[2])
-{
-       int i;
-       OVERLAPPED* overlapped;
-
-       CHECK_INIT_POLLING;
-
-       overlapped = create_overlapped();
-
-       if (overlapped == NULL) {
-               return -1;
-       }
-       // The overlapped must have status pending for signaling to work in poll
-       overlapped->Internal = STATUS_PENDING;
-       overlapped->InternalHigh = 0;
-
-       for (i=0; i<MAX_FDS; i++) {
-               if (poll_fd[i].fd < 0) {
-                       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;
-                       }
-
-                       // Use index as the unique fd number
-                       poll_fd[i].fd = i;
-                       // Read end of the "pipe"
-                       filedes[0] = poll_fd[i].fd;
-                       // We can use the same handle for both ends
-                       filedes[1] = filedes[0];
-
-                       poll_fd[i].handle = DUMMY_HANDLE;
-                       poll_fd[i].overlapped = overlapped;
-                       // There's no polling on the write end, so we just use READ for our needs
-                       poll_fd[i].rw = RW_READ;
-                       _poll_fd[i].original_handle = INVALID_HANDLE_VALUE;
-                       LeaveCriticalSection(&_poll_fd[i].mutex);
-                       return 0;
-               }
-       }
-       free_overlapped(overlapped);
-       return -1;
-}
-
-/*
- * Create both an fd and an OVERLAPPED from an open Windows handle, so that
- * it can be used with our polling function
+ * Create both an fd and an OVERLAPPED, so that it can be used with our
+ * polling function
  * The handle MUST support overlapped transfers (usually requires CreateFile
  * with FILE_FLAG_OVERLAPPED)
  * Return a pollable file descriptor struct, or INVALID_WINFD on error
  *
  * Note that the fd returned by this function is a per-transfer fd, rather
  * than a per-session fd and cannot be used for anything else but our
- * custom functions (the fd itself points to the NUL: device)
+ * custom functions.
  * if you plan to do R/W on the same handle, you MUST create 2 fds: one for
  * read and one for write. Using a single R/W fd is unsupported and will
  * produce unexpected results
  */
-struct winfd usbi_create_fd(HANDLE handle, int access_mode, struct usbi_transfer *itransfer, cancel_transfer *cancel_fn)
+struct winfd usbi_create_fd(void)
 {
-       int i;
-       struct winfd wfd = INVALID_WINFD;
-       OVERLAPPED* overlapped = NULL;
-
-       CHECK_INIT_POLLING;
+       struct file_descriptor *fd;
+       struct winfd wfd;
 
-       if ((handle == 0) || (handle == INVALID_HANDLE_VALUE)) {
+       fd = create_fd(FD_TYPE_TRANSFER);
+       if (fd == NULL)
                return INVALID_WINFD;
-       }
-
-       wfd.itransfer = itransfer;
-       wfd.cancel_fn = cancel_fn;
 
-       if ((access_mode != RW_READ) && (access_mode != RW_WRITE)) {
-               usbi_warn(NULL, "only one of RW_READ or RW_WRITE are supported. "
-                       "If you want to poll for R/W simultaneously, create multiple fds from the same handle.");
-               return INVALID_WINFD;
-       }
-       if (access_mode == RW_READ) {
-               wfd.rw = RW_READ;
-       } else {
-               wfd.rw = RW_WRITE;
+       usbi_mutex_static_lock(&fd_table_lock);
+       for (wfd.fd = 0; wfd.fd < MAX_FDS; wfd.fd++) {
+               if (fd_table[wfd.fd] != NULL)
+                       continue;
+               fd_table[wfd.fd] = fd;
+               break;
        }
+       usbi_mutex_static_unlock(&fd_table_lock);
 
-       overlapped = create_overlapped();
-       if(overlapped == NULL) {
+       if (wfd.fd == MAX_FDS) {
+               free_fd(fd);
                return INVALID_WINFD;
        }
 
-       for (i=0; i<MAX_FDS; i++) {
-               if (poll_fd[i].fd < 0) {
-                       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;
-                       }
-                       // Use index as the unique fd number
-                       wfd.fd = i;
-                       // Attempt to emulate some of the CancelIoEx behaviour on platforms
-                       // that don't have it
-                       if (Use_Duplicate_Handles) {
-                               _poll_fd[i].thread_id = GetCurrentThreadId();
-                               if (!DuplicateHandle(GetCurrentProcess(), handle, GetCurrentProcess(),
-                                       &wfd.handle, 0, TRUE, DUPLICATE_SAME_ACCESS)) {
-                                       usbi_dbg("could not duplicate handle for CancelIo - using original one");
-                                       wfd.handle = handle;
-                                       // Make sure we won't close the original handle on fd deletion then
-                                       _poll_fd[i].original_handle = INVALID_HANDLE_VALUE;
-                               } else {
-                                       _poll_fd[i].original_handle = handle;
-                               }
-                       } else {
-                               wfd.handle = handle;
-                       }
-                       wfd.overlapped = overlapped;
-                       memcpy(&poll_fd[i], &wfd, sizeof(struct winfd));
-                       LeaveCriticalSection(&_poll_fd[i].mutex);
-                       return wfd;
-               }
-       }
-       free_overlapped(overlapped);
-       return INVALID_WINFD;
-}
+       wfd.overlapped = &fd->overlapped;
 
-static void _free_index(int _index)
-{
-       // Cancel any async IO (Don't care about the validity of our handles for this)
-       cancel_io(_index);
-       // close the duplicate handle (if we have an actual duplicate)
-       if (Use_Duplicate_Handles) {
-               if (_poll_fd[_index].original_handle != INVALID_HANDLE_VALUE) {
-                       CloseHandle(poll_fd[_index].handle);
-               }
-               _poll_fd[_index].original_handle = INVALID_HANDLE_VALUE;
-               _poll_fd[_index].thread_id = 0;
-       }
-       free_overlapped(poll_fd[_index].overlapped);
-       poll_fd[_index] = INVALID_WINFD;
+       return wfd;
 }
 
-/*
- * Release a pollable file descriptor.
- *
- * Note that the associated Windows handle is not closed by this call
- */
-void usbi_free_fd(struct winfd *wfd)
+static int check_pollfds(struct pollfd *fds, unsigned int nfds,
+       HANDLE *wait_handles, DWORD *nb_wait_handles)
 {
-       int _index;
+       struct file_descriptor *fd;
+       unsigned int n;
+       int nready = 0;
 
-       CHECK_INIT_POLLING;
+       usbi_mutex_static_lock(&fd_table_lock);
 
-       _index = _fd_to_index_and_lock(wfd->fd);
-       if (_index < 0) {
-               return;
-       }
-       _free_index(_index);
-       *wfd = INVALID_WINFD;
-       LeaveCriticalSection(&_poll_fd[_index].mutex);
-}
-
-/*
- * The functions below perform various conversions between fd, handle and OVERLAPPED
- */
-struct winfd fd_to_winfd(int fd)
-{
-       int i;
-       struct winfd wfd;
-
-       CHECK_INIT_POLLING;
-
-       if (fd < 0)
-               return INVALID_WINFD;
+       for (n = 0; n < nfds; ++n) {
+               fds[n].revents = 0;
 
-       for (i=0; i<MAX_FDS; i++) {
-               if (poll_fd[i].fd == fd) {
-                       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));
-                       LeaveCriticalSection(&_poll_fd[i].mutex);
-                       return wfd;
+               // Keep it simple - only allow either POLLIN *or* POLLOUT
+               assert((fds[n].events == POLLIN) || (fds[n].events == POLLOUT));
+               if ((fds[n].events != POLLIN) && (fds[n].events != POLLOUT)) {
+                       fds[n].revents = POLLNVAL;
+                       nready++;
+                       continue;
                }
-       }
-       return INVALID_WINFD;
-}
-
-struct winfd handle_to_winfd(HANDLE handle)
-{
-       int i;
-       struct winfd wfd;
 
-       CHECK_INIT_POLLING;
+               if ((fds[n].fd >= 0) && (fds[n].fd < MAX_FDS))
+                       fd = fd_table[fds[n].fd];
+               else
+                       fd = NULL;
 
-       if ((handle == 0) || (handle == INVALID_HANDLE_VALUE))
-               return INVALID_WINFD;
+               assert(fd != NULL);
+               if (fd == NULL) {
+                       fds[n].revents = POLLNVAL;
+                       nready++;
+                       continue;
+               }
 
-       for (i=0; i<MAX_FDS; i++) {
-               if (poll_fd[i].handle == handle) {
-                       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);
+               // The following macro only works if overlapped I/O was reported pending
+               if (HasOverlappedIoCompleted(&fd->overlapped)) {
+                       fds[n].revents = fds[n].events;
+                       nready++;
+               } else if (wait_handles != NULL) {
+                       if (*nb_wait_handles == MAXIMUM_WAIT_OBJECTS) {
+                               usbi_warn(NULL, "too many HANDLEs to wait on");
                                continue;
                        }
-                       memcpy(&wfd, &poll_fd[i], sizeof(struct winfd));
-                       LeaveCriticalSection(&_poll_fd[i].mutex);
-                       return wfd;
+                       wait_handles[*nb_wait_handles] = fd->overlapped.hEvent;
+                       (*nb_wait_handles)++;
                }
        }
-       return INVALID_WINFD;
-}
 
-struct winfd overlapped_to_winfd(OVERLAPPED* overlapped)
-{
-       int i;
-       struct winfd wfd;
-
-       CHECK_INIT_POLLING;
+       usbi_mutex_static_unlock(&fd_table_lock);
 
-       if (overlapped == NULL)
-               return INVALID_WINFD;
-
-       for (i=0; i<MAX_FDS; i++) {
-               if (poll_fd[i].overlapped == overlapped) {
-                       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));
-                       LeaveCriticalSection(&_poll_fd[i].mutex);
-                       return wfd;
-               }
-       }
-       return INVALID_WINFD;
+       return nready;
 }
-
 /*
  * POSIX poll equivalent, using Windows OVERLAPPED
  * Currently, this function only accepts one of POLLIN or POLLOUT per fd
@@ -508,143 +174,117 @@ struct winfd overlapped_to_winfd(OVERLAPPED* overlapped)
  */
 int usbi_poll(struct pollfd *fds, unsigned int nfds, int timeout)
 {
-       unsigned i;
-       int _index, object_index, triggered;
-       HANDLE *handles_to_wait_on;
-       int *handle_to_index;
-       DWORD nb_handles_to_wait_on = 0;
+       HANDLE wait_handles[MAXIMUM_WAIT_OBJECTS];
+       DWORD nb_wait_handles = 0;
        DWORD ret;
+       int nready;
 
-       CHECK_INIT_POLLING;
+       nready = check_pollfds(fds, nfds, wait_handles, &nb_wait_handles);
 
-       triggered = 0;
-       handles_to_wait_on = (HANDLE*) calloc(nfds+1, sizeof(HANDLE));  // +1 for fd_update
-       handle_to_index = (int*) calloc(nfds, sizeof(int));
-       if ((handles_to_wait_on == NULL) || (handle_to_index == NULL)) {
-               errno = ENOMEM;
-               triggered = -1;
-               goto poll_exit;
+       // If nothing was triggered, wait on all fds that require it
+       if ((nready == 0) && (nb_wait_handles != 0) && (timeout != 0)) {
+               ret = WaitForMultipleObjects(nb_wait_handles, wait_handles,
+                       FALSE, (timeout < 0) ? INFINITE : (DWORD)timeout);
+               if ((ret >= WAIT_OBJECT_0) && (ret < (WAIT_OBJECT_0 + nb_wait_handles))) {
+                       nready = check_pollfds(fds, nfds, NULL, NULL);
+               } else if (ret != WAIT_TIMEOUT) {
+                       if (ret == WAIT_FAILED)
+                               usbi_err(NULL, "WaitForMultipleObjects failed: %u", (unsigned int)GetLastError());
+                       nready = -1;
+               }
        }
 
-       for (i = 0; i < nfds; ++i) {
-               fds[i].revents = 0;
-
-               // Only one of POLLIN or POLLOUT can be selected with this version of poll (not both)
-               if ((fds[i].events & ~POLLIN) && (!(fds[i].events & POLLOUT))) {
-                       fds[i].revents |= POLLERR;
-                       errno = EACCES;
-                       usbi_warn(NULL, "unsupported set of events");
-                       triggered = -1;
-                       goto poll_exit;
-               }
+       return nready;
+}
 
-               _index = _fd_to_index_and_lock(fds[i].fd);
-               poll_dbg("fd[%d]=%d: (overlapped=%p) got events %04X", i, poll_fd[_index].fd, poll_fd[_index].overlapped, fds[i].events);
+/*
+ * close a fake file descriptor
+ */
+int usbi_close(int _fd)
+{
+       struct file_descriptor *fd;
 
-               if ( (_index < 0) || (poll_fd[_index].handle == INVALID_HANDLE_VALUE)
-                 || (poll_fd[_index].handle == 0) || (poll_fd[_index].overlapped == NULL)) {
-                       fds[i].revents |= POLLNVAL | POLLERR;
-                       errno = EBADF;
-                       if (_index >= 0) {
-                               LeaveCriticalSection(&_poll_fd[_index].mutex);
-                       }
-                       usbi_warn(NULL, "invalid fd");
-                       triggered = -1;
-                       goto poll_exit;
-               }
+       if (_fd < 0 || _fd >= MAX_FDS)
+               goto err_badfd;
 
-               // IN or OUT must match our fd direction
-               if ((fds[i].events & POLLIN) && (poll_fd[_index].rw != RW_READ)) {
-                       fds[i].revents |= POLLNVAL | POLLERR;
-                       errno = EBADF;
-                       usbi_warn(NULL, "attempted POLLIN on fd without READ access");
-                       LeaveCriticalSection(&_poll_fd[_index].mutex);
-                       triggered = -1;
-                       goto poll_exit;
-               }
+       usbi_mutex_static_lock(&fd_table_lock);
+       fd = fd_table[_fd];
+       fd_table[_fd] = NULL;
+       usbi_mutex_static_unlock(&fd_table_lock);
 
-               if ((fds[i].events & POLLOUT) && (poll_fd[_index].rw != RW_WRITE)) {
-                       fds[i].revents |= POLLNVAL | POLLERR;
-                       errno = EBADF;
-                       usbi_warn(NULL, "attempted POLLOUT on fd without WRITE access");
-                       LeaveCriticalSection(&_poll_fd[_index].mutex);
-                       triggered = -1;
-                       goto poll_exit;
-               }
+       if (fd == NULL)
+               goto err_badfd;
 
-               // The following macro only works if overlapped I/O was reported pending
-               if ( (HasOverlappedIoCompleted(poll_fd[_index].overlapped))
-                 || (HasOverlappedIoCompletedSync(poll_fd[_index].overlapped)) ) {
-                       poll_dbg("  completed");
-                       // checks above should ensure this works:
-                       fds[i].revents = fds[i].events;
-                       triggered++;
-               } else {
-                       handles_to_wait_on[nb_handles_to_wait_on] = poll_fd[_index].overlapped->hEvent;
-                       handle_to_index[nb_handles_to_wait_on] = i;
-                       nb_handles_to_wait_on++;
-               }
-               LeaveCriticalSection(&_poll_fd[_index].mutex);
+       if (fd->type == FD_TYPE_PIPE) {
+               // InternalHigh is our reference count
+               fd->overlapped.InternalHigh--;
+               if (fd->overlapped.InternalHigh == 0)
+                       free_fd(fd);
+       } else {
+               free_fd(fd);
        }
 
-       // If nothing was triggered, wait on all fds that require it
-       if ((timeout != 0) && (triggered == 0) && (nb_handles_to_wait_on != 0)) {
-               if (timeout < 0) {
-                       poll_dbg("starting infinite wait for %u handles...", (unsigned int)nb_handles_to_wait_on);
-               } else {
-                       poll_dbg("starting %d ms wait for %u handles...", timeout, (unsigned int)nb_handles_to_wait_on);
-               }
-               ret = WaitForMultipleObjects(nb_handles_to_wait_on, handles_to_wait_on,
-                       FALSE, (timeout<0)?INFINITE:(DWORD)timeout);
-               object_index = ret-WAIT_OBJECT_0;
-               if ((object_index >= 0) && ((DWORD)object_index < nb_handles_to_wait_on)) {
-                       poll_dbg("  completed after wait");
-                       i = handle_to_index[object_index];
-                       _index = _fd_to_index_and_lock(fds[i].fd);
-                       fds[i].revents = fds[i].events;
-                       triggered++;
-                       if (_index >= 0) {
-                               LeaveCriticalSection(&_poll_fd[_index].mutex);
-                       }
-               } else if (ret == WAIT_TIMEOUT) {
-                       poll_dbg("  timed out");
-                       triggered = 0;  // 0 = timeout
-               } else {
-                       errno = EIO;
-                       triggered = -1; // error
-               }
-       }
+       return 0;
 
-poll_exit:
-       if (handles_to_wait_on != NULL) {
-               free(handles_to_wait_on);
-       }
-       if (handle_to_index != NULL) {
-               free(handle_to_index);
-       }
-       return triggered;
+err_badfd:
+       errno = EBADF;
+       return -1;
 }
 
 /*
- * close a fake pipe fd
- */
-int usbi_close(int fd)
+* Create a fake pipe.
+* As libusb only uses pipes for signaling, all we need from a pipe is an
+* event. To that extent, we create a single wfd and overlapped as a means
+* to access that event.
+*/
+int usbi_pipe(int filedes[2])
 {
-       int _index;
-       int r = -1;
+       struct file_descriptor *fd;
+       int r_fd = -1, w_fd = -1;
+       int i;
 
-       CHECK_INIT_POLLING;
+       fd = create_fd(FD_TYPE_PIPE);
+       if (fd == NULL) {
+               errno = ENOMEM;
+               return -1;
+       }
 
-       _index = _fd_to_index_and_lock(fd);
+       // Use InternalHigh as a reference count
+       fd->overlapped.Internal = STATUS_PENDING;
+       fd->overlapped.InternalHigh = 2;
 
-       if (_index < 0) {
-               errno = EBADF;
-       } else {
-               free_overlapped(poll_fd[_index].overlapped);
-               poll_fd[_index] = INVALID_WINFD;
-               LeaveCriticalSection(&_poll_fd[_index].mutex);
+       usbi_mutex_static_lock(&fd_table_lock);
+       do {
+               for (i = 0; i < MAX_FDS; i++) {
+                       if (fd_table[i] != NULL)
+                               continue;
+                       if (r_fd == -1) {
+                               r_fd = i;
+                       } else if (w_fd == -1) {
+                               w_fd = i;
+                               break;
+                       }
+               }
+
+               if (i == MAX_FDS)
+                       break;
+
+               fd_table[r_fd] = fd;
+               fd_table[w_fd] = fd;
+
+       } while (0);
+       usbi_mutex_static_unlock(&fd_table_lock);
+
+       if (i == MAX_FDS) {
+               free_fd(fd);
+               errno = EMFILE;
+               return -1;
        }
-       return r;
+
+       filedes[0] = r_fd;
+       filedes[1] = w_fd;
+
+       return 0;
 }
 
 /*
@@ -652,35 +292,37 @@ int usbi_close(int fd)
  */
 ssize_t usbi_write(int fd, const void *buf, size_t count)
 {
-       int _index;
+       int error = EBADF;
+
        UNUSED(buf);
 
-       CHECK_INIT_POLLING;
+       if (fd < 0 || fd >= MAX_FDS)
+               goto err_out;
 
        if (count != sizeof(unsigned char)) {
                usbi_err(NULL, "this function should only used for signaling");
-               return -1;
+               error = EINVAL;
+               goto err_out;
        }
 
-       _index = _fd_to_index_and_lock(fd);
-
-       if ( (_index < 0) || (poll_fd[_index].overlapped == NULL) ) {
-               errno = EBADF;
-               if (_index >= 0) {
-                       LeaveCriticalSection(&_poll_fd[_index].mutex);
-               }
-               return -1;
+       usbi_mutex_static_lock(&fd_table_lock);
+       if ((fd_table[fd] != NULL) && (fd_table[fd]->type == FD_TYPE_PIPE)) {
+               assert(fd_table[fd]->overlapped.Internal == STATUS_PENDING);
+               assert(fd_table[fd]->overlapped.InternalHigh == 2);
+               fd_table[fd]->overlapped.Internal = STATUS_WAIT_0;
+               SetEvent(fd_table[fd]->overlapped.hEvent);
+               error = 0;
        }
+       usbi_mutex_static_unlock(&fd_table_lock);
 
-       poll_dbg("set pipe event (fd = %d, thread = %08X)", _index, (unsigned int)GetCurrentThreadId());
-       SetEvent(poll_fd[_index].overlapped->hEvent);
-       poll_fd[_index].overlapped->Internal = STATUS_WAIT_0;
-       // If two threads write on the pipe at the same time, we need to
-       // process two separate reads => use the overlapped as a counter
-       poll_fd[_index].overlapped->InternalHigh++;
+       if (error)
+               goto err_out;
 
-       LeaveCriticalSection(&_poll_fd[_index].mutex);
        return sizeof(unsigned char);
+
+err_out:
+       errno = error;
+       return -1;
 }
 
 /*
@@ -688,41 +330,35 @@ ssize_t usbi_write(int fd, const void *buf, size_t count)
  */
 ssize_t usbi_read(int fd, void *buf, size_t count)
 {
-       int _index;
-       ssize_t r = -1;
+       int error = EBADF;
+
        UNUSED(buf);
 
-       CHECK_INIT_POLLING;
+       if (fd < 0 || fd >= MAX_FDS)
+               goto err_out;
 
        if (count != sizeof(unsigned char)) {
                usbi_err(NULL, "this function should only used for signaling");
-               return -1;
+               error = EINVAL;
+               goto err_out;
        }
 
-       _index = _fd_to_index_and_lock(fd);
-
-       if (_index < 0) {
-               errno = EBADF;
-               return -1;
+       usbi_mutex_static_lock(&fd_table_lock);
+       if ((fd_table[fd] != NULL) && (fd_table[fd]->type == FD_TYPE_PIPE)) {
+               assert(fd_table[fd]->overlapped.Internal == STATUS_WAIT_0);
+               assert(fd_table[fd]->overlapped.InternalHigh == 2);
+               fd_table[fd]->overlapped.Internal = STATUS_PENDING;
+               ResetEvent(fd_table[fd]->overlapped.hEvent);
+               error = 0;
        }
+       usbi_mutex_static_unlock(&fd_table_lock);
 
-       if (WaitForSingleObject(poll_fd[_index].overlapped->hEvent, INFINITE) != WAIT_OBJECT_0) {
-               usbi_warn(NULL, "waiting for event failed: %u", (unsigned int)GetLastError());
-               errno = EIO;
-               goto out;
-       }
-
-       poll_dbg("clr pipe event (fd = %d, thread = %08X)", _index, (unsigned int)GetCurrentThreadId());
-       poll_fd[_index].overlapped->InternalHigh--;
-       // Don't reset unless we don't have any more events to process
-       if (poll_fd[_index].overlapped->InternalHigh <= 0) {
-               ResetEvent(poll_fd[_index].overlapped->hEvent);
-               poll_fd[_index].overlapped->Internal = STATUS_PENDING;
-       }
+       if (error)
+               goto err_out;
 
-       r = sizeof(unsigned char);
+       return sizeof(unsigned char);
 
-out:
-       LeaveCriticalSection(&_poll_fd[_index].mutex);
-       return r;
+err_out:
+       errno = error;
+       return -1;
 }
index aa4c985..2650fb4 100644 (file)
@@ -2,6 +2,7 @@
  * Windows compat: POSIX compatibility wrapper
  * Copyright © 2012-2013 RealVNC Ltd.
  * Copyright © 2009-2010 Pete Batard <pete@akeo.ie>
+ * Copyright © 2016-2018 Chris Dickens <christopher.a.dickens@gmail.com>
  * With contributions from Michael Plante, Orin Eman et al.
  * Parts of poll implementation from libusb-win32, by Stephan Meyer et al.
  *
@@ -65,46 +66,26 @@ extern int windows_version;
 #define POLLNVAL    0x0020    /* Invalid request: fd not open */
 
 struct pollfd {
-    int fd;           /* file descriptor */
-    short events;     /* requested events */
-    short revents;    /* returned events */
+       int fd;         /* file descriptor */
+       short events;   /* requested events */
+       short revents;  /* returned events */
 };
 
-// access modes
-enum rw_type {
-       RW_NONE,
-       RW_READ,
-       RW_WRITE,
-};
-
-// fd struct that can be used for polling on Windows
-typedef int cancel_transfer(struct usbi_transfer *itransfer);
-
 struct winfd {
-       int fd;                                                 // what's exposed to libusb core
-       HANDLE handle;                                  // what we need to attach overlapped to the I/O op, so we can poll it
-       OVERLAPPED* overlapped;                 // what will report our I/O status
-       struct usbi_transfer *itransfer;                // Associated transfer, or NULL if completed
-       cancel_transfer *cancel_fn;             // Function pointer to cancel transfer API
-       enum rw_type rw;                                // I/O transfer direction: read *XOR* write (NOT BOTH)
+       int fd;                         // what's exposed to libusb core
+       OVERLAPPED *overlapped;         // what will report our I/O status
 };
+
 extern const struct winfd INVALID_WINFD;
 
+struct winfd usbi_create_fd(void);
+
 int usbi_pipe(int pipefd[2]);
 int usbi_poll(struct pollfd *fds, unsigned int nfds, int timeout);
 ssize_t usbi_write(int fd, const void *buf, size_t count);
 ssize_t usbi_read(int fd, void *buf, size_t count);
 int usbi_close(int fd);
 
-void init_polling(void);
-void exit_polling(void);
-struct winfd usbi_create_fd(HANDLE handle, int access_mode, 
-       struct usbi_transfer *transfer, cancel_transfer *cancel_fn);
-void usbi_free_fd(struct winfd* winfd);
-struct winfd fd_to_winfd(int fd);
-struct winfd handle_to_winfd(HANDLE handle);
-struct winfd overlapped_to_winfd(OVERLAPPED* overlapped);
-
 /*
  * Timeval operations
  */
index 630dcab..409de2d 100644 (file)
 #define LIBUSB_THREADS_WINDOWS_H
 
 #define USBI_MUTEX_INITIALIZER 0L
+#ifdef _WIN32_WCE
+typedef LONG usbi_mutex_static_t;
+#else
 typedef volatile LONG usbi_mutex_static_t;
+#endif
 void usbi_mutex_static_lock(usbi_mutex_static_t *mutex);
 static inline void usbi_mutex_static_unlock(usbi_mutex_static_t *mutex)
 {
index 3ac5729..29e6559 100644 (file)
@@ -187,9 +187,6 @@ static int wince_init(struct libusb_context *ctx)
        // 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?
-               // Initialize pollable file descriptors
-               init_polling();
-
                // Load DLL imports
                if (init_dllimports() != LIBUSB_SUCCESS) {
                        usbi_err(ctx, "could not resolve DLL functions");
@@ -224,7 +221,6 @@ static int wince_init(struct libusb_context *ctx)
 init_exit: // Holds semaphore here.
        if (!concurrent_usage && r != LIBUSB_SUCCESS) { // First init failed?
                exit_dllimports();
-               exit_polling();
 
                if (driver_handle != INVALID_HANDLE_VALUE) {
                        UkwCloseDriver(driver_handle);
@@ -261,7 +257,6 @@ static void wince_exit(struct libusb_context *ctx)
        // Only works if exits and inits are balanced exactly
        if (--concurrent_usage < 0) {   // Last exit
                exit_dllimports();
-               exit_polling();
 
                if (driver_handle != INVALID_HANDLE_VALUE) {
                        UkwCloseDriver(driver_handle);
@@ -543,12 +538,9 @@ static void wince_destroy_device(struct libusb_device *dev)
 static void wince_clear_transfer_priv(struct usbi_transfer *itransfer)
 {
        struct wince_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
-       struct winfd wfd = fd_to_winfd(transfer_priv->pollable_fd.fd);
 
-       // No need to cancel transfer as it is either complete or abandoned
-       wfd.itransfer = NULL;
-       CloseHandle(wfd.handle);
-       usbi_free_fd(&transfer_priv->pollable_fd);
+       usbi_close(transfer_priv->pollable_fd.fd);
+       transfer_priv->pollable_fd = INVALID_WINFD;
 }
 
 static int wince_cancel_transfer(struct usbi_transfer *itransfer)
@@ -572,11 +564,10 @@ static int wince_submit_control_or_bulk_transfer(struct usbi_transfer *itransfer
        BOOL direction_in, ret;
        struct winfd wfd;
        DWORD flags;
-       HANDLE eventHandle;
        PUKW_CONTROL_HEADER setup = NULL;
        const BOOL control_transfer = transfer->type == LIBUSB_TRANSFER_TYPE_CONTROL;
+       int r;
 
-       transfer_priv->pollable_fd = INVALID_WINFD;
        if (control_transfer) {
                setup = (PUKW_CONTROL_HEADER) transfer->buffer;
                direction_in = setup->bmRequestType & LIBUSB_ENDPOINT_IN;
@@ -586,19 +577,18 @@ static int wince_submit_control_or_bulk_transfer(struct usbi_transfer *itransfer
        flags = direction_in ? UKW_TF_IN_TRANSFER : UKW_TF_OUT_TRANSFER;
        flags |= UKW_TF_SHORT_TRANSFER_OK;
 
-       eventHandle = CreateEvent(NULL, FALSE, FALSE, NULL);
-       if (eventHandle == NULL) {
-               usbi_err(ctx, "Failed to create event for async transfer");
+       wfd = usbi_create_fd();
+       if (wfd.fd < 0)
                return LIBUSB_ERROR_NO_MEM;
-       }
 
-       wfd = usbi_create_fd(eventHandle, direction_in ? RW_READ : RW_WRITE, itransfer, &wince_cancel_transfer);
-       if (wfd.fd < 0) {
-               CloseHandle(eventHandle);
-               return LIBUSB_ERROR_NO_MEM;
+       r = usbi_add_pollfd(ctx, wfd.fd, direction_in ? POLLIN : POLLOUT);
+       if (r) {
+               usbi_close(wfd.fd);
+               return r;
        }
 
        transfer_priv->pollable_fd = wfd;
+
        if (control_transfer) {
                // Split out control setup header and data buffer
                DWORD bufLen = transfer->length - sizeof(UKW_CONTROL_HEADER);
@@ -614,10 +604,11 @@ static int wince_submit_control_or_bulk_transfer(struct usbi_transfer *itransfer
                int libusbErr = translate_driver_error(GetLastError());
                usbi_err(ctx, "UkwIssue%sTransfer failed: error %u",
                        control_transfer ? "Control" : "Bulk", (unsigned int)GetLastError());
-               wince_clear_transfer_priv(itransfer);
+               usbi_remove_pollfd(ctx, wfd.fd);
+               usbi_close(wfd.fd);
+               transfer_priv->pollable_fd = INVALID_WINFD;
                return libusbErr;
        }
-       usbi_add_pollfd(ctx, transfer_priv->pollable_fd.fd, direction_in ? POLLIN : POLLOUT);
 
 
        return LIBUSB_SUCCESS;
index ceb764a..900eace 100644 (file)
@@ -32,6 +32,9 @@
 #include "windows_common.h"
 #include "windows_nt_common.h"
 
+// Public
+BOOL (WINAPI *pCancelIoEx)(HANDLE, LPOVERLAPPED);
+
 // Global variables for clock_gettime mechanism
 static uint64_t hires_ticks_to_ps;
 static uint64_t hires_frequency;
@@ -258,6 +261,15 @@ out_unlock:
 
 static int windows_init_dlls(void)
 {
+       HMODULE hKernel32 = GetModuleHandleA("KERNEL32");
+
+       if (hKernel32 == NULL)
+               return LIBUSB_ERROR_NOT_FOUND;
+
+       pCancelIoEx = (BOOL (WINAPI *)(HANDLE, LPOVERLAPPED))
+               GetProcAddress(hKernel32, "CancelIoEx");
+       usbi_dbg("Will use CancelIo%s for I/O cancellation", pCancelIoEx ? "Ex" : "");
+
        DLL_GET_HANDLE(User32);
        DLL_LOAD_FUNC_PREFIXED(User32, p, GetMessageA, TRUE);
        DLL_LOAD_FUNC_PREFIXED(User32, p, PeekMessageA, TRUE);
@@ -460,15 +472,15 @@ int windows_clock_gettime(int clk_id, struct timespec *tp)
        }
 }
 
-static void windows_transfer_callback(struct usbi_transfer *itransfer, uint32_t io_result, uint32_t io_size)
+static void windows_transfer_callback(struct usbi_transfer *itransfer, DWORD io_result, DWORD io_size)
 {
        int status, istatus;
 
-       usbi_dbg("handling I/O completion with errcode %u, size %u", io_result, io_size);
+       usbi_dbg("handling I/O completion with errcode %u, size %u", (unsigned int)io_result, (unsigned int)io_size);
 
        switch (io_result) {
        case NO_ERROR:
-               status = windows_copy_transfer_data(itransfer, io_size);
+               status = windows_copy_transfer_data(itransfer, (uint32_t)io_size);
                break;
        case ERROR_GEN_FAILURE:
                usbi_dbg("detected endpoint stall");
@@ -479,7 +491,7 @@ static void windows_transfer_callback(struct usbi_transfer *itransfer, uint32_t
                status = LIBUSB_TRANSFER_TIMED_OUT;
                break;
        case ERROR_OPERATION_ABORTED:
-               istatus = windows_copy_transfer_data(itransfer, io_size);
+               istatus = windows_copy_transfer_data(itransfer, (uint32_t)io_size);
                if (istatus != LIBUSB_TRANSFER_COMPLETED)
                        usbi_dbg("Failed to copy partial data in aborted operation: %d", istatus);
 
@@ -491,7 +503,7 @@ static void windows_transfer_callback(struct usbi_transfer *itransfer, uint32_t
                status = LIBUSB_TRANSFER_NO_DEVICE;
                break;
        default:
-               usbi_err(ITRANSFER_CTX(itransfer), "detected I/O error %u: %s", io_result, windows_error_str(io_result));
+               usbi_err(ITRANSFER_CTX(itransfer), "detected I/O error %u: %s", (unsigned int)io_result, windows_error_str(io_result));
                status = LIBUSB_TRANSFER_ERROR;
                break;
        }
@@ -502,6 +514,16 @@ static void windows_transfer_callback(struct usbi_transfer *itransfer, uint32_t
                usbi_handle_transfer_completion(itransfer, (enum libusb_transfer_status)status);
 }
 
+/*
+* Make a transfer complete synchronously
+*/
+void windows_force_sync_completion(OVERLAPPED *overlapped, ULONG size)
+{
+       overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY;
+       overlapped->InternalHigh = size;
+       SetEvent(overlapped->hEvent);
+}
+
 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);
@@ -523,11 +545,11 @@ void windows_handle_callback(struct usbi_transfer *itransfer, uint32_t io_result
 
 int windows_handle_events(struct libusb_context *ctx, struct pollfd *fds, POLL_NFDS_TYPE nfds, int num_ready)
 {
-       POLL_NFDS_TYPE i;
-       bool found = false;
-       struct usbi_transfer *transfer;
-       struct winfd *pollable_fd = NULL;
+       struct usbi_transfer *itransfer;
        DWORD io_size, io_result;
+       POLL_NFDS_TYPE i;
+       bool found;
+       int transfer_fd = -1;
        int r = LIBUSB_SUCCESS;
 
        usbi_mutex_lock(&ctx->open_devs_lock);
@@ -542,11 +564,11 @@ int windows_handle_events(struct libusb_context *ctx, struct pollfd *fds, POLL_N
 
                // 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) {
+               usbi_mutex_lock(&ctx->flying_transfers_lock);
+               list_for_each_entry(itransfer, &ctx->flying_transfers, list, struct usbi_transfer) {
+                       transfer_fd = windows_get_transfer_fd(itransfer);
+                       if (transfer_fd == fds[i].fd) {
                                found = true;
                                break;
                        }
@@ -554,13 +576,14 @@ int windows_handle_events(struct libusb_context *ctx, struct pollfd *fds, POLL_N
                usbi_mutex_unlock(&ctx->flying_transfers_lock);
 
                if (found) {
-                       windows_get_overlapped_result(transfer, pollable_fd, &io_result, &io_size);
+                       windows_get_overlapped_result(itransfer, &io_result, &io_size);
+
+                       usbi_remove_pollfd(ctx, transfer_fd);
 
-                       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);
+                       windows_handle_callback(itransfer, io_result, io_size);
                } else {
                        usbi_err(ctx, "could not find a matching transfer for fd %d", fds[i]);
                        r = LIBUSB_ERROR_NOT_FOUND;
index e87cbc5..d3ac6bc 100644 (file)
@@ -48,6 +48,9 @@ typedef struct USB_CONFIGURATION_DESCRIPTOR {
 
 typedef struct libusb_device_descriptor USB_DEVICE_DESCRIPTOR, *PUSB_DEVICE_DESCRIPTOR;
 
+/* This call is only available from Vista */
+extern BOOL (WINAPI *pCancelIoEx)(HANDLE, LPOVERLAPPED);
+
 int windows_common_init(struct libusb_context *ctx);
 void windows_common_exit(void);
 
@@ -56,9 +59,10 @@ 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);
+int windows_get_transfer_fd(struct usbi_transfer *itransfer);
+void windows_get_overlapped_result(struct usbi_transfer *itransfer, DWORD *io_result, DWORD *io_size);
 
+void windows_force_sync_completion(OVERLAPPED *overlapped, ULONG 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);
 
index 45c7840..5ace88b 100644 (file)
@@ -76,6 +76,7 @@ struct usbdk_device_priv {
 struct usbdk_transfer_priv {
        USB_DK_TRANSFER_REQUEST request;
        struct winfd pollable_fd;
+       HANDLE system_handle;
        PULONG64 IsochronousPacketsArray;
        PUSB_DK_ISO_TRANSFER_RESULT IsochronousResultsArray;
 };
@@ -203,8 +204,6 @@ static int usbdk_init(struct libusb_context *ctx)
                if (r)
                        goto init_exit;
 
-               init_polling();
-
                r = windows_common_init(ctx);
                if (r)
                        goto init_exit;
@@ -214,7 +213,6 @@ static int usbdk_init(struct libusb_context *ctx)
 
 init_exit:
        if (!concurrent_usage && r != LIBUSB_SUCCESS) { // First init failed?
-               exit_polling();
                windows_common_exit();
                unload_usbdk_helper_dll();
        }
@@ -374,7 +372,6 @@ static void usbdk_exit(struct libusb_context *ctx)
        UNUSED(ctx);
        if (--concurrent_usage < 0) {
                windows_common_exit();
-               exit_polling();
                unload_usbdk_helper_dll();
        }
 }
@@ -516,7 +513,9 @@ void windows_clear_transfer_priv(struct usbi_transfer *itransfer)
        struct usbdk_transfer_priv *transfer_priv = _usbdk_transfer_priv(itransfer);
        struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
 
-       usbi_free_fd(&transfer_priv->pollable_fd);
+       usbi_close(transfer_priv->pollable_fd.fd);
+       transfer_priv->pollable_fd = INVALID_WINFD;
+       transfer_priv->system_handle = NULL;
 
        if (transfer->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS) {
                safe_free(transfer_priv->IsochronousPacketsArray);
@@ -530,47 +529,31 @@ static int usbdk_do_control_transfer(struct usbi_transfer *itransfer)
        struct usbdk_device_priv *priv = _usbdk_device_priv(transfer->dev_handle->dev);
        struct usbdk_transfer_priv *transfer_priv = _usbdk_transfer_priv(itransfer);
        struct libusb_context *ctx = TRANSFER_CTX(transfer);
-       struct winfd wfd;
+       OVERLAPPED *overlapped = transfer_priv->pollable_fd.overlapped;
        ULONG Length;
        TransferResult transResult;
-       bool direction_in;
-
-       // Direction is specified by bmRequestType
-       direction_in = ((transfer->buffer[0] & LIBUSB_ENDPOINT_IN) == LIBUSB_ENDPOINT_IN);
-
-       wfd = usbi_create_fd(priv->system_handle, direction_in ? RW_READ : RW_WRITE, NULL, NULL);
-       // Always use the handle returned from usbi_create_fd (wfd.handle)
-       if (wfd.fd < 0)
-               return LIBUSB_ERROR_NO_MEM;
 
        transfer_priv->request.Buffer = (PVOID64)transfer->buffer;
        transfer_priv->request.BufferLength = transfer->length;
        transfer_priv->request.TransferType = ControlTransferType;
-       transfer_priv->pollable_fd = INVALID_WINFD;
        Length = (ULONG)transfer->length;
 
-       if (direction_in)
-               transResult = usbdk_helper.ReadPipe(priv->redirector_handle, &transfer_priv->request, wfd.overlapped);
+       if (transfer->buffer[0] & LIBUSB_ENDPOINT_IN)
+               transResult = usbdk_helper.ReadPipe(priv->redirector_handle, &transfer_priv->request, overlapped);
        else
-               transResult = usbdk_helper.WritePipe(priv->redirector_handle, &transfer_priv->request, wfd.overlapped);
+               transResult = usbdk_helper.WritePipe(priv->redirector_handle, &transfer_priv->request, overlapped);
 
        switch (transResult) {
        case TransferSuccess:
-               wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY;
-               wfd.overlapped->InternalHigh = (DWORD)Length;
+               windows_force_sync_completion(overlapped, (ULONG)transfer_priv->request.Result.GenResult.BytesTransferred);
                break;
        case TransferSuccessAsync:
                break;
        case TransferFailure:
                usbi_err(ctx, "ControlTransfer failed: %s", windows_error_str(0));
-               usbi_free_fd(&wfd);
                return LIBUSB_ERROR_IO;
        }
 
-       // Use priv_transfer to store data needed for async polling
-       transfer_priv->pollable_fd = wfd;
-       usbi_add_pollfd(ctx, transfer_priv->pollable_fd.fd, direction_in ? POLLIN : POLLOUT);
-
        return LIBUSB_SUCCESS;
 }
 
@@ -580,7 +563,7 @@ static int usbdk_do_bulk_transfer(struct usbi_transfer *itransfer)
        struct usbdk_device_priv *priv = _usbdk_device_priv(transfer->dev_handle->dev);
        struct usbdk_transfer_priv *transfer_priv = _usbdk_transfer_priv(itransfer);
        struct libusb_context *ctx = TRANSFER_CTX(transfer);
-       struct winfd wfd;
+       OVERLAPPED *overlapped = transfer_priv->pollable_fd.overlapped;
        TransferResult transferRes;
 
        transfer_priv->request.Buffer = (PVOID64)transfer->buffer;
@@ -599,33 +582,22 @@ static int usbdk_do_bulk_transfer(struct usbi_transfer *itransfer)
                return LIBUSB_ERROR_INVALID_PARAM;
        }
 
-       transfer_priv->pollable_fd = INVALID_WINFD;
-
-       wfd = usbi_create_fd(priv->system_handle, IS_XFERIN(transfer) ? RW_READ : RW_WRITE, NULL, NULL);
-       // Always use the handle returned from usbi_create_fd (wfd.handle)
-       if (wfd.fd < 0)
-               return LIBUSB_ERROR_NO_MEM;
-
        if (IS_XFERIN(transfer))
-               transferRes = usbdk_helper.ReadPipe(priv->redirector_handle, &transfer_priv->request, wfd.overlapped);
+               transferRes = usbdk_helper.ReadPipe(priv->redirector_handle, &transfer_priv->request, overlapped);
        else
-               transferRes = usbdk_helper.WritePipe(priv->redirector_handle, &transfer_priv->request, wfd.overlapped);
+               transferRes = usbdk_helper.WritePipe(priv->redirector_handle, &transfer_priv->request, overlapped);
 
        switch (transferRes) {
        case TransferSuccess:
-               wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY;
+               windows_force_sync_completion(overlapped, (ULONG)transfer_priv->request.Result.GenResult.BytesTransferred);
                break;
        case TransferSuccessAsync:
                break;
        case TransferFailure:
                usbi_err(ctx, "ReadPipe/WritePipe failed: %s", windows_error_str(0));
-               usbi_free_fd(&wfd);
                return LIBUSB_ERROR_IO;
        }
 
-       transfer_priv->pollable_fd = wfd;
-       usbi_add_pollfd(ctx, transfer_priv->pollable_fd.fd, IS_XFERIN(transfer) ? POLLIN : POLLOUT);
-
        return LIBUSB_SUCCESS;
 }
 
@@ -635,7 +607,7 @@ static int usbdk_do_iso_transfer(struct usbi_transfer *itransfer)
        struct usbdk_device_priv *priv = _usbdk_device_priv(transfer->dev_handle->dev);
        struct usbdk_transfer_priv *transfer_priv = _usbdk_transfer_priv(itransfer);
        struct libusb_context *ctx = TRANSFER_CTX(transfer);
-       struct winfd wfd;
+       OVERLAPPED *overlapped = transfer_priv->pollable_fd.overlapped;
        TransferResult transferRes;
        int i;
 
@@ -655,44 +627,60 @@ static int usbdk_do_iso_transfer(struct usbi_transfer *itransfer)
        transfer_priv->request.Result.IsochronousResultsArray = (PVOID64)transfer_priv->IsochronousResultsArray;
        if (!transfer_priv->IsochronousResultsArray) {
                usbi_err(ctx, "Allocation of isochronousResultsArray failed");
-               free(transfer_priv->IsochronousPacketsArray);
                return LIBUSB_ERROR_NO_MEM;
        }
 
        for (i = 0; i < transfer->num_iso_packets; i++)
                transfer_priv->IsochronousPacketsArray[i] = transfer->iso_packet_desc[i].length;
 
-       transfer_priv->pollable_fd = INVALID_WINFD;
-
-       wfd = usbi_create_fd(priv->system_handle, IS_XFERIN(transfer) ? RW_READ : RW_WRITE, NULL, NULL);
-       // Always use the handle returned from usbi_create_fd (wfd.handle)
-       if (wfd.fd < 0) {
-               free(transfer_priv->IsochronousPacketsArray);
-               free(transfer_priv->IsochronousResultsArray);
-               return LIBUSB_ERROR_NO_MEM;
-       }
-
        if (IS_XFERIN(transfer))
-               transferRes = usbdk_helper.ReadPipe(priv->redirector_handle, &transfer_priv->request, wfd.overlapped);
+               transferRes = usbdk_helper.ReadPipe(priv->redirector_handle, &transfer_priv->request, overlapped);
        else
-               transferRes = usbdk_helper.WritePipe(priv->redirector_handle, &transfer_priv->request, wfd.overlapped);
+               transferRes = usbdk_helper.WritePipe(priv->redirector_handle, &transfer_priv->request, overlapped);
 
        switch (transferRes) {
        case TransferSuccess:
-               wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY;
+               windows_force_sync_completion(overlapped, (ULONG)transfer_priv->request.Result.GenResult.BytesTransferred);
                break;
        case TransferSuccessAsync:
                break;
        case TransferFailure:
-               usbi_err(ctx, "ReadPipe/WritePipe failed: %s", windows_error_str(0));
-               usbi_free_fd(&wfd);
-               free(transfer_priv->IsochronousPacketsArray);
-               free(transfer_priv->IsochronousResultsArray);
                return LIBUSB_ERROR_IO;
        }
 
+       return LIBUSB_SUCCESS;
+}
+
+static int usbdk_do_submit_transfer(struct usbi_transfer *itransfer,
+       short events, int (*transfer_fn)(struct usbi_transfer *))
+{
+       struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+       struct libusb_context *ctx = TRANSFER_CTX(transfer);
+       struct usbdk_device_priv *priv = _usbdk_device_priv(transfer->dev_handle->dev);
+       struct usbdk_transfer_priv *transfer_priv = _usbdk_transfer_priv(itransfer);
+       struct winfd wfd;
+       int r;
+
+       wfd = usbi_create_fd();
+       if (wfd.fd < 0)
+               return LIBUSB_ERROR_NO_MEM;
+
+       r = usbi_add_pollfd(ctx, wfd.fd, events);
+       if (r) {
+               usbi_close(wfd.fd);
+               return r;
+       }
+
+       // Use transfer_priv to store data needed for async polling
        transfer_priv->pollable_fd = wfd;
-       usbi_add_pollfd(ctx, transfer_priv->pollable_fd.fd, IS_XFERIN(transfer) ? POLLIN : POLLOUT);
+       transfer_priv->system_handle = priv->system_handle;
+
+       r = transfer_fn(itransfer);
+       if (r != LIBUSB_SUCCESS) {
+               usbi_remove_pollfd(ctx, wfd.fd);
+               windows_clear_transfer_priv(itransfer);
+               return r;
+       }
 
        return LIBUSB_SUCCESS;
 }
@@ -700,22 +688,31 @@ static int usbdk_do_iso_transfer(struct usbi_transfer *itransfer)
 static int usbdk_submit_transfer(struct usbi_transfer *itransfer)
 {
        struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+       int (*transfer_fn)(struct usbi_transfer *);
+       short events;
 
        switch (transfer->type) {
        case LIBUSB_TRANSFER_TYPE_CONTROL:
-               return usbdk_do_control_transfer(itransfer);
+               events = (transfer->buffer[0] & LIBUSB_ENDPOINT_IN) ? POLLIN : POLLOUT;
+               transfer_fn = usbdk_do_control_transfer;
+               break;
        case LIBUSB_TRANSFER_TYPE_BULK:
        case LIBUSB_TRANSFER_TYPE_INTERRUPT:
                if (IS_XFEROUT(transfer) && (transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET))
                        return LIBUSB_ERROR_NOT_SUPPORTED; //TODO: Check whether we can support this in UsbDk
-               else
-                       return usbdk_do_bulk_transfer(itransfer);
+               events = IS_XFERIN(transfer) ? POLLIN : POLLOUT;
+               transfer_fn = usbdk_do_bulk_transfer;
+               break;
        case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
-               return usbdk_do_iso_transfer(itransfer);
+               events = IS_XFERIN(transfer) ? POLLIN : POLLOUT;
+               transfer_fn = usbdk_do_iso_transfer;
+               break;
        default:
                usbi_err(TRANSFER_CTX(transfer), "unknown endpoint type %d", transfer->type);
                return LIBUSB_ERROR_INVALID_PARAM;
        }
+
+       return usbdk_do_submit_transfer(itransfer, events, transfer_fn);
 }
 
 static int usbdk_abort_transfers(struct usbi_transfer *itransfer)
@@ -723,10 +720,20 @@ static int usbdk_abort_transfers(struct usbi_transfer *itransfer)
        struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
        struct libusb_context *ctx = TRANSFER_CTX(transfer);
        struct usbdk_device_priv *priv = _usbdk_device_priv(transfer->dev_handle->dev);
+       struct usbdk_transfer_priv *transfer_priv = _usbdk_transfer_priv(itransfer);
+       struct winfd *pollable_fd = &transfer_priv->pollable_fd;
 
-       if (!usbdk_helper.AbortPipe(priv->redirector_handle, transfer->endpoint)) {
-               usbi_err(ctx, "AbortPipe failed: %s", windows_error_str(0));
-               return LIBUSB_ERROR_NO_DEVICE;
+       if (pCancelIoEx != NULL) {
+               // Use CancelIoEx if available to cancel just a single transfer
+               if (!pCancelIoEx(priv->system_handle, pollable_fd->overlapped)) {
+                       usbi_err(ctx, "CancelIoEx failed: %s", windows_error_str(0));
+                       return LIBUSB_ERROR_NO_DEVICE;
+               }
+       } else {
+               if (!usbdk_helper.AbortPipe(priv->redirector_handle, transfer->endpoint)) {
+                       usbi_err(ctx, "AbortPipe failed: %s", windows_error_str(0));
+                       return LIBUSB_ERROR_NO_DEVICE;
+               }
        }
 
        return LIBUSB_SUCCESS;
@@ -757,10 +764,10 @@ int windows_copy_transfer_data(struct usbi_transfer *itransfer, uint32_t io_size
        return LIBUSB_TRANSFER_COMPLETED;
 }
 
-struct winfd *windows_get_fd(struct usbi_transfer *transfer)
+int windows_get_transfer_fd(struct usbi_transfer *itransfer)
 {
-       struct usbdk_transfer_priv *transfer_priv = _usbdk_transfer_priv(transfer);
-       return &transfer_priv->pollable_fd;
+       struct usbdk_transfer_priv *transfer_priv = _usbdk_transfer_priv(itransfer);
+       return transfer_priv->pollable_fd.fd;
 }
 
 static DWORD usbdk_translate_usbd_status(USBD_STATUS UsbdStatus)
@@ -778,10 +785,13 @@ static DWORD usbdk_translate_usbd_status(USBD_STATUS UsbdStatus)
        }
 }
 
-void windows_get_overlapped_result(struct usbi_transfer *itransfer, struct winfd *pollable_fd, DWORD *io_result, DWORD *io_size)
+void windows_get_overlapped_result(struct usbi_transfer *itransfer, DWORD *io_result, DWORD *io_size)
 {
+       struct usbdk_transfer_priv *transfer_priv = _usbdk_transfer_priv(itransfer);
+       struct winfd *pollable_fd = &transfer_priv->pollable_fd;
+
        if (HasOverlappedIoCompletedSync(pollable_fd->overlapped) // Handle async requests that completed synchronously first
-                       || GetOverlappedResult(pollable_fd->handle, pollable_fd->overlapped, io_size, FALSE)) { // Regular async overlapped
+                       || GetOverlappedResult(transfer_priv->system_handle, pollable_fd->overlapped, io_size, FALSE)) { // Regular async overlapped
                struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
 
                if (transfer->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS) {
index f4e6931..8ff815b 100644 (file)
@@ -789,9 +789,6 @@ static int windows_init(struct libusb_context *ctx)
                // We need a lock for proper auto-release
                usbi_mutex_init(&autoclaim_lock);
 
-               // Initialize pollable file descriptors
-               init_polling();
-
                // Load DLL imports
                if (init_dlls() != LIBUSB_SUCCESS) {
                        usbi_err(ctx, "could not resolve DLL functions");
@@ -819,7 +816,6 @@ init_exit: // Holds semaphore here.
                                usb_api_backend[i].exit();
                }
                exit_dlls();
-               exit_polling();
                windows_common_exit();
                usbi_mutex_destroy(&autoclaim_lock);
        }
@@ -1741,7 +1737,6 @@ static void windows_exit(struct libusb_context *ctx)
                                usb_api_backend[i].exit();
                }
                exit_dlls();
-               exit_polling();
                windows_common_exit();
                usbi_mutex_destroy(&autoclaim_lock);
        }
@@ -1965,97 +1960,84 @@ void windows_clear_transfer_priv(struct usbi_transfer *itransfer)
 {
        struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
 
-       usbi_free_fd(&transfer_priv->pollable_fd);
+       usbi_close(transfer_priv->pollable_fd.fd);
+       transfer_priv->pollable_fd = INVALID_WINFD;
+       transfer_priv->handle = NULL;
        safe_free(transfer_priv->hid_buffer);
        // When auto claim is in use, attempt to release the auto-claimed interface
        auto_release(itransfer);
 }
 
-static int submit_bulk_transfer(struct usbi_transfer *itransfer)
+static int do_submit_transfer(struct usbi_transfer *itransfer, short events,
+       int (*transfer_fn)(int, struct usbi_transfer *))
 {
-       struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
-       struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev);
+       struct libusb_context *ctx = ITRANSFER_CTX(itransfer);
        struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
-       struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev);
+       struct winfd wfd;
        int r;
 
-       if (priv->apib->submit_bulk_transfer == NULL) {
-               PRINT_UNSUPPORTED_API(submit_bulk_transfer);
-       }
+       wfd = usbi_create_fd();
+       if (wfd.fd < 0)
+               return LIBUSB_ERROR_NO_MEM;
 
-       r = priv->apib->submit_bulk_transfer(SUB_API_NOTSET, itransfer);
-       if (r != LIBUSB_SUCCESS)
+       r = usbi_add_pollfd(ctx, wfd.fd, events);
+       if (r) {
+               usbi_close(wfd.fd);
                return r;
-
-       usbi_add_pollfd(ctx, transfer_priv->pollable_fd.fd,
-               (short)(IS_XFERIN(transfer) ? POLLIN : POLLOUT));
-
-       return LIBUSB_SUCCESS;
-}
-
-static int submit_iso_transfer(struct usbi_transfer *itransfer)
-{
-       struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
-       struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev);
-       struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
-       struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev);
-       int r;
-
-       if (priv->apib->submit_iso_transfer == NULL) {
-               PRINT_UNSUPPORTED_API(submit_iso_transfer);
        }
 
-       r = priv->apib->submit_iso_transfer(SUB_API_NOTSET, itransfer);
-       if (r != LIBUSB_SUCCESS)
-               return r;
-
-       usbi_add_pollfd(ctx, transfer_priv->pollable_fd.fd,
-               (short)(IS_XFERIN(transfer) ? POLLIN : POLLOUT));
-
-       return LIBUSB_SUCCESS;
-}
+       // Use transfer_priv to store data needed for async polling
+       transfer_priv->pollable_fd = wfd;
 
-static int submit_control_transfer(struct usbi_transfer *itransfer)
-{
-       struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
-       struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev);
-       struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
-       struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev);
-       int r;
+       r = transfer_fn(SUB_API_NOTSET, itransfer);
 
-       if (priv->apib->submit_control_transfer == NULL) {
-               PRINT_UNSUPPORTED_API(submit_control_transfer);
+       if ((r != LIBUSB_SUCCESS) && (r != LIBUSB_ERROR_OVERFLOW)) {
+               usbi_remove_pollfd(ctx, wfd.fd);
+               usbi_close(wfd.fd);
+               transfer_priv->pollable_fd = INVALID_WINFD;
        }
 
-       r = priv->apib->submit_control_transfer(SUB_API_NOTSET, itransfer);
-       if (r != LIBUSB_SUCCESS)
-               return r;
-
-       usbi_add_pollfd(ctx, transfer_priv->pollable_fd.fd, POLLIN);
-
-       return LIBUSB_SUCCESS;
+       return r;
 }
 
 static int windows_submit_transfer(struct usbi_transfer *itransfer)
 {
        struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+       struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev);
+       int (*transfer_fn)(int, struct usbi_transfer *);
+       short events;
 
        switch (transfer->type) {
        case LIBUSB_TRANSFER_TYPE_CONTROL:
-               return submit_control_transfer(itransfer);
+               events = (transfer->buffer[0] & LIBUSB_ENDPOINT_IN) ? POLLIN : POLLOUT;
+               transfer_fn = priv->apib->submit_control_transfer;
+               break;
        case LIBUSB_TRANSFER_TYPE_BULK:
        case LIBUSB_TRANSFER_TYPE_INTERRUPT:
                if (IS_XFEROUT(transfer) && (transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET))
                        return LIBUSB_ERROR_NOT_SUPPORTED;
-               return submit_bulk_transfer(itransfer);
+               events = IS_XFERIN(transfer) ? POLLIN : POLLOUT;
+               transfer_fn = priv->apib->submit_bulk_transfer;
+               break;
        case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
-               return submit_iso_transfer(itransfer);
+               events = IS_XFERIN(transfer) ? POLLIN : POLLOUT;
+               transfer_fn = priv->apib->submit_iso_transfer;
+               break;
        case LIBUSB_TRANSFER_TYPE_BULK_STREAM:
                return LIBUSB_ERROR_NOT_SUPPORTED;
        default:
                usbi_err(TRANSFER_CTX(transfer), "unknown endpoint type %d", transfer->type);
                return LIBUSB_ERROR_INVALID_PARAM;
        }
+
+       if (transfer_fn == NULL) {
+               usbi_warn(TRANSFER_CTX(transfer),
+                       "unsupported transfer type %d (unrecognized device driver)",
+                       transfer->type);
+               return LIBUSB_ERROR_NOT_SUPPORTED;
+       }
+
+       return do_submit_transfer(itransfer, events, transfer_fn);
 }
 
 static int windows_abort_control(struct usbi_transfer *itransfer)
@@ -2108,18 +2090,21 @@ int windows_copy_transfer_data(struct usbi_transfer *itransfer, uint32_t io_size
        return priv->apib->copy_transfer_data(SUB_API_NOTSET, itransfer, io_size);
 }
 
-struct winfd *windows_get_fd(struct usbi_transfer *transfer)
+int windows_get_transfer_fd(struct usbi_transfer *itransfer)
 {
-       struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(transfer);
-       return &transfer_priv->pollable_fd;
+       struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
+       return transfer_priv->pollable_fd.fd;
 }
 
-void windows_get_overlapped_result(struct usbi_transfer *transfer, struct winfd *pollable_fd, DWORD *io_result, DWORD *io_size)
+void windows_get_overlapped_result(struct usbi_transfer *itransfer, DWORD *io_result, DWORD *io_size)
 {
+       struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
+       struct winfd *pollable_fd = &transfer_priv->pollable_fd;
+
        if (HasOverlappedIoCompletedSync(pollable_fd->overlapped)) {
                *io_result = NO_ERROR;
                *io_size = (DWORD)pollable_fd->overlapped->InternalHigh;
-       } else if (GetOverlappedResult(pollable_fd->handle, pollable_fd->overlapped, io_size, false)) {
+       } else if (GetOverlappedResult(transfer_priv->handle, pollable_fd->overlapped, io_size, FALSE)) {
                // Regular async overlapped
                *io_result = NO_ERROR;
        } else {
@@ -2669,15 +2654,14 @@ static int winusbx_submit_control_transfer(int sub_api, struct usbi_transfer *it
        struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev);
        struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
        struct windows_device_handle_priv *handle_priv = _device_handle_priv(transfer->dev_handle);
-       WINUSB_SETUP_PACKET *setup = (WINUSB_SETUP_PACKET *)transfer->buffer;
+       PWINUSB_SETUP_PACKET setup = (PWINUSB_SETUP_PACKET)transfer->buffer;
        ULONG size;
        HANDLE winusb_handle;
+       OVERLAPPED *overlapped;
        int current_interface;
-       struct winfd wfd;
 
        CHECK_WINUSBX_AVAILABLE(sub_api);
 
-       transfer_priv->pollable_fd = INVALID_WINFD;
        size = transfer->length - LIBUSB_CONTROL_SETUP_SIZE;
 
        // Windows places upper limits on the control transfer size
@@ -2692,38 +2676,29 @@ static int winusbx_submit_control_transfer(int sub_api, struct usbi_transfer *it
        }
 
        usbi_dbg("will use interface %d", current_interface);
-       winusb_handle = handle_priv->interface_handle[current_interface].api_handle;
 
-       wfd = usbi_create_fd(winusb_handle, RW_READ, NULL, NULL);
-       // Always use the handle returned from usbi_create_fd (wfd.handle)
-       if (wfd.fd < 0)
-               return LIBUSB_ERROR_NO_MEM;
+       transfer_priv->handle = winusb_handle = handle_priv->interface_handle[current_interface].api_handle;
+       overlapped = transfer_priv->pollable_fd.overlapped;
 
        // Sending of set configuration control requests from WinUSB creates issues
        if ((LIBUSB_REQ_TYPE(setup->RequestType) == LIBUSB_REQUEST_TYPE_STANDARD)
                        && (setup->Request == LIBUSB_REQUEST_SET_CONFIGURATION)) {
                if (setup->Value != priv->active_config) {
                        usbi_warn(ctx, "cannot set configuration other than the default one");
-                       usbi_free_fd(&wfd);
                        return LIBUSB_ERROR_INVALID_PARAM;
                }
-               wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY;
-               wfd.overlapped->InternalHigh = 0;
+               windows_force_sync_completion(overlapped, 0);
        } else {
-               if (!WinUSBX[sub_api].ControlTransfer(wfd.handle, *setup, transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE, size, NULL, wfd.overlapped)) {
+               if (!WinUSBX[sub_api].ControlTransfer(winusb_handle, *setup, transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE, size, NULL, overlapped)) {
                        if (GetLastError() != ERROR_IO_PENDING) {
                                usbi_warn(ctx, "ControlTransfer failed: %s", windows_error_str(0));
-                               usbi_free_fd(&wfd);
                                return LIBUSB_ERROR_IO;
                        }
                } else {
-                       wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY;
-                       wfd.overlapped->InternalHigh = (DWORD)size;
+                       windows_force_sync_completion(overlapped, size);
                }
        }
 
-       // Use priv_transfer to store data needed for async polling
-       transfer_priv->pollable_fd = wfd;
        transfer_priv->interface_number = (uint8_t)current_interface;
 
        return LIBUSB_SUCCESS;
@@ -2763,14 +2738,12 @@ static int winusbx_submit_bulk_transfer(int sub_api, struct usbi_transfer *itran
        struct windows_device_handle_priv *handle_priv = _device_handle_priv(transfer->dev_handle);
        struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev);
        HANDLE winusb_handle;
+       OVERLAPPED *overlapped;
        bool ret;
        int current_interface;
-       struct winfd wfd;
 
        CHECK_WINUSBX_AVAILABLE(sub_api);
 
-       transfer_priv->pollable_fd = INVALID_WINFD;
-
        current_interface = interface_by_endpoint(priv, handle_priv, transfer->endpoint);
        if (current_interface < 0) {
                usbi_err(ctx, "unable to match endpoint to an open interface - cancelling transfer");
@@ -2779,33 +2752,26 @@ static int winusbx_submit_bulk_transfer(int sub_api, struct usbi_transfer *itran
 
        usbi_dbg("matched endpoint %02X with interface %d", transfer->endpoint, current_interface);
 
-       winusb_handle = handle_priv->interface_handle[current_interface].api_handle;
-
-       wfd = usbi_create_fd(winusb_handle, IS_XFERIN(transfer) ? RW_READ : RW_WRITE, NULL, NULL);
-       // Always use the handle returned from usbi_create_fd (wfd.handle)
-       if (wfd.fd < 0)
-               return LIBUSB_ERROR_NO_MEM;
+       transfer_priv->handle = winusb_handle = handle_priv->interface_handle[current_interface].api_handle;
+       overlapped = transfer_priv->pollable_fd.overlapped;
 
        if (IS_XFERIN(transfer)) {
                usbi_dbg("reading %d bytes", transfer->length);
-               ret = WinUSBX[sub_api].ReadPipe(wfd.handle, transfer->endpoint, transfer->buffer, transfer->length, NULL, wfd.overlapped);
+               ret = WinUSBX[sub_api].ReadPipe(winusb_handle, transfer->endpoint, transfer->buffer, transfer->length, NULL, overlapped);
        } else {
                usbi_dbg("writing %d bytes", transfer->length);
-               ret = WinUSBX[sub_api].WritePipe(wfd.handle, transfer->endpoint, transfer->buffer, transfer->length, NULL, wfd.overlapped);
+               ret = WinUSBX[sub_api].WritePipe(winusb_handle, transfer->endpoint, transfer->buffer, transfer->length, NULL, overlapped);
        }
 
        if (!ret) {
                if (GetLastError() != ERROR_IO_PENDING) {
                        usbi_err(ctx, "ReadPipe/WritePipe failed: %s", windows_error_str(0));
-                       usbi_free_fd(&wfd);
                        return LIBUSB_ERROR_IO;
                }
        } else {
-               wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY;
-               wfd.overlapped->InternalHigh = (DWORD)transfer->length;
+               windows_force_sync_completion(overlapped, (ULONG)transfer->length);
        }
 
-       transfer_priv->pollable_fd = wfd;
        transfer_priv->interface_number = (uint8_t)current_interface;
 
        return LIBUSB_SUCCESS;
@@ -2857,7 +2823,7 @@ static int winusbx_abort_transfers(int sub_api, struct usbi_transfer *itransfer)
        struct windows_device_handle_priv *handle_priv = _device_handle_priv(transfer->dev_handle);
        struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
        struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev);
-       HANDLE winusb_handle;
+       HANDLE handle;
        int current_interface;
 
        CHECK_WINUSBX_AVAILABLE(sub_api);
@@ -2869,11 +2835,23 @@ static int winusbx_abort_transfers(int sub_api, struct usbi_transfer *itransfer)
        }
        usbi_dbg("will use interface %d", current_interface);
 
-       winusb_handle = handle_priv->interface_handle[current_interface].api_handle;
+       handle = handle_priv->interface_handle[current_interface].dev_handle;
 
-       if (!WinUSBX[sub_api].AbortPipe(winusb_handle, transfer->endpoint)) {
-               usbi_err(ctx, "AbortPipe failed: %s", windows_error_str(0));
-               return LIBUSB_ERROR_NO_DEVICE;
+       if (pCancelIoEx != NULL) {
+               // Use CancelIoEx if available to cancel just a single transfer
+               if (!pCancelIoEx(handle, transfer_priv->pollable_fd.overlapped)) {
+                       usbi_err(ctx, "CancelIoEx failed: %s", windows_error_str(0));
+                       return LIBUSB_ERROR_NO_DEVICE;
+               }
+       } else {
+               if (!CancelIo(handle)) {
+                       usbi_err(ctx, "CancelIo failed: %s", windows_error_str(0));
+                       handle = handle_priv->interface_handle[current_interface].api_handle;
+                       if (!WinUSBX[sub_api].AbortPipe(handle, transfer->endpoint)) {
+                               usbi_err(ctx, "AbortPipe failed: %s", windows_error_str(0));
+                               return LIBUSB_ERROR_NO_DEVICE;
+                       }
+               }
        }
 
        return LIBUSB_SUCCESS;
@@ -2893,7 +2871,6 @@ static int winusbx_reset_device(int sub_api, struct libusb_device_handle *dev_ha
        struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev);
        struct windows_device_handle_priv *handle_priv = _device_handle_priv(dev_handle);
        struct windows_device_priv *priv = _device_priv(dev_handle->dev);
-       struct winfd wfd;
        HANDLE winusb_handle;
        int i, j;
 
@@ -2902,13 +2879,6 @@ static int winusbx_reset_device(int sub_api, struct libusb_device_handle *dev_ha
        // Reset any available pipe (except control)
        for (i = 0; i < USB_MAXINTERFACES; i++) {
                winusb_handle = handle_priv->interface_handle[i].api_handle;
-               for (wfd = handle_to_winfd(winusb_handle); wfd.fd > 0; ) {
-                       // Cancel any pollable I/O
-                       usbi_remove_pollfd(ctx, wfd.fd);
-                       usbi_free_fd(&wfd);
-                       wfd = handle_to_winfd(winusb_handle);
-               }
-
                if (HANDLE_VALID(winusb_handle)) {
                        for (j = 0; j < priv->usb_interface[i].nb_endpoints; j++) {
                                usbi_dbg("resetting ep %02X", priv->usb_interface[i].endpoint[j]);
@@ -3678,14 +3648,13 @@ static int hid_submit_control_transfer(int sub_api, struct usbi_transfer *itrans
        struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev);
        WINUSB_SETUP_PACKET *setup = (WINUSB_SETUP_PACKET *)transfer->buffer;
        HANDLE hid_handle;
-       struct winfd wfd;
+       OVERLAPPED *overlapped;
        int current_interface, config;
        size_t size;
        int r = LIBUSB_ERROR_INVALID_PARAM;
 
        CHECK_HID_AVAILABLE;
 
-       transfer_priv->pollable_fd = INVALID_WINFD;
        safe_free(transfer_priv->hid_buffer);
        transfer_priv->hid_dest = NULL;
        size = transfer->length - LIBUSB_CONTROL_SETUP_SIZE;
@@ -3700,17 +3669,15 @@ static int hid_submit_control_transfer(int sub_api, struct usbi_transfer *itrans
        }
 
        usbi_dbg("will use interface %d", current_interface);
+
        hid_handle = handle_priv->interface_handle[current_interface].api_handle;
-       // Always use the handle returned from usbi_create_fd (wfd.handle)
-       wfd = usbi_create_fd(hid_handle, RW_READ, NULL, NULL);
-       if (wfd.fd < 0)
-               return LIBUSB_ERROR_NOT_FOUND;
+       overlapped = transfer_priv->pollable_fd.overlapped;
 
        switch (LIBUSB_REQ_TYPE(setup->RequestType)) {
        case LIBUSB_REQUEST_TYPE_STANDARD:
                switch (setup->Request) {
                case LIBUSB_REQUEST_GET_DESCRIPTOR:
-                       r = _hid_get_descriptor(priv->hid, wfd.handle, LIBUSB_REQ_RECIPIENT(setup->RequestType),
+                       r = _hid_get_descriptor(priv->hid, hid_handle, LIBUSB_REQ_RECIPIENT(setup->RequestType),
                                (setup->Value >> 8) & 0xFF, setup->Value & 0xFF, transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE, &size);
                        break;
                case LIBUSB_REQUEST_GET_CONFIGURATION:
@@ -3741,39 +3708,31 @@ static int hid_submit_control_transfer(int sub_api, struct usbi_transfer *itrans
                        break;
                default:
                        usbi_warn(ctx, "unsupported HID control request");
-                       r = LIBUSB_ERROR_NOT_SUPPORTED;
-                       break;
+                       return LIBUSB_ERROR_NOT_SUPPORTED;
                }
                break;
        case LIBUSB_REQUEST_TYPE_CLASS:
-               r = _hid_class_request(priv->hid, wfd.handle, setup->RequestType, setup->Request, setup->Value,
+               r = _hid_class_request(priv->hid, hid_handle, setup->RequestType, setup->Request, setup->Value,
                        setup->Index, transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE, transfer_priv,
-                       &size, wfd.overlapped);
+                       &size, overlapped);
                break;
        default:
                usbi_warn(ctx, "unsupported HID control request");
-               r = LIBUSB_ERROR_NOT_SUPPORTED;
-               break;
+               return LIBUSB_ERROR_NOT_SUPPORTED;
        }
 
+       if (r < 0)
+               return r;
+
        if (r == LIBUSB_COMPLETED) {
                // Force request to be completed synchronously. Transferred size has been set by previous call
-               wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY;
-               // http://msdn.microsoft.com/en-us/library/ms684342%28VS.85%29.aspx
-               // set InternalHigh to the number of bytes transferred
-               wfd.overlapped->InternalHigh = (DWORD)size;
+               windows_force_sync_completion(overlapped, (ULONG)size);
                r = LIBUSB_SUCCESS;
        }
 
-       if (r == LIBUSB_SUCCESS) {
-               // Use priv_transfer to store data needed for async polling
-               transfer_priv->pollable_fd = wfd;
-               transfer_priv->interface_number = (uint8_t)current_interface;
-       } else {
-               usbi_free_fd(&wfd);
-       }
+       transfer_priv->interface_number = (uint8_t)current_interface;
 
-       return r;
+       return LIBUSB_SUCCESS;
 }
 
 static int hid_submit_bulk_transfer(int sub_api, struct usbi_transfer *itransfer)
@@ -3783,8 +3742,8 @@ static int hid_submit_bulk_transfer(int sub_api, struct usbi_transfer *itransfer
        struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev);
        struct windows_device_handle_priv *handle_priv = _device_handle_priv(transfer->dev_handle);
        struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev);
-       struct winfd wfd;
        HANDLE hid_handle;
+       OVERLAPPED *overlapped;
        bool direction_in, ret;
        int current_interface, length;
        DWORD size;
@@ -3792,7 +3751,6 @@ static int hid_submit_bulk_transfer(int sub_api, struct usbi_transfer *itransfer
 
        CHECK_HID_AVAILABLE;
 
-       transfer_priv->pollable_fd = INVALID_WINFD;
        transfer_priv->hid_dest = NULL;
        safe_free(transfer_priv->hid_buffer);
 
@@ -3804,13 +3762,9 @@ static int hid_submit_bulk_transfer(int sub_api, struct usbi_transfer *itransfer
 
        usbi_dbg("matched endpoint %02X with interface %d", transfer->endpoint, current_interface);
 
-       hid_handle = handle_priv->interface_handle[current_interface].api_handle;
-       direction_in = transfer->endpoint & LIBUSB_ENDPOINT_IN;
-
-       wfd = usbi_create_fd(hid_handle, direction_in?RW_READ:RW_WRITE, NULL, NULL);
-       // Always use the handle returned from usbi_create_fd (wfd.handle)
-       if (wfd.fd < 0)
-               return LIBUSB_ERROR_NO_MEM;
+       transfer_priv->handle = hid_handle = handle_priv->interface_handle[current_interface].api_handle;
+       overlapped = transfer_priv->pollable_fd.overlapped;
+       direction_in = IS_XFERIN(transfer);
 
        // If report IDs are not in use, an extra prefix byte must be added
        if (((direction_in) && (!priv->hid->uses_report_ids[0]))
@@ -3829,7 +3783,7 @@ static int hid_submit_bulk_transfer(int sub_api, struct usbi_transfer *itransfer
        if (direction_in) {
                transfer_priv->hid_dest = transfer->buffer;
                usbi_dbg("reading %d bytes (report ID: 0x00)", length);
-               ret = ReadFile(wfd.handle, transfer_priv->hid_buffer, length + 1, &size, wfd.overlapped);
+               ret = ReadFile(hid_handle, transfer_priv->hid_buffer, length + 1, &size, overlapped);
        } else {
                if (!priv->hid->uses_report_ids[1])
                        memcpy(transfer_priv->hid_buffer + 1, transfer->buffer, transfer->length);
@@ -3838,13 +3792,12 @@ static int hid_submit_bulk_transfer(int sub_api, struct usbi_transfer *itransfer
                        memcpy(transfer_priv->hid_buffer, transfer->buffer, transfer->length);
 
                usbi_dbg("writing %d bytes (report ID: 0x%02X)", length, transfer_priv->hid_buffer[0]);
-               ret = WriteFile(wfd.handle, transfer_priv->hid_buffer, length, &size, wfd.overlapped);
+               ret = WriteFile(hid_handle, transfer_priv->hid_buffer, length, &size, overlapped);
        }
 
        if (!ret) {
                if (GetLastError() != ERROR_IO_PENDING) {
                        usbi_err(ctx, "HID transfer failed: %s", windows_error_str(0));
-                       usbi_free_fd(&wfd);
                        safe_free(transfer_priv->hid_buffer);
                        return LIBUSB_ERROR_IO;
                }
@@ -3862,11 +3815,9 @@ static int hid_submit_bulk_transfer(int sub_api, struct usbi_transfer *itransfer
                        usbi_err(ctx, "OVERFLOW!");
                        r = LIBUSB_ERROR_OVERFLOW;
                }
-               wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY;
-               wfd.overlapped->InternalHigh = size;
+               windows_force_sync_completion(overlapped, (ULONG)size);
        }
 
-       transfer_priv->pollable_fd = wfd;
        transfer_priv->interface_number = (uint8_t)current_interface;
 
        return r;
@@ -3874,6 +3825,7 @@ static int hid_submit_bulk_transfer(int sub_api, struct usbi_transfer *itransfer
 
 static int hid_abort_transfers(int sub_api, struct usbi_transfer *itransfer)
 {
+       struct libusb_context *ctx = ITRANSFER_CTX(itransfer);
        struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
        struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
        struct windows_device_handle_priv *handle_priv = _device_handle_priv(transfer->dev_handle);
@@ -3883,10 +3835,25 @@ static int hid_abort_transfers(int sub_api, struct usbi_transfer *itransfer)
        CHECK_HID_AVAILABLE;
 
        current_interface = transfer_priv->interface_number;
-       hid_handle = handle_priv->interface_handle[current_interface].api_handle;
-       CancelIo(hid_handle);
+       if ((current_interface < 0) || (current_interface >= USB_MAXINTERFACES)) {
+               usbi_err(ctx, "program assertion failed: invalid interface_number");
+               return LIBUSB_ERROR_NOT_FOUND;
+       }
+       usbi_dbg("will use interface %d", current_interface);
 
-       return LIBUSB_SUCCESS;
+       hid_handle = handle_priv->interface_handle[current_interface].dev_handle;
+
+       if (pCancelIoEx != NULL) {
+               // Use CancelIoEx if available to cancel just a single transfer
+               if (pCancelIoEx(hid_handle, transfer_priv->pollable_fd.overlapped))
+                       return LIBUSB_SUCCESS;
+       } else {
+               if (CancelIo(hid_handle))
+                       return LIBUSB_SUCCESS;
+       }
+
+       usbi_warn(ctx, "cancel failed: %s", windows_error_str(0));
+       return LIBUSB_ERROR_NOT_FOUND;
 }
 
 static int hid_reset_device(int sub_api, struct libusb_device_handle *dev_handle)
@@ -4187,9 +4154,15 @@ static int composite_abort_control(int sub_api, struct usbi_transfer *itransfer)
        struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
        struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
        struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev);
+       int current_interface = transfer_priv->interface_number;
 
-       return priv->usb_interface[transfer_priv->interface_number].apib->
-               abort_control(priv->usb_interface[transfer_priv->interface_number].sub_api, itransfer);
+       if ((current_interface < 0) || (current_interface >= USB_MAXINTERFACES)) {
+               usbi_err(TRANSFER_CTX(transfer), "program assertion failed: invalid interface_number");
+               return LIBUSB_ERROR_NOT_FOUND;
+       }
+
+       return priv->usb_interface[current_interface].apib->
+               abort_control(priv->usb_interface[current_interface].sub_api, itransfer);
 }
 
 static int composite_abort_transfers(int sub_api, struct usbi_transfer *itransfer)
@@ -4197,9 +4170,15 @@ static int composite_abort_transfers(int sub_api, struct usbi_transfer *itransfe
        struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
        struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
        struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev);
+       int current_interface = transfer_priv->interface_number;
 
-       return priv->usb_interface[transfer_priv->interface_number].apib->
-               abort_transfers(priv->usb_interface[transfer_priv->interface_number].sub_api, itransfer);
+       if ((current_interface < 0) || (current_interface >= USB_MAXINTERFACES)) {
+               usbi_err(TRANSFER_CTX(transfer), "program assertion failed: invalid interface_number");
+               return LIBUSB_ERROR_NOT_FOUND;
+       }
+
+       return priv->usb_interface[current_interface].apib->
+               abort_transfers(priv->usb_interface[current_interface].sub_api, itransfer);
 }
 
 static int composite_reset_device(int sub_api, struct libusb_device_handle *dev_handle)
index b0e834a..fb0a4e7 100644 (file)
@@ -277,6 +277,7 @@ static inline struct windows_device_handle_priv *_device_handle_priv(
 // used for async polling functions
 struct windows_transfer_priv {
        struct winfd pollable_fd;
+       HANDLE handle;
        uint8_t interface_number;
        uint8_t *hid_buffer; // 1 byte extended data buffer, required for HID
        uint8_t *hid_dest;   // transfer buffer destination, required for HID
index fde5f52..ffbd254 100644 (file)
@@ -1 +1 @@
-#define LIBUSB_NANO 11273
+#define LIBUSB_NANO 11274