/*
* 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
* - 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
*/
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;
}
/*
*/
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;
}
/*
*/
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;
}
* 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.
*
#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
*/
#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)
{
// 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");
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);
// 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);
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)
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;
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);
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;
#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;
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);
}
}
-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");
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);
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;
}
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);
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);
// 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;
}
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;
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);
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);
struct usbdk_transfer_priv {
USB_DK_TRANSFER_REQUEST request;
struct winfd pollable_fd;
+ HANDLE system_handle;
PULONG64 IsochronousPacketsArray;
PUSB_DK_ISO_TRANSFER_RESULT IsochronousResultsArray;
};
if (r)
goto init_exit;
- init_polling();
-
r = windows_common_init(ctx);
if (r)
goto init_exit;
init_exit:
if (!concurrent_usage && r != LIBUSB_SUCCESS) { // First init failed?
- exit_polling();
windows_common_exit();
unload_usbdk_helper_dll();
}
UNUSED(ctx);
if (--concurrent_usage < 0) {
windows_common_exit();
- exit_polling();
unload_usbdk_helper_dll();
}
}
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);
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;
}
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;
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;
}
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;
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;
}
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)
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;
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)
}
}
-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) {
// 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");
usb_api_backend[i].exit();
}
exit_dlls();
- exit_polling();
windows_common_exit();
usbi_mutex_destroy(&autoclaim_lock);
}
usb_api_backend[i].exit();
}
exit_dlls();
- exit_polling();
windows_common_exit();
usbi_mutex_destroy(&autoclaim_lock);
}
{
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)
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 {
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
}
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;
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");
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;
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);
}
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;
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;
// 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]);
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;
}
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:
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)
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;
CHECK_HID_AVAILABLE;
- transfer_priv->pollable_fd = INVALID_WINFD;
transfer_priv->hid_dest = NULL;
safe_free(transfer_priv->hid_buffer);
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]))
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);
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;
}
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;
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);
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)
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)
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)
// 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
-#define LIBUSB_NANO 11273
+#define LIBUSB_NANO 11274