From d4a736d822a6a7e8494f4055e77c188c53c1fa2f Mon Sep 17 00:00:00 2001 From: Pete Batard Date: Sat, 6 Mar 2010 00:15:19 +0000 Subject: merged r197 --- configure.ac | 8 +- examples/lsusb.dsp | 6 +- examples/lsusb_2008.vcproj | 4 + examples/xusb.c | 51 ++- examples/xusb.dsp | 6 +- examples/xusb_2008.vcproj | 4 + libusb-dll_2005.vcproj | 4 +- libusb-dll_2008.vcproj | 14 +- libusb-static_2005.vcproj | 4 +- libusb-static_2008.vcproj | 14 +- libusb/Makefile.am | 8 +- libusb/core.c | 79 ++-- libusb/io.c | 2 +- libusb/libusb.h | 2 +- libusb/libusbi.h | 4 +- libusb/os/poll_posix.h | 12 + libusb/os/poll_windows.c | 1020 +++++++++++++++++++++++++++++++++++++++++++ libusb/os/poll_windows.h | 134 ++++++ libusb/os/sources | 2 +- libusb/os/threads_windows.c | 4 +- libusb/os/unistd_posix.h | 12 - libusb/os/windows_compat.c | 910 -------------------------------------- libusb/os/windows_compat.h | 134 ------ libusb/os/windows_usb.c | 61 ++- libusb/os/windows_usb.h | 105 ++--- libusb_dll.dsp | 12 +- libusb_static.dsp | 8 +- msvc/config.h | 5 + 28 files changed, 1404 insertions(+), 1225 deletions(-) create mode 100644 libusb/os/poll_posix.h create mode 100644 libusb/os/poll_windows.c create mode 100644 libusb/os/poll_windows.h delete mode 100644 libusb/os/unistd_posix.h delete mode 100644 libusb/os/windows_compat.c delete mode 100644 libusb/os/windows_compat.h diff --git a/configure.ac b/configure.ac index 8d3ba9d..ac08e0b 100644 --- a/configure.ac +++ b/configure.ac @@ -43,17 +43,19 @@ case $host in threads="windows" LIBS="-lsetupapi -lole32" AM_CFLAGS="-Wshadow" - AM_LDFLAGS="-no-undefined" + AM_LDFLAGS="-no-undefined -avoid-version" + AC_CHECK_TOOL(RC, windres, no) ;; *-cygwin*) AC_DEFINE(OS_WINDOWS, [], [Windows backend]) AC_SUBST(OS_WINDOWS) AC_MSG_RESULT([Windows]) backend="windows" - threads="posix" + threads="posix" LIBS="-lpthread -lsetupapi -lole32" AM_CFLAGS="" - AM_LDFLAGS="-no-undefined" + AM_LDFLAGS="-no-undefined -avoid-version" + AC_CHECK_TOOL(RC, windres, no) ;; *) AC_MSG_ERROR([unsupported operating system]) diff --git a/examples/lsusb.dsp b/examples/lsusb.dsp index b598ffa..bf16c4e 100644 --- a/examples/lsusb.dsp +++ b/examples/lsusb.dsp @@ -50,7 +50,7 @@ BSC32=bscmake.exe # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib "D:/Program Files/Microsoft SDK/Lib/setupapi.lib" /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib setupapi.lib /nologo /subsystem:console /machine:I386 !ELSEIF "$(CFG)" == "lsusb - Win32 Debug" @@ -71,10 +71,10 @@ LINK32=link.exe # ADD RSC /l 0x409 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo -# ADD BSC32 /nologo /n "../Win32/Debug/dll/core.sbr" "../Win32/Debug/dll/descriptor.sbr" "../Win32/Debug/dll/io.sbr" "../Win32/Debug/dll/sync.sbr" "../Win32/Debug/dll/windows_compat.sbr" "../Win32/Debug/dll/windows_usb.sbr" +# ADD BSC32 /nologo /n "../Win32/Debug/dll/core.sbr" "../Win32/Debug/dll/descriptor.sbr" "../Win32/Debug/dll/io.sbr" "../Win32/Debug/dll/sync.sbr" "../Win32/Debug/dll/poll_windows.sbr" "../Win32/Debug/dll/windows_usb.sbr" LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib "D:/Program Files/Microsoft SDK/Lib/setupapi.lib" /nologo /subsystem:console /debug /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib setupapi.lib /nologo /subsystem:console /debug /machine:I386 # SUBTRACT LINK32 /pdb:none !ENDIF diff --git a/examples/lsusb_2008.vcproj b/examples/lsusb_2008.vcproj index 168cf3f..f56019c 100644 --- a/examples/lsusb_2008.vcproj +++ b/examples/lsusb_2008.vcproj @@ -53,6 +53,7 @@ UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="4" + CallingConvention="2" /> = 0) { display_buffer_hex(input_report, size); - } else if (r == LIBUSB_ERROR_NOT_FOUND) { - printf(" No Feature Report available for this device\n"); } else { - printf(" Error: %s\n", libusb_strerror(r)); + switch(r) { + case LIBUSB_ERROR_NOT_FOUND: + printf(" No Feature Report available for this device\n"); + break; + case LIBUSB_ERROR_PIPE: + printf(" Detected stall - resetting pipe...\n"); + libusb_clear_halt(handle, 0); + break; + default: + printf(" Error: %s\n", libusb_strerror(r)); + break; + } } printf("\nReading Input Report...\n"); @@ -514,6 +522,10 @@ int test_hid(libusb_device_handle *handle, uint8_t endpoint_in) case LIBUSB_ERROR_TIMEOUT: printf(" Timeout! Please make sure you act on the device within the 5 seconds allocated...\n"); break; + case LIBUSB_ERROR_PIPE: + printf(" Detected stall - resetting pipe...\n"); + libusb_clear_halt(handle, 0); + break; default: printf(" Error: %s\n", libusb_strerror(r)); break; @@ -521,8 +533,8 @@ int test_hid(libusb_device_handle *handle, uint8_t endpoint_in) } // Attempt a bulk read from endpoint 0 (this should just return a raw input report) - printf("\nTesting bulk read using endpoint %02X...\n", endpoint_in); - r = libusb_bulk_transfer(handle, endpoint_in, input_report, size, &size, 5000); + printf("\nTesting interrupt read using endpoint %02X...\n", endpoint_in); + r = libusb_interrupt_transfer(handle, endpoint_in, input_report, size, &size, 5000); if (r >= 0) { display_buffer_hex(input_report, size); } else { @@ -541,6 +553,9 @@ int test_device(uint16_t vid, uint16_t pid) const struct libusb_endpoint_descriptor *endpoint; int i, j, k, r; int iface, nb_ifaces, nb_strings; +#ifndef OS_WINDOWS + int iface_detached = -1; +#endif int test_scsi = 0; struct libusb_device_descriptor dev_desc; char string[128]; @@ -612,6 +627,7 @@ int test_device(uint16_t vid, uint16_t pid) // Maybe we need to detach the driver perr(" Failed. Trying to detach driver...\n"); libusb_detach_kernel_driver(handle, iface); + iface_detached = iface; printf(" Claiming interface again...\n"); libusb_claim_interface(handle, iface); } else { @@ -656,6 +672,13 @@ int test_device(uint16_t vid, uint16_t pid) libusb_release_interface(handle, iface); } +#ifndef OS_WINDOWS + if (iface_detached >= 0) { + printf("Re-attaching kernel driver...\n"); + libusb_attach_kernel_driver(handle, iface_detached); + } +#endif + printf("Closing device...\n"); libusb_close(handle); diff --git a/examples/xusb.dsp b/examples/xusb.dsp index b47b8c0..9bd5e38 100644 --- a/examples/xusb.dsp +++ b/examples/xusb.dsp @@ -50,7 +50,7 @@ BSC32=bscmake.exe # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib "D:/Program Files/Microsoft SDK/Lib/setupapi.lib" /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib setupapi.lib /nologo /subsystem:console /machine:I386 !ELSEIF "$(CFG)" == "xusb - Win32 Debug" @@ -71,10 +71,10 @@ LINK32=link.exe # ADD RSC /l 0x409 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo -# ADD BSC32 /nologo /n "../Win32/Debug/dll/core.sbr" "../Win32/Debug/dll/descriptor.sbr" "../Win32/Debug/dll/io.sbr" "../Win32/Debug/dll/sync.sbr" "../Win32/Debug/dll/windows_compat.sbr" "../Win32/Debug/dll/windows_usb.sbr" +# ADD BSC32 /nologo /n "../Win32/Debug/dll/core.sbr" "../Win32/Debug/dll/descriptor.sbr" "../Win32/Debug/dll/io.sbr" "../Win32/Debug/dll/sync.sbr" "../Win32/Debug/dll/poll_windows.sbr" "../Win32/Debug/dll/windows_usb.sbr" LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept -# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib "D:/Program Files/Microsoft SDK/Lib/setupapi.lib" /nologo /subsystem:console /debug /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib setupapi.lib /nologo /subsystem:console /debug /machine:I386 !ENDIF diff --git a/examples/xusb_2008.vcproj b/examples/xusb_2008.vcproj index 224249a..f604ecc 100644 --- a/examples/xusb_2008.vcproj +++ b/examples/xusb_2008.vcproj @@ -53,6 +53,7 @@ UsePrecompiledHeader="0" WarningLevel="3" DebugInformationFormat="4" + CallingConvention="2" /> #include #ifndef OS_WINDOWS -#include "os/unistd_posix.h" +#include "os/poll_posix.h" #endif #include "libusbi.h" @@ -832,10 +832,55 @@ API_EXPORTED void libusb_unref_device(libusb_device *dev) list_del(&dev->list); usbi_mutex_unlock(&dev->ctx->usb_devs_lock); + usbi_mutex_destroy(&dev->lock); free(dev); } } +/* + * Interrupt the iteration of the event handling thread, so that it picks + * up the new fd. + */ +void usbi_fd_notification(struct libusb_context *ctx) +{ + unsigned char dummy = 1; + ssize_t r; + + if (ctx == NULL) + return; + + /* record that we are messing with poll fds */ + usbi_mutex_lock(&ctx->pollfd_modify_lock); + ctx->pollfd_modify++; + usbi_mutex_unlock(&ctx->pollfd_modify_lock); + + /* write some data on control pipe to interrupt event handlers */ + r = usbi_write(ctx->ctrl_pipe[1], &dummy, sizeof(dummy)); + if (r <= 0) { + usbi_warn(ctx, "internal signalling write failed"); + usbi_mutex_lock(&ctx->pollfd_modify_lock); + ctx->pollfd_modify--; + usbi_mutex_unlock(&ctx->pollfd_modify_lock); + return; + } + + /* take event handling lock */ + libusb_lock_events(ctx); + + /* read the dummy data */ + r = usbi_read(ctx->ctrl_pipe[0], &dummy, sizeof(dummy)); + if (r <= 0) + usbi_warn(ctx, "internal signalling read failed"); + + /* we're done with modifying poll fds */ + usbi_mutex_lock(&ctx->pollfd_modify_lock); + ctx->pollfd_modify--; + usbi_mutex_unlock(&ctx->pollfd_modify_lock); + + /* Release event handling lock and wake up event waiters */ + libusb_unlock_events(ctx); +} + /** \ingroup dev * Open a device and obtain a device handle. A handle allows you to perform * I/O on the device in question. @@ -860,7 +905,6 @@ API_EXPORTED int libusb_open(libusb_device *dev, libusb_device_handle **handle) struct libusb_context *ctx = DEVICE_CTX(dev); struct libusb_device_handle *_handle; size_t priv_size = usbi_backend->device_handle_priv_size; - unsigned char dummy = 1; ssize_t r; usbi_dbg("open %d.%d", dev->bus_number, dev->device_address); @@ -899,36 +943,7 @@ API_EXPORTED int libusb_open(libusb_device *dev, libusb_device_handle **handle) * or infinite timeout. We want to interrupt that iteration of the loop, * so that it picks up the new fd, and then continues. */ - /* record that we are messing with poll fds */ - usbi_mutex_lock(&ctx->pollfd_modify_lock); - ctx->pollfd_modify++; - usbi_mutex_unlock(&ctx->pollfd_modify_lock); - - /* write some data on control pipe to interrupt event handlers */ - r = usbi_write(ctx->ctrl_pipe[1], &dummy, sizeof(dummy)); - if (r <= 0) { - usbi_warn(ctx, "internal signalling write failed"); - usbi_mutex_lock(&ctx->pollfd_modify_lock); - ctx->pollfd_modify--; - usbi_mutex_unlock(&ctx->pollfd_modify_lock); - return 0; - } - - /* take event handling lock */ - libusb_lock_events(ctx); - - /* read the dummy data */ - r = usbi_read(ctx->ctrl_pipe[0], &dummy, sizeof(dummy)); - if (r <= 0) - usbi_warn(ctx, "internal signalling read failed"); - - /* we're done with modifying poll fds */ - usbi_mutex_lock(&ctx->pollfd_modify_lock); - ctx->pollfd_modify--; - usbi_mutex_unlock(&ctx->pollfd_modify_lock); - - /* Release event handling lock and wake up event waiters */ - libusb_unlock_events(ctx); + usbi_fd_notification(ctx); return 0; } diff --git a/libusb/io.c b/libusb/io.c index 48122b5..a7728e0 100644 --- a/libusb/io.c +++ b/libusb/io.c @@ -25,7 +25,7 @@ #include #ifndef OS_WINDOWS -#include "os/unistd_posix.h" +#include "os/poll_posix.h" #endif #ifdef USBI_TIMERFD_AVAILABLE diff --git a/libusb/libusb.h b/libusb/libusb.h index 8986013..0127d9f 100644 --- a/libusb/libusb.h +++ b/libusb/libusb.h @@ -28,8 +28,8 @@ #include #endif #include -#include #include +#include /* 'interface' might be defined as a macro on Windows, so we need to * undefine it so as not to break the current libusb API, because diff --git a/libusb/libusbi.h b/libusb/libusbi.h index 33b642c..d7fd4a4 100644 --- a/libusb/libusbi.h +++ b/libusb/libusbi.h @@ -178,10 +178,10 @@ void inline usbi_dbg(const char *format, ...) #include #elif defined(OS_WINDOWS) && (defined(__CYGWIN__) || defined(USE_PTHREAD)) #include -#include +#include #elif defined(OS_WINDOWS) #include -#include +#include #endif extern struct libusb_context *usbi_default_context; diff --git a/libusb/os/poll_posix.h b/libusb/os/poll_posix.h new file mode 100644 index 0000000..17298a5 --- /dev/null +++ b/libusb/os/poll_posix.h @@ -0,0 +1,12 @@ +#ifndef __LIBUSB_POLL_POSIX_H__ +#define __LIBUSB_POLL_POSIX_H__ + +#include +#include +#define usbi_write write +#define usbi_read read +#define usbi_close close +#define usbi_pipe pipe +#define usbi_poll poll + +#endif /* __LIBUSB_POLL_POSIX_H__ */ diff --git a/libusb/os/poll_windows.c b/libusb/os/poll_windows.c new file mode 100644 index 0000000..b6fed7c --- /dev/null +++ b/libusb/os/poll_windows.c @@ -0,0 +1,1020 @@ +/* + * poll_windows: poll compatibility wrapper for Windows + * Copyright (C) 2009-2010 Pete Batard + * With contributions from Michael Plante, Orin Eman et al. + * Parts of poll implementation from libusb-win32, by Stephan Meyer et al. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +/* + * poll() and pipe() Windows compatibility layer for libusb 1.0 + * + * The way this layer works is by using OVERLAPPED with async I/O transfers, as + * OVERLAPPED have an associated event which is flagged for I/O completion. + * + * For USB pollable async I/O, you would typically: + * - 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 _O_RDONLY and once in _O_WRONLY mode to obtain 2 separate + * pollable fds + * - leave the core functions call the poll routine and flag POLLIN/POLLOUT + * + * For pipe pollable synchronous I/O (read end polling only), you would: + * - create an anonymous pipe with usbi_pipe to obtain 2 fds (r & w) + * - use usbi_write / usbi_read to write to either end of the pipe + * - use poll to check for data to read + * Note that the usbi_read/usbi_write function actually perform + * asynchronous I/O internally, and could potentially be modified to support + * O_NON_BLOCK + * + * The way the polling on usbi_read works is by splitting all read I/O + * into a dual 1 byte/n-1 bytes asynchronous read operation. + * The 1 byte data (called the marker), is always armed for asynchronous + * readout, so that as soon as data becomes available, an OVERLAPPED event + * will be flagged, which poll can report. + * Then during the usbi_read routine itself, this 1 byte marker is copied + * to the buffer, along with the rest of the data. + * + * Note that, since most I/O is buffered, being notified when only the first + * byte of data is available is unlikely to delay read operations, since the + * rest of the data should be available in system buffers by the time read + * is called. + * + * Also note that if you don't use usbi_read to read inbound data, but + * use the OVERLAPPED directly (which is what we do in the USB async I/O + * functions), the marker is not used at all. + */ +#include +#include +#include +#include +#include + +#include + +// Uncomment to debug the polling layer +//#define DEBUG_POLL_WINDOWS + +// Uncomment to have poll return with EINTR as soon as a new transfer (fd) is added +// This should result in a LIBUSB_ERROR_INTERRUPTED being returned by libusb calls, +// which should give the app an opportunity to resubmit a new fd set. +//#define DYNAMIC_FDS + +#if defined(DEBUG_POLL_WINDOWS) +#define poll_dbg usbi_dbg +#else +// MSVC6 cannot use a variadic argument and non MSVC +// compilers produce warnings if parenthesis are ommitted. +#if defined(_MSC_VER) +#define poll_dbg +#else +#define poll_dbg(...) +#endif +#endif + +#if defined(_PREFAST_) +#pragma warning(disable:28719) +#endif + +#if defined(__CYGWIN__) +// cygwin produces a warning unless these prototypes are defined +extern int _close(int fd); +extern int _snprintf(char *buffer, size_t count, const char *format, ...); +extern int cygwin_attach_handle_to_fd(char *name, int fd, HANDLE handle, int bin, int access); +// _open_osfhandle() is not available on cygwin, but we can emulate +// it for our needs with cygwin_attach_handle_to_fd() +static inline int _open_osfhandle(intptr_t osfhandle, int flags) +{ + int access; + switch (flags) { + case _O_RDONLY: + access = GENERIC_READ; + break; + case _O_WRONLY: + access = GENERIC_WRITE; + break; + case _O_RDWR: + access = GENERIC_READ|GENERIC_WRITE; + break; + default: + usbi_err(NULL, "unuspported access mode"); + return -1; + } + return cygwin_attach_handle_to_fd("/dev/null", -1, (HANDLE)osfhandle, -1, access); +} +#endif + +#define CHECK_INIT_POLLING do {if(!is_polling_set) init_polling();} while(0) + +extern void usbi_fd_notification(struct libusb_context *ctx); + +// public fd data +const struct winfd INVALID_WINFD = {-1, NULL, NULL, RW_NONE, FALSE}; +struct winfd poll_fd[MAX_FDS]; +// internal fd data +struct { + CRITICAL_SECTION mutex; // lock for fds + BYTE marker; // 1st byte of a usbi_read operation gets stored here + +} _poll_fd[MAX_FDS]; + +// globals +BOOLEAN is_polling_set = FALSE; +#if defined(DYNAMIC_FDS) +HANDLE fd_update = INVALID_HANDLE_VALUE; // event to notify poll of fd update +HANDLE new_fd[MAX_FDS]; // overlapped event handlesm for fds created since last poll +unsigned nb_new_fds = 0; // nb new fds created since last poll +usbi_mutex_t new_fd_mutex; // mutex required for the above +#endif +LONG pipe_number = 0; +static volatile LONG compat_spinlock = 0; + +// 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; +__inline BOOL cancel_io(int index) +{ + if ((index < 0) || (index >= MAX_FDS)) { + return FALSE; + } + if (pCancelIoEx != NULL) { + return (*pCancelIoEx)(poll_fd[index].handle, poll_fd[index].overlapped); + } else { + return CancelIo(poll_fd[index].handle); + } +} + +// Init +void init_polling(void) +{ + int i; + + while (InterlockedExchange((LONG *)&compat_spinlock, 1) == 1) { + SleepEx(0, TRUE); + } + if (!is_polling_set) { + pCancelIoEx = (BOOL (__stdcall *)(HANDLE,LPOVERLAPPED)) + GetProcAddress(GetModuleHandle("KERNEL32"), "CancelIoEx"); + usbi_dbg("Will use CancelIo%s for I/O cancellation", + (pCancelIoEx != NULL)?"Ex":""); + for (i=0; ihEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if(overlapped->hEvent == NULL) { + free (overlapped); + return NULL; + } + return overlapped; +} + +void free_overlapped(OVERLAPPED *overlapped) +{ + if (overlapped == NULL) + return; + + if ( (overlapped->hEvent != 0) + && (overlapped->hEvent != INVALID_HANDLE_VALUE) ) { + CloseHandle(overlapped->hEvent); + } + free(overlapped); +} + +void reset_overlapped(OVERLAPPED *overlapped) +{ + HANDLE event_handle; + if (overlapped == NULL) + return; + + event_handle = overlapped->hEvent; + if (event_handle != NULL) { + ResetEvent(event_handle); + } + memset(overlapped, 0, sizeof(OVERLAPPED)); + overlapped->hEvent = event_handle; +} + +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 0) && (poll_fd[i].handle != INVALID_HANDLE_VALUE) && (poll_fd[i].handle != 0) + && (GetFileType(poll_fd[i].handle) == FILE_TYPE_UNKNOWN) ) { + _close(poll_fd[i].fd); + } + free_overlapped(poll_fd[i].overlapped); + poll_fd[i] = INVALID_WINFD; +#if defined(DYNAMIC_FDS) + usbi_mutex_destroy(&new_fd_mutex); + CloseHandle(fd_update); + fd_update = INVALID_HANDLE_VALUE; +#endif + LeaveCriticalSection(&_poll_fd[i].mutex); + DeleteCriticalSection(&_poll_fd[i].mutex); + } + } + compat_spinlock = 0; +} + +/* + * sets the async I/O read on our 1 byte marker + * + * requires a valid index + */ +__inline void _init_read_marker(int index) +{ + // Cancel any read operation in progress + cancel_io(index); + // Setup a new async read on our marker + reset_overlapped(poll_fd[index].overlapped); + if (!ReadFile(poll_fd[index].handle, &_poll_fd[index].marker, 1, NULL, poll_fd[index].overlapped)) { + if(GetLastError() != ERROR_IO_PENDING) { + usbi_warn(NULL, "didn't get IO_PENDING!"); + reset_overlapped(poll_fd[index].overlapped); + } + } else { + // We got some sync I/O. We'll pretend it's async and set overlapped manually + poll_dbg("marker readout completed before exit!"); + if (!HasOverlappedIoCompleted(poll_fd[index].overlapped)) { + usbi_warn(NULL, "completed I/O still flagged as pending"); + poll_fd[index].overlapped->Internal = 0; + } + SetEvent(poll_fd[index].overlapped->hEvent); + poll_fd[index].overlapped->InternalHigh = 1; + } +} + +/* + * Create an async I/O anonymous pipe (that can be used for sync as well) + */ +int usbi_pipe(int filedes[2]) +{ + int i, j; + HANDLE handle[2]; + OVERLAPPED *overlapped0, *overlapped1; + char pipe_name[] = "\\\\.\\pipe\\libusb000000000000"; + LONG our_pipe_number; + + CHECK_INIT_POLLING; + + overlapped0 = calloc(1, sizeof(OVERLAPPED)); + if (overlapped0 == NULL) { + return -1; + } + + overlapped1 = calloc(1, sizeof(OVERLAPPED)); + if (overlapped1 == NULL) { + free(overlapped0); + return -1; + } + + our_pipe_number = InterlockedIncrement(&pipe_number) - 1; // - 1 to mirror postfix operation inside _snprintf + if (our_pipe_number >= 0x10000) { + usbi_warn(NULL, "program assertion failed - more than 65536 pipes were used"); + our_pipe_number &= 0xFFFF; + } + _snprintf(pipe_name, sizeof(pipe_name), "\\\\.\\pipe\\libusb%08x%04x", (unsigned)GetCurrentProcessId(), our_pipe_number); + + // Read end of the pipe + handle[0] = CreateNamedPipeA(pipe_name, PIPE_ACCESS_INBOUND|FILE_FLAG_OVERLAPPED, + PIPE_TYPE_MESSAGE|PIPE_READMODE_MESSAGE, 1, 4096, 4096, 0, NULL); + if (handle[0] == INVALID_HANDLE_VALUE) { + usbi_err(NULL, "could not create pipe (read end): errcode %d", (int)GetLastError()); + goto out1; + } + filedes[0] = _open_osfhandle((intptr_t)handle[0], _O_RDONLY); + poll_dbg("filedes[0] = %d", filedes[0]); + + // Write end of the pipe + handle[1] = CreateFileA(pipe_name, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED, NULL); + if (handle[1] == INVALID_HANDLE_VALUE) { + usbi_err(NULL, "could not create pipe (write end): errcode %d", (int)GetLastError()); + goto out2; + } + filedes[1] = _open_osfhandle((intptr_t)handle[1], _O_WRONLY); + poll_dbg("filedes[1] = %d", filedes[1]); + + // Create an OVERLAPPED for each end + overlapped0->hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if(!overlapped0->hEvent) { + goto out3; + } + overlapped1->hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if(!overlapped1->hEvent) { + goto out4; + } + + for (i=0, j=0; i= 0) { + LeaveCriticalSection(&_poll_fd[i].mutex); + continue; + } + + poll_fd[i].fd = filedes[j]; + poll_fd[i].handle = handle[j]; + poll_fd[i].overlapped = (j==0)?overlapped0:overlapped1; + poll_fd[i].rw = RW_READ+j; + poll_fd[i].completed_synchronously = FALSE; + j++; + if (j==1) { + // Start a 1 byte nonblocking read operation + // so that we get read event notifications + _init_read_marker(i); + } + LeaveCriticalSection(&_poll_fd[i].mutex); + if (j>=2) { + return 0; + } + } + } + + CloseHandle(overlapped1->hEvent); +out4: + CloseHandle(overlapped0->hEvent); +out3: + CloseHandle(handle[1]); +out2: + CloseHandle(handle[0]); +out1: + free(overlapped1); + free(overlapped0); + return -1; +} + +/* + * Create both an fd and an OVERLAPPED from an open Windows handle, 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) + * 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 libusb_context *ctx) +{ + int i, fd; + struct winfd wfd = INVALID_WINFD; + OVERLAPPED* overlapped = NULL; + + CHECK_INIT_POLLING; + + if ((handle == 0) || (handle == INVALID_HANDLE_VALUE)) { + return INVALID_WINFD; + } + + if ((access_mode != _O_RDONLY) && (access_mode != _O_WRONLY)) { + usbi_warn(NULL, "only one of _O_RDONLY or _O_WRONLY are supported.\n" + "If you want to poll for R/W simultaneously, create multiple fds from the same handle."); + return INVALID_WINFD; + } + if (access_mode == _O_RDONLY) { + wfd.rw = RW_READ; + } else { + wfd.rw = RW_WRITE; + } + + // Ensure that we get a non system conflicting unique fd + fd = _open_osfhandle((intptr_t)CreateFileA("NUL", 0, 0, + NULL, OPEN_EXISTING, 0, NULL), _O_RDWR); + if (fd < 0) { + return INVALID_WINFD; + } + + overlapped = create_overlapped(); + if(overlapped == NULL) { + _close(fd); + return INVALID_WINFD; + } + + for (i=0; i= 0) { + LeaveCriticalSection(&_poll_fd[i].mutex); + continue; + } + wfd.fd = fd; + wfd.handle = handle; + wfd.overlapped = overlapped; + memcpy(&poll_fd[i], &wfd, sizeof(struct winfd)); + LeaveCriticalSection(&_poll_fd[i].mutex); +#if defined(DYNAMIC_FDS) + usbi_mutex_lock(&new_fd_mutex); + new_fd[nb_new_fds++] = overlapped->hEvent; + usbi_mutex_unlock(&new_fd_mutex); + // Notify poll that fds have been updated + SetEvent(fd_update); +#else + // NOTE: For now, usbi_fd_notification is only called on fd creation, as + // fd deletion results in a CancelIo() event, which poll should detect. + // Will see if there's an actual justification to call this on delete... + usbi_fd_notification(ctx); +#endif + return wfd; + } + } + free_overlapped(overlapped); + _close(fd); + return INVALID_WINFD; +} + +void _free_index(int index) +{ + // Cancel any async IO (Don't care about the validity of our handles for this) + cancel_io(index); + // close fake handle for devices + if ( (poll_fd[index].handle != INVALID_HANDLE_VALUE) && (poll_fd[index].handle != 0) + && (GetFileType(poll_fd[index].handle) == FILE_TYPE_UNKNOWN) ) { + _close(poll_fd[index].fd); + } + 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(int fd) +{ + int index; + + CHECK_INIT_POLLING; + + index = _fd_to_index_and_lock(fd); + if (index < 0) { + return; + } + _free_index(index); + 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 (i=0; i= 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[%d] without READ access", i); + 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[%d] without WRITE access", i); + LeaveCriticalSection(&_poll_fd[index].mutex); + triggered = -1; + goto poll_exit; + } + + poll_dbg("fd[%d]=%d (overlapped = %p) got events %04X", i, poll_fd[index].fd, poll_fd[index].overlapped, fds[i].events); + + // The following macro only works if overlapped I/O was reported pending + if ( (HasOverlappedIoCompleted(poll_fd[index].overlapped)) + || (poll_fd[index].completed_synchronously) ) { + 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; +#if defined(DYNAMIC_FDS) + // If this fd from the poll set is also part of the new_fd event handle table, remove it + usbi_mutex_lock(&new_fd_mutex); + for (j=0; j= 0) && ((DWORD)object_index < nb_handles_to_wait_on)) { +#if defined(DYNAMIC_FDS) + if ((DWORD)object_index >= (nb_handles_to_wait_on-nb_extra_handles)) { + // Detected fd update => flag a poll interruption + if ((DWORD)object_index == (nb_handles_to_wait_on-1)) + poll_dbg(" dynamic_fds: fd_update event"); + else + poll_dbg(" dynamic_fds: new fd I/O event"); + errno = EINTR; + triggered = -1; + goto poll_exit; + } +#endif + 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 + } + } + +poll_exit: + if (handles_to_wait_on != NULL) { + free(handles_to_wait_on); + } + if (handle_to_index != NULL) { + free(handle_to_index); + } +#if defined(DYNAMIC_FDS) + usbi_mutex_lock(&new_fd_mutex); + nb_new_fds = 0; + usbi_mutex_unlock(&new_fd_mutex); +#endif + return triggered; +} + +/* + * close a pollable fd + * + * Note that this function will also close the associated handle + */ +int usbi_close(int fd) +{ + int index; + HANDLE handle; + int r = -1; + + CHECK_INIT_POLLING; + + index = _fd_to_index_and_lock(fd); + + if (index < 0) { + errno = EBADF; + } else { + handle = poll_fd[index].handle; + _free_index(index); + if (CloseHandle(handle) == 0) { + errno = EIO; + } else { + r = 0; + } + LeaveCriticalSection(&_poll_fd[index].mutex); + } + return r; +} + +/* + * synchronous write for custom poll (works on Windows file handles that + * have been opened with the FILE_FLAG_OVERLAPPED flag) + * + * Current restrictions: + * - binary mode only + * - no append mode + */ +ssize_t usbi_write(int fd, const void *buf, size_t count) +{ + int index; + DWORD wr_count; + int r = -1; + + CHECK_INIT_POLLING; + + index = _fd_to_index_and_lock(fd); + + if (count == 0) { + return 0; + } + + if ( (index < 0) || (poll_fd[index].overlapped == NULL) + || (poll_fd[index].rw != RW_WRITE) ) { + errno = EBADF; + if (index >= 0) { + LeaveCriticalSection(&_poll_fd[index].mutex); + } + return -1; + } + + // For sync mode, we shouldn't get pending async write I/O + if (!HasOverlappedIoCompleted(poll_fd[index].overlapped)) { + usbi_warn(NULL, "usbi_write: previous write I/O was flagged pending!"); + cancel_io(index); + } + + poll_dbg("writing %d bytes to fd=%d", count, poll_fd[index].fd); + + reset_overlapped(poll_fd[index].overlapped); + if (!WriteFile(poll_fd[index].handle, buf, (DWORD)count, &wr_count, poll_fd[index].overlapped)) { + if(GetLastError() == ERROR_IO_PENDING) { + // I/O started but is not completed => wait till completion + switch(WaitForSingleObject(poll_fd[index].overlapped->hEvent, INFINITE)) + { + case WAIT_OBJECT_0: + if (GetOverlappedResult(poll_fd[index].handle, + poll_fd[index].overlapped, &wr_count, FALSE)) { + r = 0; + goto out; + } else { + usbi_warn(NULL, "GetOverlappedResult failed with error %d", (int)GetLastError()); + errno = EIO; + goto out; + } + default: + errno = EIO; + goto out; + } + } else { + // I/O started and failed + usbi_warn(NULL, "WriteFile failed with error %d", (int)GetLastError()); + errno = EIO; + goto out; + } + } + + // I/O started and completed synchronously + r = 0; + +out: + if (r) { + reset_overlapped(poll_fd[index].overlapped); + LeaveCriticalSection(&_poll_fd[index].mutex); + return -1; + } else { + LeaveCriticalSection(&_poll_fd[index].mutex); + return (ssize_t)wr_count; + } +} + +/* + * synchronous read for custom poll (works on Windows file handles that + * have been opened with the FILE_FLAG_OVERLAPPED flag) + */ +ssize_t usbi_read(int fd, void *buf, size_t count) +{ + int index; + DWORD rd_count; + int r = -1; + + CHECK_INIT_POLLING; + + if (count == 0) { + return 0; + } + + index = _fd_to_index_and_lock(fd); + + if (index < 0) { + errno = EBADF; + return -1; + } + + if (poll_fd[index].rw != RW_READ) { + errno = EBADF; + goto out; + } + + + // still waiting for completion => force completion + if (!HasOverlappedIoCompleted(poll_fd[index].overlapped)) { + if (WaitForSingleObject(poll_fd[index].overlapped->hEvent, INFINITE) != WAIT_OBJECT_0) { + usbi_warn(NULL, "waiting for marker failed: %d", (int)GetLastError()); + errno = EIO; + goto out; + } + } + + // Find out if we've read the first byte + if (!GetOverlappedResult(poll_fd[index].handle, poll_fd[index].overlapped, &rd_count, FALSE)) { + if (GetLastError() != ERROR_MORE_DATA) { + usbi_warn(NULL, "readout of marker failed: %d", (int)GetLastError()); + errno = EIO; + goto out; + } else { + usbi_warn(NULL, "readout of marker reported more data"); + } + } + + poll_dbg("count = %d, rd_count(marker) = %d", count, (int)rd_count); + + // We should have our marker by now + if (rd_count != 1) { + usbi_warn(NULL, "unexpected number of bytes for marker (%d)", (int)rd_count); + errno = EIO; + goto out; + } + + ((BYTE*)buf)[0] = _poll_fd[index].marker; + + // Read supplementary bytes if needed (blocking) + if (count > 1) { + reset_overlapped(poll_fd[index].overlapped); + if (!ReadFile(poll_fd[index].handle, (char*)buf+1, (DWORD)(count-1), &rd_count, poll_fd[index].overlapped)) { + if(GetLastError() == ERROR_IO_PENDING) { + if (!GetOverlappedResult(poll_fd[index].handle, poll_fd[index].overlapped, &rd_count, TRUE)) { + if (GetLastError() == ERROR_MORE_DATA) { + usbi_warn(NULL, "could not fetch all data"); + } + usbi_warn(NULL, "readout of supplementary data failed: %d", (int)GetLastError()); + errno = EIO; + goto out; + } + } else { + usbi_warn(NULL, "could not start blocking read of supplementary: %d", (int)GetLastError()); + errno = EIO; + goto out; + } + } + // If ReadFile completed synchronously, we're fine too + + poll_dbg("rd_count(supplementary ) = %d", (int)rd_count); + + if ((rd_count+1) != count) { + poll_dbg("wanted %d-1, got %d", count, (int)rd_count); + errno = EIO; + goto out; + } + } + + r = 0; + +out: + // Setup pending read I/O for the marker + _init_read_marker(index); + LeaveCriticalSection(&_poll_fd[index].mutex); + if (r) + return -1; + else + return count; +} diff --git a/libusb/os/poll_windows.h b/libusb/os/poll_windows.h new file mode 100644 index 0000000..996ff48 --- /dev/null +++ b/libusb/os/poll_windows.h @@ -0,0 +1,134 @@ +/* + * Windows compat: POSIX compatibility wrapper + * Copyright (C) 2009-2010 Pete Batard + * With contributions from Michael Plante, Orin Eman et al. + * Parts of poll implementation from libusb-win32, by Stephan Meyer et al. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +#pragma once + +#if defined(_MSC_VER) +// disable /W4 MSVC warnings that are benign +#pragma warning(disable:4127) // conditional expression is constant +#endif + +#if !defined(ssize_t) +#if defined (_WIN64) +#define ssize_t __int64 +#else +#define ssize_t long +#endif +#endif + +enum windows_version { + WINDOWS_UNSUPPORTED, + WINDOWS_XP, + WINDOWS_VISTA_AND_LATER, +}; +extern enum windows_version windows_version; + +#define MAX_FDS 256 + +#if !defined(__CYGWIN__) +#define POLLIN 0x0001 /* There is data to read */ +#define POLLPRI 0x0002 /* There is urgent data to read */ +#define POLLOUT 0x0004 /* Writing now will not block */ +#define POLLERR 0x0008 /* Error condition */ +#define POLLHUP 0x0010 /* Hung up */ +#define POLLNVAL 0x0020 /* Invalid request: fd not open */ + +struct pollfd { + int fd; /* file descriptor */ + short events; /* requested events */ + short revents; /* returned events */ +}; +#endif +typedef unsigned int nfds_t; + +// access modes +enum rw_type { + RW_NONE, + RW_READ, + RW_WRITE, +}; + +// fd struct that can be used for polling on Windows +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 + enum rw_type rw; // I/O transfer direction: read *XOR* write (NOT BOTH) + BOOLEAN completed_synchronously;// flag for async transfers that completed during request +}; +extern const struct winfd INVALID_WINFD; + +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 libusb_context *ctx); +void usbi_free_fd(int fd); +struct winfd fd_to_winfd(int fd); +struct winfd handle_to_winfd(HANDLE handle); +struct winfd overlapped_to_winfd(OVERLAPPED* overlapped); + +// When building using the MSDDK and sources +#if defined(DDKBUILD) +#if !defined(timeval) +struct timeval { + long tv_sec; /* seconds */ + long tv_usec; /* and microseconds */ +}; +#endif + +#if !defined(timerisset) +#define timerisset(tvp) ((tvp)->tv_sec || (tvp)->tv_usec) +#endif + +#if !defined(timercmp) +#define timercmp(tvp, uvp, cmp) \ + ((tvp)->tv_sec cmp (uvp)->tv_sec || \ + (tvp)->tv_sec == (uvp)->tv_sec && (tvp)->tv_usec cmp (uvp)->tv_usec) +#endif + +#if !defined(timerclr) +#define timerclear(tvp) (tvp)->tv_sec = (tvp)->tv_usec = 0 +#endif +#endif + +#if !defined(TIMESPEC_TO_TIMEVAL) +#define TIMESPEC_TO_TIMEVAL(tv, ts) { \ + (tv)->tv_sec = (long)(ts)->tv_sec; \ + (tv)->tv_usec = (long)(ts)->tv_nsec / 1000; \ +} +#endif +#if !defined(timersub) +#define timersub(a, b, result) \ +do { \ + (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ + (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \ + if ((result)->tv_usec < 0) { \ + --(result)->tv_sec; \ + (result)->tv_usec += 1000000; \ + } \ +} while (0) +#endif + diff --git a/libusb/os/sources b/libusb/os/sources index 669e32a..dc474ca 100644 --- a/libusb/os/sources +++ b/libusb/os/sources @@ -23,7 +23,7 @@ SOURCES=..\core.c \ ..\io.c \ ..\sync.c \ threads_windows.c \ - windows_compat.c \ + poll_windows.c \ windows_usb.c \ libusb-1.0.rc diff --git a/libusb/os/threads_windows.c b/libusb/os/threads_windows.c index 11b55f9..7762190 100644 --- a/libusb/os/threads_windows.c +++ b/libusb/os/threads_windows.c @@ -61,8 +61,8 @@ int usbi_mutex_lock(usbi_mutex_t *mutex) { // so don't know proper errno } int usbi_mutex_unlock(usbi_mutex_t *mutex) { - if(!mutex) return ((errno=EINVAL)); - if(!ReleaseMutex(mutex)) return ((errno=EPERM )); + if(!mutex) return ((errno=EINVAL)); + if(!ReleaseMutex(*mutex)) return ((errno=EPERM )); return 0; } diff --git a/libusb/os/unistd_posix.h b/libusb/os/unistd_posix.h deleted file mode 100644 index 0e9981d..0000000 --- a/libusb/os/unistd_posix.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef __LIBUSB_UNISTD_POSIX_H__ -#define __LIBUSB_UNISTD_POSIX_H__ - -#include -#include -#define usbi_write write -#define usbi_read read -#define usbi_close close -#define usbi_pipe pipe -#define usbi_poll poll - -#endif /* __LIBUSB_UNISTD_POSIX_H__ */ diff --git a/libusb/os/windows_compat.c b/libusb/os/windows_compat.c deleted file mode 100644 index 664bf5d..0000000 --- a/libusb/os/windows_compat.c +++ /dev/null @@ -1,910 +0,0 @@ -/* - * Windows compat: POSIX compatibility wrapper - * Copyright (C) 2009-2010 Pete Batard - * With contributions from Michael Plante, Orin Eman et al. - * Parts of poll implementation from libusb-win32, by Stephan Meyer et al. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -/* - * Posix poll() and pipe() Windows compatibility layer for libusb 1.0 - * - * The way this layer works is by using OVERLAPPED with async I/O transfers, as - * OVERLAPPED have an associated event which is flagged for I/O completion. - * - * For USB pollable async I/O, you would typically: - * - 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 _O_RDONLY and once in _O_WRONLY mode to obtain 2 separate - * pollable fds - * - leave the core functions call the poll routine and flag POLLIN/POLLOUT - * - * For pipe pollable synchronous I/O (read end polling only), you would: - * - create an anonymous pipe with usbi_pipe to obtain 2 fds (r & w) - * - use usbi_write / usbi_read to write to either end of the pipe - * - use poll to check for data to read - * Note that the usbi_read/usbi_write function actually perform - * asynchronous I/O internally, and could potentially be modified to support - * O_NON_BLOCK - * - * The way the polling on usbi_read works is by splitting all read I/O - * into a dual 1 byte/n-1 bytes asynchronous read operation. - * The 1 byte data (called the marker), is always armed for asynchronous - * readout, so that as soon as data becomes available, an OVERLAPPED event - * will be flagged, which poll can report. - * Then during the usbi_read routine itself, this 1 byte marker is copied - * to the buffer, along with the rest of the data. - * - * Note that, since most I/O is buffered, being notified when only the first - * byte of data is available is unlikely to delay read operations, since the - * rest of the data should be available in system buffers by the time read - * is called. - * - * Also note that if you don't use usbi_read to read inbound data, but - * use the OVERLAPPED directly (which is what we do in the USB async I/O - * functions), the marker is not used at all. - */ -#include -#include -#include -#include -#include - -#include - -// Uncomment to debug the polling layer -//#define DEBUG_WINDOWS_COMPAT -#if defined(DEBUG_WINDOWS_COMPAT) -#define printb printf -#else -// MSVC6 cannot use a variadic argument and non MSVC -// compilers produce warnings if parenthesis are ommitted. -#if defined(_MSC_VER) -#define printb -#else -#define printb(...) -#endif -#endif - -#if defined(_PREFAST_) -#pragma warning(disable:28719) -#endif - -#if defined(__CYGWIN__) -// cygwin produces a warning unless these prototypes are defined -extern int _close(int fd); -extern int _snprintf(char *buffer, size_t count, const char *format, ...); -extern int cygwin_attach_handle_to_fd(char *name, int fd, HANDLE handle, int bin, int access); -// _open_osfhandle() is not available on cygwin, but we can emulate -// it for our needs with cygwin_attach_handle_to_fd() -static inline int _open_osfhandle(intptr_t osfhandle, int flags) -{ - int access; - switch (flags) { - case _O_RDONLY: - access = GENERIC_READ; - break; - case _O_WRONLY: - access = GENERIC_WRITE; - break; - case _O_RDWR: - access = GENERIC_READ|GENERIC_WRITE; - break; - default: - printb("_open_osfhandle (emulated): unuspported access mode\n"); - return -1; - } - return cygwin_attach_handle_to_fd("/dev/null", -1, (HANDLE)osfhandle, -1, access); -} -#endif - -#define CHECK_INIT_POLLING do {if(!is_polling_set) init_polling();} while(0) - -// public fd data -const struct winfd INVALID_WINFD = {-1, NULL, NULL, RW_NONE, FALSE}; -struct winfd poll_fd[MAX_FDS]; -// internal fd data -struct { - CRITICAL_SECTION mutex; // lock for fds - BYTE marker; // 1st byte of a usbi_read operation gets stored here - -} _poll_fd[MAX_FDS]; - -// globals -BOOLEAN is_polling_set = FALSE; -LONG pipe_number = 0; -static volatile LONG compat_spinlock = 0; - -// 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; -__inline BOOL cancel_io(int index) -{ - if ((index < 0) || (index >= MAX_FDS)) { - return FALSE; - } - if (pCancelIoEx != NULL) { - return (*pCancelIoEx)(poll_fd[index].handle, poll_fd[index].overlapped); - } else { - return CancelIo(poll_fd[index].handle); - } -} - -// Init -void init_polling(void) -{ - int i; - - while (InterlockedExchange((LONG *)&compat_spinlock, 1) == 1) { - SleepEx(0, TRUE); - } - if (!is_polling_set) { - pCancelIoEx = (BOOL (__stdcall *)(HANDLE,LPOVERLAPPED)) - GetProcAddress(GetModuleHandle("KERNEL32"), "CancelIoEx"); - printb("init_polling: Will use CancelIo%s for I/O cancellation\n", - (pCancelIoEx != NULL)?"Ex":""); - for (i=0; ihEvent = CreateEvent(NULL, TRUE, FALSE, NULL); - if(overlapped->hEvent == NULL) { - free (overlapped); - return NULL; - } - return overlapped; -} - -void free_overlapped(OVERLAPPED *overlapped) -{ - if (overlapped == NULL) - return; - - if ( (overlapped->hEvent != 0) - && (overlapped->hEvent != INVALID_HANDLE_VALUE) ) { - CloseHandle(overlapped->hEvent); - } - free(overlapped); -} - -void reset_overlapped(OVERLAPPED *overlapped) -{ - HANDLE event_handle; - if (overlapped == NULL) - return; - - event_handle = overlapped->hEvent; - if (event_handle != NULL) { - ResetEvent(event_handle); - } - memset(overlapped, 0, sizeof(OVERLAPPED)); - overlapped->hEvent = event_handle; -} - -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 0) && (poll_fd[i].handle != INVALID_HANDLE_VALUE) && (poll_fd[i].handle != 0) - && (GetFileType(poll_fd[i].handle) == FILE_TYPE_UNKNOWN) ) { - _close(poll_fd[i].fd); - } - free_overlapped(poll_fd[i].overlapped); - poll_fd[i] = INVALID_WINFD; - LeaveCriticalSection(&_poll_fd[i].mutex); - DeleteCriticalSection(&_poll_fd[i].mutex); - } - } - compat_spinlock = 0; -} - -/* - * sets the async I/O read on our 1 byte marker - * - * requires a valid index - */ -__inline void _init_read_marker(int index) -{ - // Cancel any read operation in progress - cancel_io(index); - // Setup a new async read on our marker - reset_overlapped(poll_fd[index].overlapped); - if (!ReadFile(poll_fd[index].handle, &_poll_fd[index].marker, 1, NULL, poll_fd[index].overlapped)) { - if(GetLastError() != ERROR_IO_PENDING) { - printb("_init_read_marker: didn't get IO_PENDING!\n"); - reset_overlapped(poll_fd[index].overlapped); - } - } else { - // We got some sync I/O. We'll pretend it's async and set overlapped manually - printb("_init_read_marker: marker readout completed before exit!\n"); - if (!HasOverlappedIoCompleted(poll_fd[index].overlapped)) { - printb("_init_read_marker: completed I/O still flagged as pending\n"); - poll_fd[index].overlapped->Internal = 0; - } - SetEvent(poll_fd[index].overlapped->hEvent); - poll_fd[index].overlapped->InternalHigh = 1; - } -} - -/* - * Create an async I/O anonymous pipe (that can be used for sync as well) - */ -int usbi_pipe(int filedes[2]) -{ - int i, j; - HANDLE handle[2]; - OVERLAPPED *overlapped0, *overlapped1; - char pipe_name[] = "\\\\.\\pipe\\libusb000000000000"; - LONG our_pipe_number; - - CHECK_INIT_POLLING; - - overlapped0 = calloc(1, sizeof(OVERLAPPED)); - if (overlapped0 == NULL) { - return -1; - } - - overlapped1 = calloc(1, sizeof(OVERLAPPED)); - if (overlapped1 == NULL) { - free(overlapped0); - return -1; - } - - our_pipe_number = InterlockedIncrement(&pipe_number) - 1; // - 1 to mirror postfix operation inside _snprintf - if (our_pipe_number >= 0x10000) { - fprintf(stderr, "usbi_pipe: program assertion failed - more than 65536 pipes were used"); - our_pipe_number &= 0xFFFF; - } - _snprintf(pipe_name, sizeof(pipe_name), "\\\\.\\pipe\\libusb%08x%04x", (unsigned)GetCurrentProcessId(), our_pipe_number); - - // Read end of the pipe - handle[0] = CreateNamedPipeA(pipe_name, PIPE_ACCESS_INBOUND|FILE_FLAG_OVERLAPPED, - PIPE_TYPE_MESSAGE|PIPE_READMODE_MESSAGE, 1, 4096, 4096, 0, NULL); - if (handle[0] == INVALID_HANDLE_VALUE) { - printb("Could not create pipe (read end): errcode %d\n", (int)GetLastError()); - goto out1; - } - filedes[0] = _open_osfhandle((intptr_t)handle[0], _O_RDONLY); - printb("filedes[0] = %d\n", filedes[0]); - - // Write end of the pipe - handle[1] = CreateFileA(pipe_name, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED, NULL); - if (handle[1] == INVALID_HANDLE_VALUE) { - printb("Could not create pipe (write end): errcode %d\n", (int)GetLastError()); - goto out2; - } - filedes[1] = _open_osfhandle((intptr_t)handle[1], _O_WRONLY); - printb("filedes[1] = %d\n", filedes[1]); - - // Create an OVERLAPPED for each end - overlapped0->hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); - if(!overlapped0->hEvent) { - goto out3; - } - overlapped1->hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); - if(!overlapped1->hEvent) { - goto out4; - } - - for (i=0, j=0; i= 0) { - LeaveCriticalSection(&_poll_fd[i].mutex); - continue; - } - - poll_fd[i].fd = filedes[j]; - poll_fd[i].handle = handle[j]; - poll_fd[i].overlapped = (j==0)?overlapped0:overlapped1; - poll_fd[i].rw = RW_READ+j; - poll_fd[i].completed_synchronously = FALSE; - j++; - if (j==1) { - // Start a 1 byte nonblocking read operation - // so that we get read event notifications - _init_read_marker(i); - } - LeaveCriticalSection(&_poll_fd[i].mutex); - if (j>=2) { - return 0; - } - } - } - - CloseHandle(overlapped1->hEvent); -out4: - CloseHandle(overlapped0->hEvent); -out3: - CloseHandle(handle[1]); -out2: - CloseHandle(handle[0]); -out1: - free(overlapped1); - free(overlapped0); - return -1; -} - -/* - * Create both an fd and an OVERLAPPED from an open Windows handle, 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) - * 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) -{ - int i, fd; - struct winfd wfd = INVALID_WINFD; - OVERLAPPED* overlapped = NULL; - - CHECK_INIT_POLLING; - - if ((handle == 0) || (handle == INVALID_HANDLE_VALUE)) { - return INVALID_WINFD; - } - - if ((access_mode != _O_RDONLY) && (access_mode != _O_WRONLY)) { - printb("usbi_create_fd: only one of _O_RDONLY or _O_WRONLY are supported.\n" - "If you want to poll for R/W simultaneously, create multiple fds from the same handle.\n"); - return INVALID_WINFD; - } - if (access_mode == _O_RDONLY) { - wfd.rw = RW_READ; - } else { - wfd.rw = RW_WRITE; - } - - // Ensure that we get a non system conflicting unique fd - fd = _open_osfhandle((intptr_t)CreateFileA("NUL", 0, 0, - NULL, OPEN_EXISTING, 0, NULL), _O_RDWR); - if (fd < 0) { - return INVALID_WINFD; - } - - overlapped = create_overlapped(); - if(overlapped == NULL) { - _close(fd); - return INVALID_WINFD; - } - - for (i=0; i= 0) { - LeaveCriticalSection(&_poll_fd[i].mutex); - continue; - } - wfd.fd = fd; - wfd.handle = handle; - wfd.overlapped = overlapped; - memcpy(&poll_fd[i], &wfd, sizeof(struct winfd)); - LeaveCriticalSection(&_poll_fd[i].mutex); - return wfd; - } - } - free_overlapped(overlapped); - _close(fd); - return INVALID_WINFD; -} - -void _free_index(int index) -{ - // Cancel any async IO (Don't care about the validity of our handles for this) - cancel_io(index); - // close fake handle for devices - if ( (poll_fd[index].handle != INVALID_HANDLE_VALUE) && (poll_fd[index].handle != 0) - && (GetFileType(poll_fd[index].handle) == FILE_TYPE_UNKNOWN) ) { - _close(poll_fd[index].fd); - } - 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(int fd) -{ - int index; - - CHECK_INIT_POLLING; - - index = _fd_to_index_and_lock(fd); - if (index < 0) { - return; - } - _free_index(index); - 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 (i=0; i= 0) { - LeaveCriticalSection(&_poll_fd[index].mutex); - } - printb("usbi_poll: invalid fd\n"); - 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; - printb("usbi_poll: attempted POLLIN on fd[%d] without READ access\n", i); - 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; - printb("usbi_poll: attempted POLLOUT on fd[%d] without WRITE access\n", i); - LeaveCriticalSection(&_poll_fd[index].mutex); - triggered = -1; - goto poll_exit; - } - - printb("usbi_poll: fd[%d]=%d (overlapped = %p) got events %04X\n", i, poll_fd[index].fd, poll_fd[index].overlapped, fds[i].events); - - // The following macro only works if overlapped I/O was reported pending - if ( (HasOverlappedIoCompleted(poll_fd[index].overlapped)) - || (poll_fd[index].completed_synchronously) ) { - printb(" completed\n"); - // 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 nothing was triggered, wait on all fds that require it - if ((triggered == 0) && (nb_handles_to_wait_on != 0)) { - printb("usbi_poll: starting %d ms wait for %d handles...\n", timeout, (int)nb_handles_to_wait_on); - ret = WaitForMultipleObjects(nb_handles_to_wait_on, handles_to_wait_on, - FALSE, (timeout==-1)?INFINITE:(DWORD)timeout); - - object_index = ret-WAIT_OBJECT_0; - if ((object_index >= 0) && ((DWORD)object_index < nb_handles_to_wait_on)) { - printb(" completed after wait\n"); - 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) { - printb(" timed out\n"); - triggered = 0; // 0 = timeout - } else { - errno = EIO; - triggered = -1; // error - } - } - -poll_exit: - if (handles_to_wait_on != NULL) { - free(handles_to_wait_on); - } - if (handle_to_index != NULL) { - free(handle_to_index); - } - return triggered; -} - -/* - * close a pollable fd - * - * Note that this function will also close the associated handle - */ -int usbi_close(int fd) -{ - int index; - HANDLE handle; - int r = -1; - - CHECK_INIT_POLLING; - - index = _fd_to_index_and_lock(fd); - - if (index < 0) { - errno = EBADF; - } else { - handle = poll_fd[index].handle; - _free_index(index); - if (CloseHandle(handle) == 0) { - errno = EIO; - } else { - r = 0; - } - LeaveCriticalSection(&_poll_fd[index].mutex); - } - return r; -} - -/* - * synchronous write for custom poll (works on Windows file handles that - * have been opened with the FILE_FLAG_OVERLAPPED flag) - * - * Current restrictions: - * - binary mode only - * - no append mode - */ -ssize_t usbi_write(int fd, const void *buf, size_t count) -{ - int index; - DWORD wr_count; - int r = -1; - - CHECK_INIT_POLLING; - - index = _fd_to_index_and_lock(fd); - - if (count == 0) { - return 0; - } - - if ( (index < 0) || (poll_fd[index].overlapped == NULL) - || (poll_fd[index].rw != RW_WRITE) ) { - errno = EBADF; - if (index >= 0) { - LeaveCriticalSection(&_poll_fd[index].mutex); - } - return -1; - } - - // For sync mode, we shouldn't get pending async write I/O - if (!HasOverlappedIoCompleted(poll_fd[index].overlapped)) { - printb("usbi_write: previous write I/O was flagged pending!\n"); - cancel_io(index); - } - - printb("usbi_write: writing %d bytes to fd=%d\n", count, poll_fd[index].fd); - - reset_overlapped(poll_fd[index].overlapped); - if (!WriteFile(poll_fd[index].handle, buf, (DWORD)count, &wr_count, poll_fd[index].overlapped)) { - if(GetLastError() == ERROR_IO_PENDING) { - // I/O started but is not completed => wait till completion - switch(WaitForSingleObject(poll_fd[index].overlapped->hEvent, INFINITE)) - { - case WAIT_OBJECT_0: - if (GetOverlappedResult(poll_fd[index].handle, - poll_fd[index].overlapped, &wr_count, FALSE)) { - r = 0; - goto out; - } else { - printb("usbi_write: GetOverlappedResult failed with error %d\n", (int)GetLastError()); - errno = EIO; - goto out; - } - default: - errno = EIO; - goto out; - } - } else { - // I/O started and failed - printb("usbi_write: WriteFile failed with error %d\n", (int)GetLastError()); - errno = EIO; - goto out; - } - } - - // I/O started and completed synchronously - r = 0; - -out: - if (r) { - reset_overlapped(poll_fd[index].overlapped); - LeaveCriticalSection(&_poll_fd[index].mutex); - return -1; - } else { - LeaveCriticalSection(&_poll_fd[index].mutex); - return (ssize_t)wr_count; - } -} - -/* - * synchronous read for custom poll (works on Windows file handles that - * have been opened with the FILE_FLAG_OVERLAPPED flag) - */ -ssize_t usbi_read(int fd, void *buf, size_t count) -{ - int index; - DWORD rd_count; - int r = -1; - - CHECK_INIT_POLLING; - - if (count == 0) { - return 0; - } - - index = _fd_to_index_and_lock(fd); - - if (index < 0) { - errno = EBADF; - return -1; - } - - if (poll_fd[index].rw != RW_READ) { - errno = EBADF; - goto out; - } - - - // still waiting for completion => force completion - if (!HasOverlappedIoCompleted(poll_fd[index].overlapped)) { - if (WaitForSingleObject(poll_fd[index].overlapped->hEvent, INFINITE) != WAIT_OBJECT_0) { - printb("usbi_read: waiting for marker failed: %d\n", (int)GetLastError()); - errno = EIO; - goto out; - } - } - - // Find out if we've read the first byte - if (!GetOverlappedResult(poll_fd[index].handle, poll_fd[index].overlapped, &rd_count, FALSE)) { - if (GetLastError() != ERROR_MORE_DATA) { - printb("usbi_read: readout of marker failed: %d\n", (int)GetLastError()); - errno = EIO; - goto out; - } else { - printb("usbi_read: readout of marker reported more data\n"); - } - } - - printb("usbi_read: count = %d, rd_count(marker) = %d\n", count, (int)rd_count); - - // We should have our marker by now - if (rd_count != 1) { - printb("usbi_read: unexpected number of bytes for marker (%d)\n", (int)rd_count); - errno = EIO; - goto out; - } - - ((BYTE*)buf)[0] = _poll_fd[index].marker; - - // Read supplementary bytes if needed (blocking) - if (count > 1) { - reset_overlapped(poll_fd[index].overlapped); - if (!ReadFile(poll_fd[index].handle, (char*)buf+1, (DWORD)(count-1), &rd_count, poll_fd[index].overlapped)) { - if(GetLastError() == ERROR_IO_PENDING) { - if (!GetOverlappedResult(poll_fd[index].handle, poll_fd[index].overlapped, &rd_count, TRUE)) { - if (GetLastError() == ERROR_MORE_DATA) { - printb("usbi_read: could not fetch all data\n"); - } - printb("usbi_read: readout of supplementary data failed: %d\n", (int)GetLastError()); - errno = EIO; - goto out; - } - } else { - printb("usbi_read: could not start blocking read of supplementary: %d\n", (int)GetLastError()); - errno = EIO; - goto out; - } - } - // If ReadFile completed synchronously, we're fine too - - printb("usbi_read: rd_count(supplementary ) = %d\n", (int)rd_count); - - if ((rd_count+1) != count) { - printb("usbi_read: wanted %d-1, got %d\n", count, (int)rd_count); - errno = EIO; - goto out; - } - } - - r = 0; - -out: - // Setup pending read I/O for the marker - _init_read_marker(index); - LeaveCriticalSection(&_poll_fd[index].mutex); - if (r) - return -1; - else - return count; -} diff --git a/libusb/os/windows_compat.h b/libusb/os/windows_compat.h deleted file mode 100644 index 699f167..0000000 --- a/libusb/os/windows_compat.h +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Windows compat: POSIX compatibility wrapper - * Copyright (C) 2009-2010 Pete Batard - * With contributions from Michael Plante, Orin Eman et al. - * Parts of poll implementation from libusb-win32, by Stephan Meyer et al. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ -#pragma once - -#if defined(_MSC_VER) -// disable /W4 MSVC warnings that are benign -#pragma warning(disable:4127) // conditional expression is constant -#endif - -#if !defined(ssize_t) -#if defined (_WIN64) -#define ssize_t __int64 -#else -#define ssize_t long -#endif -#endif - -enum windows_version { - WINDOWS_UNSUPPORTED, - WINDOWS_XP, - WINDOWS_VISTA_AND_LATER, -}; -extern enum windows_version windows_version; - -#define MAX_FDS 256 - -#if !defined(__CYGWIN__) -#define POLLIN 0x0001 /* There is data to read */ -#define POLLPRI 0x0002 /* There is urgent data to read */ -#define POLLOUT 0x0004 /* Writing now will not block */ -#define POLLERR 0x0008 /* Error condition */ -#define POLLHUP 0x0010 /* Hung up */ -#define POLLNVAL 0x0020 /* Invalid request: fd not open */ - -struct pollfd { - int fd; /* file descriptor */ - short events; /* requested events */ - short revents; /* returned events */ -}; -#endif -typedef unsigned int nfds_t; - -// access modes -enum rw_type { - RW_NONE, - RW_READ, - RW_WRITE, -}; - -// fd struct that can be used for polling on Windows -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 - enum rw_type rw; // I/O transfer direction: read *XOR* write (NOT BOTH) - BOOLEAN completed_synchronously;// flag for async transfers that completed during request -}; -extern const struct winfd INVALID_WINFD; - -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); -void usbi_free_fd(int fd); -struct winfd fd_to_winfd(int fd); -struct winfd handle_to_winfd(HANDLE handle); -struct winfd overlapped_to_winfd(OVERLAPPED* overlapped); - -// When building using the MSDDK and sources -#if defined(DDKBUILD) -#if !defined(timeval) -struct timeval { - long tv_sec; /* seconds */ - long tv_usec; /* and microseconds */ -}; -#endif - -#if !defined(timerisset) -#define timerisset(tvp) ((tvp)->tv_sec || (tvp)->tv_usec) -#endif - -#if !defined(timercmp) -#define timercmp(tvp, uvp, cmp) \ - ((tvp)->tv_sec cmp (uvp)->tv_sec || \ - (tvp)->tv_sec == (uvp)->tv_sec && (tvp)->tv_usec cmp (uvp)->tv_usec) -#endif - -#if !defined(timerclr) -#define timerclear(tvp) (tvp)->tv_sec = (tvp)->tv_usec = 0 -#endif -#endif - -#if !defined(TIMESPEC_TO_TIMEVAL) -#define TIMESPEC_TO_TIMEVAL(tv, ts) { \ - (tv)->tv_sec = (long)(ts)->tv_sec; \ - (tv)->tv_usec = (long)(ts)->tv_nsec / 1000; \ -} -#endif -#if !defined(timersub) -#define timersub(a, b, result) \ -do { \ - (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ - (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \ - if ((result)->tv_usec < 0) { \ - --(result)->tv_sec; \ - (result)->tv_usec += 1000000; \ - } \ -} while (0) -#endif - diff --git a/libusb/os/windows_usb.c b/libusb/os/windows_usb.c index 610a91d..62fb871 100644 --- a/libusb/os/windows_usb.c +++ b/libusb/os/windows_usb.c @@ -47,7 +47,7 @@ #include // for string to GUID conv. requires libole32.a #include -#include "windows_compat.h" +#include "poll_windows.h" #include "windows_usb.h" #if defined(_PREFAST_) @@ -124,7 +124,7 @@ const GUID CLASS_GUID_COMPOSITE = { 0x36FC9E60, 0xC465, 0x11cF, {0x80, 0x56, // Global variables struct windows_hcd_priv* hcd_root = NULL; uint64_t hires_frequency, hires_ticks_to_ps; -const uint64_t epoch_time = 116444736000000000; // 1970.01.01 00:00:000 in MS Filetime +const uint64_t epoch_time = UINT64_C(116444736000000000); // 1970.01.01 00:00:000 in MS Filetime enum windows_version windows_version = WINDOWS_UNSUPPORTED; // Concurrency static int concurrent_usage = -1; @@ -248,6 +248,20 @@ char* sanitize_path(const char* path) return ret_path; } +/* + * Cfgmgr32 API functions + */ +static int Cfgmgr32_init(void) +{ + DLL_LOAD(Cfgmgr32.dll, CM_Get_Parent, TRUE); + DLL_LOAD(Cfgmgr32.dll, CM_Get_Child, TRUE); + DLL_LOAD(Cfgmgr32.dll, CM_Get_Sibling, TRUE); + DLL_LOAD(Cfgmgr32.dll, CM_Get_Device_IDA, TRUE); + DLL_LOAD(Cfgmgr32.dll, CM_Get_Device_IDW, TRUE); + + return LIBUSB_SUCCESS; +} + /* * enumerate interfaces for a specific GUID * @@ -429,6 +443,12 @@ static int windows_init(struct libusb_context *ctx) // Initialize pollable file descriptors init_polling(); + // Load missing CFGMGR32.DLL imports + if (Cfgmgr32_init() != LIBUSB_SUCCESS) { + usbi_err(ctx, "could not resolve Cfgmgr32.dll functions"); + return LIBUSB_ERROR_OTHER; + } + // Initialize the low level APIs for (i=0; idev_descriptor.bLength = sizeof(USB_DEVICE_DESCRIPTOR); priv->dev_descriptor.bDescriptorType = USB_DEVICE_DESCRIPTOR_TYPE; dev->num_configurations = priv->dev_descriptor.bNumConfigurations = 1; - priv->dev_descriptor.idVendor = 0x1d6b; // Linux Foundation root hub + // The following is used to set the VIS:PID of root HUBs similarly to what + // Linux does: 1d6b:0001 is for 1x root hubs, 1d6b:0002 for 2x + priv->dev_descriptor.idVendor = 0x1d6b; // Linux Foundation root hub if (windows_version >= WINDOWS_VISTA_AND_LATER) { size = sizeof(USB_HUB_CAPABILITIES_EX); if (DeviceIoControl(handle, IOCTL_USB_GET_HUB_CAPABILITIES_EX, &hub_caps_ex, @@ -608,9 +630,10 @@ static int force_hcd_device_descriptor(struct libusb_device *dev, HANDLE handle) size, &hub_caps, size, &size, NULL)) { usbi_warn(ctx, "could not read hub capabilities (std) for hub %s: %s", priv->path, windows_error_str(0)); - return LIBUSB_ERROR_IO; + priv->dev_descriptor.idProduct = 1; // Indicate 1x speed + } else { + priv->dev_descriptor.idProduct = hub_caps.HubIs2xCapable?2:1; } - priv->dev_descriptor.idProduct = hub_caps.HubIs2xCapable?2:1; } return LIBUSB_SUCCESS; @@ -735,7 +758,7 @@ static int usb_enumerate_hub(struct libusb_context *ctx, struct discovered_devs USB_HUB_NAME_FIXED s_hubname; USB_NODE_CONNECTION_INFORMATION conn_info; USB_NODE_INFORMATION hub_node; - bool is_hcd; + bool is_hcd, need_unref = false; int i, r; LPCWSTR wstr; char *tmp_str = NULL, *path_str = NULL; @@ -768,7 +791,10 @@ static int usb_enumerate_hub(struct libusb_context *ctx, struct discovered_devs for (i = 1, r = LIBUSB_SUCCESS; ; i++) { // safe loop: release all dynamic resources - safe_unref_device(dev); + if (need_unref) { + safe_unref_device(dev); + need_unref = false; + } safe_free(tmp_str); safe_free(path_str); safe_closehandle(handle); @@ -849,7 +875,7 @@ static int usb_enumerate_hub(struct libusb_context *ctx, struct discovered_devs // Open Hub handle = CreateFileA(path_str, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, - FILE_FLAG_POSIX_SEMANTICS|FILE_FLAG_OVERLAPPED, NULL); + FILE_FLAG_OVERLAPPED, NULL); if(handle == INVALID_HANDLE_VALUE) { usbi_warn(ctx, "could not open hub %s: %s", path_str, windows_error_str(0)); continue; @@ -882,6 +908,7 @@ static int usb_enumerate_hub(struct libusb_context *ctx, struct discovered_devs if ((dev = usbi_alloc_device(ctx, session_id)) == NULL) { LOOP_BREAK(LIBUSB_ERROR_NO_MEM); } + need_unref = true; LOOP_CHECK(initialize_device(dev, busnum, devaddr, path_str, i, conn_info.CurrentConfigurationValue, parent_dev)); @@ -1427,7 +1454,7 @@ static int windows_get_device_list(struct libusb_context *ctx, struct discovered } handle = CreateFileA(hcd->path, GENERIC_WRITE, FILE_SHARE_WRITE, - NULL, OPEN_EXISTING, FILE_FLAG_POSIX_SEMANTICS|FILE_FLAG_OVERLAPPED, NULL); + NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); if (handle == INVALID_HANDLE_VALUE) { usbi_warn(ctx, "could not open bus %u, skipping: %s", bus, windows_error_str(0)); continue; @@ -1931,12 +1958,12 @@ unsigned __stdcall windows_clock_gettime_threaded(void* param) if (!QueryPerformanceFrequency(&li_frequency)) { usbi_dbg("no hires timer available on this platform"); hires_frequency = 0; - hires_ticks_to_ps = 0; + hires_ticks_to_ps = UINT64_C(0); } else { hires_frequency = li_frequency.QuadPart; // The hires frequency can go as high as 4 GHz, so we'll use a conversion // to picoseconds to compute the tv_nsecs part in clock_gettime - hires_ticks_to_ps = 1000000000000 / hires_frequency; + hires_ticks_to_ps = UINT64_C(1000000000000) / hires_frequency; usbi_dbg("hires timer available (Frequency: %"PRIu64" Hz)", hires_frequency); } @@ -2516,7 +2543,7 @@ static int winusb_submit_control_transfer(struct usbi_transfer *itransfer) usbi_dbg("will use interface %d", current_interface); winusb_handle = handle_priv->interface_handle[current_interface].api_handle; - wfd = usbi_create_fd(winusb_handle, _O_RDONLY); + wfd = usbi_create_fd(winusb_handle, _O_RDONLY, ctx); if (wfd.fd < 0) { return LIBUSB_ERROR_NO_MEM; } @@ -2592,7 +2619,7 @@ static int winusb_submit_bulk_transfer(struct usbi_transfer *itransfer) winusb_handle = handle_priv->interface_handle[current_interface].api_handle; direction_in = transfer->endpoint & LIBUSB_ENDPOINT_IN; - wfd = usbi_create_fd(winusb_handle, direction_in?_O_RDONLY:_O_WRONLY); + wfd = usbi_create_fd(winusb_handle, direction_in?_O_RDONLY:_O_WRONLY, ctx); if (wfd.fd < 0) { return LIBUSB_ERROR_NO_MEM; } @@ -3174,9 +3201,11 @@ static int _hid_get_feature(struct hid_device_priv* dev, HANDLE hid_handle, int switch (err) { case ERROR_INVALID_FUNCTION: r = LIBUSB_ERROR_NOT_FOUND; + break; default: - usbi_dbg("error %s", windows_error_str(r)); + usbi_dbg("error %s", windows_error_str(err)); r = LIBUSB_ERROR_OTHER; + break; } } safe_free(buf); @@ -3554,7 +3583,7 @@ static int hid_submit_control_transfer(struct usbi_transfer *itransfer) usbi_dbg("will use interface %d", current_interface); hid_handle = handle_priv->interface_handle[current_interface].api_handle; - wfd = usbi_create_fd(hid_handle, _O_RDONLY); + wfd = usbi_create_fd(hid_handle, _O_RDONLY, ctx); if (wfd.fd < 0) { return LIBUSB_ERROR_NO_MEM; } @@ -3656,7 +3685,7 @@ static int hid_submit_bulk_transfer(struct usbi_transfer *itransfer) { 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?_O_RDONLY:_O_WRONLY); + wfd = usbi_create_fd(hid_handle, direction_in?_O_RDONLY:_O_WRONLY, ctx); if (wfd.fd < 0) { return LIBUSB_ERROR_NO_MEM; } diff --git a/libusb/os/windows_usb.h b/libusb/os/windows_usb.h index e5c5c79..30560b5 100644 --- a/libusb/os/windows_usb.h +++ b/libusb/os/windows_usb.h @@ -84,8 +84,6 @@ inline void upperize(char* str) { for (i=0; i