Windows: Rework poll() emulation to a much simpler and effective design

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>
This commit is contained in:
Chris Dickens
2018-01-06 21:43:12 -08:00
parent 0865774fe9
commit d0779e9303
10 changed files with 515 additions and 886 deletions

View 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
@@ -42,465 +36,137 @@
*/
#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];
const struct winfd INVALID_WINFD = { -1, NULL };
// globals
BOOLEAN is_polling_set = FALSE;
LONG pipe_number = 0;
static volatile LONG compat_spinlock = 0;
// private data
struct file_descriptor {
enum fd_type { FD_TYPE_PIPE, FD_TYPE_TRANSFER } type;
OVERLAPPED overlapped;
};
#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 usbi_mutex_static_t fd_table_lock = USBI_MUTEX_INITIALIZER;
static struct file_descriptor *fd_table[MAX_FDS];
static inline void setup_cancel_io(void)
static struct file_descriptor *create_fd(enum fd_type type)
{
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);
}
// Internal function to retrieve the table index (and lock the fd mutex)
static int _fd_to_index_and_lock(int fd)
{
int i;
if (fd < 0)
return -1;
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)
{
OVERLAPPED *overlapped = (OVERLAPPED*) calloc(1, sizeof(OVERLAPPED));
if (overlapped == NULL) {
struct file_descriptor *fd = calloc(1, sizeof(*fd));
if (fd == NULL)
return NULL;
fd->overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (fd->overlapped.hEvent == NULL) {
free(fd);
return NULL;
}
overlapped->hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if(overlapped->hEvent == NULL) {
free (overlapped);
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;
if ((handle == 0) || (handle == INVALID_HANDLE_VALUE)) {
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;
}
overlapped = create_overlapped();
if(overlapped == NULL) {
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;
}
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;
}
/*
* Release a pollable file descriptor.
*
* Note that the associated Windows handle is not closed by this call
*/
void usbi_free_fd(struct winfd *wfd)
{
int _index;
CHECK_INIT_POLLING;
_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 file_descriptor *fd;
struct winfd wfd;
CHECK_INIT_POLLING;
if (fd < 0)
fd = create_fd(FD_TYPE_TRANSFER);
if (fd == NULL)
return INVALID_WINFD;
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;
}
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;
}
return INVALID_WINFD;
usbi_mutex_static_unlock(&fd_table_lock);
if (wfd.fd == MAX_FDS) {
free_fd(fd);
return INVALID_WINFD;
}
wfd.overlapped = &fd->overlapped;
return wfd;
}
struct winfd handle_to_winfd(HANDLE handle)
static int check_pollfds(struct pollfd *fds, unsigned int nfds,
HANDLE *wait_handles, DWORD *nb_wait_handles)
{
int i;
struct winfd wfd;
struct file_descriptor *fd;
unsigned int n;
int nready = 0;
CHECK_INIT_POLLING;
usbi_mutex_static_lock(&fd_table_lock);
if ((handle == 0) || (handle == INVALID_HANDLE_VALUE))
return INVALID_WINFD;
for (n = 0; n < nfds; ++n) {
fds[n].revents = 0;
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);
// 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;
}
if ((fds[n].fd >= 0) && (fds[n].fd < MAX_FDS))
fd = fd_table[fds[n].fd];
else
fd = NULL;
assert(fd != NULL);
if (fd == NULL) {
fds[n].revents = POLLNVAL;
nready++;
continue;
}
// 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;
usbi_mutex_static_unlock(&fd_table_lock);
return nready;
}
struct winfd overlapped_to_winfd(OVERLAPPED* overlapped)
{
int i;
struct winfd wfd;
CHECK_INIT_POLLING;
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;
}
/*
* 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;
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;
}
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;
}
_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);
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;
}
// 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;
}
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;
}
// 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);
}
nready = check_pollfds(fds, nfds, wait_handles, &nb_wait_handles);
// 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
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;
}
}
poll_exit:
if (handles_to_wait_on != NULL) {
free(handles_to_wait_on);
}
if (handle_to_index != NULL) {
free(handle_to_index);
}
return triggered;
return nready;
}
/*
* close a fake pipe fd
* close a fake file descriptor
*/
int usbi_close(int fd)
int usbi_close(int _fd)
{
int _index;
int r = -1;
struct file_descriptor *fd;
CHECK_INIT_POLLING;
if (_fd < 0 || _fd >= MAX_FDS)
goto err_badfd;
_index = _fd_to_index_and_lock(fd);
usbi_mutex_static_lock(&fd_table_lock);
fd = fd_table[_fd];
fd_table[_fd] = NULL;
usbi_mutex_static_unlock(&fd_table_lock);
if (_index < 0) {
errno = EBADF;
if (fd == NULL)
goto err_badfd;
if (fd->type == FD_TYPE_PIPE) {
// InternalHigh is our reference count
fd->overlapped.InternalHigh--;
if (fd->overlapped.InternalHigh == 0)
free_fd(fd);
} else {
free_overlapped(poll_fd[_index].overlapped);
poll_fd[_index] = INVALID_WINFD;
LeaveCriticalSection(&_poll_fd[_index].mutex);
free_fd(fd);
}
return r;
return 0;
err_badfd:
errno = EBADF;
return -1;
}
/*
* 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])
{
struct file_descriptor *fd;
int r_fd = -1, w_fd = -1;
int i;
fd = create_fd(FD_TYPE_PIPE);
if (fd == NULL) {
errno = ENOMEM;
return -1;
}
// Use InternalHigh as a reference count
fd->overlapped.Internal = STATUS_PENDING;
fd->overlapped.InternalHigh = 2;
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;
}
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;
}
if (error)
goto err_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;
}
return sizeof(unsigned char);
r = sizeof(unsigned char);
out:
LeaveCriticalSection(&_poll_fd[_index].mutex);
return r;
err_out:
errno = error;
return -1;
}

View 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
*/

View File

@@ -22,7 +22,11 @@
#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)
{

View 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;

View 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;

View 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);

View 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) {

View 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 = usbi_add_pollfd(ctx, wfd.fd, events);
if (r) {
usbi_close(wfd.fd);
return r;
}
r = priv->apib->submit_bulk_transfer(SUB_API_NOTSET, itransfer);
if (r != LIBUSB_SUCCESS)
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,
(short)(IS_XFERIN(transfer) ? POLLIN : POLLOUT));
r = transfer_fn(SUB_API_NOTSET, itransfer);
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);
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_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;
}
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;
if (priv->apib->submit_control_transfer == NULL) {
PRINT_UNSUPPORTED_API(submit_control_transfer);
}
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)

View 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

View File

@@ -1 +1 @@
#define LIBUSB_NANO 11273
#define LIBUSB_NANO 11274