Add Windows support
authorPete Batard <pbatard@gmail.com>
Sat, 10 Jul 2010 23:51:13 +0000 (17:51 -0600)
committerDaniel Drake <dan@reactivated.net>
Wed, 28 Jul 2010 02:57:20 +0000 (20:57 -0600)
Via Cygwin/MinGW, libusb now has windows support.
Thanks to contributors: Michael Plante, Orin Eman, Peter Stuge,
Stephan Meyer, Xiaofan Chen.

15 files changed:
AUTHORS
configure.ac
libusb/Makefile.am
libusb/core.c
libusb/io.c
libusb/libusb-1.0.rc.in [new file with mode: 0644]
libusb/libusb.h
libusb/libusbi.h
libusb/os/poll_windows.c [new file with mode: 0644]
libusb/os/poll_windows.h [new file with mode: 0644]
libusb/os/threads_posix.h
libusb/os/threads_windows.c [new file with mode: 0644]
libusb/os/threads_windows.h [new file with mode: 0644]
libusb/os/windows_usb.c [new file with mode: 0644]
libusb/os/windows_usb.h [new file with mode: 0644]

diff --git a/AUTHORS b/AUTHORS
index 8f21c07..6ee0d45 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -1,6 +1,8 @@
 Copyright (C) 2007-2009 Daniel Drake <dsd@gentoo.org>
 Copyright (c) 2001 Johannes Erdfelt <johannes@erdfelt.com>
 Copyright (C) 2008-2010 Nathan Hjelm <hjelmn@users.sourceforge.net>
+Copyright (C) 2009-2010 Pete Batard <pbatard@gmail.com>
+Copyright (C) 2010 Michael Plante <michael.plante@gmail.com>
 
 Other contributors:
 Alex Vatchenko
@@ -14,9 +16,7 @@ Francesco Montorsi
 Hans Ulrich Niedermann
 Ludovic Rousseau
 Martin Koegler
-Michael Plante
 Mikhail Gusarov
-Pete Batard
 Peter Stuge
 Rob Walker
 Toby Peterson
index 36f364c..122ffca 100644 (file)
@@ -1,4 +1,13 @@
-AC_INIT([libusb], [1.0.8])
+m4_define(LIBUSB_MAJOR, [1])
+m4_define(LIBUSB_MINOR, [0])
+m4_define(LIBUSB_MICRO, [8])
+
+AC_INIT([libusb], LIBUSB_MAJOR.LIBUSB_MINOR.LIBUSB_MICRO, [libusb-devel@lists.sourceforge.net], [libusb], [http://www.libusb.org/])
+
+AC_SUBST([LIBUSB_VERSION_MAJOR], [LIBUSB_MAJOR])
+AC_SUBST([LIBUSB_VERSION_MINOR], [LIBUSB_MINOR])
+AC_SUBST([LIBUSB_VERSION_MICRO], [LIBUSB_MICRO])
+
 AM_INIT_AUTOMAKE
 AC_CONFIG_SRCDIR([libusb/core.c])
 AC_CONFIG_MACRO_DIR([m4])
@@ -19,6 +28,7 @@ case $host in
 *-linux*)
        AC_DEFINE(OS_LINUX, [], [Linux backend])
        AC_SUBST(OS_LINUX)
+       AC_DEFINE([THREADS_POSIX], [], [Use Posix Threads])     
        AC_MSG_RESULT([Linux])
        backend="linux"
        AC_CHECK_LIB(rt, clock_gettime)
@@ -29,6 +39,7 @@ case $host in
 *-darwin*)
        AC_DEFINE(OS_DARWIN, [], [Darwin backend])
        AC_SUBST(OS_DARWIN)
+       AC_DEFINE([THREADS_POSIX], [], [Use Posix Threads])
        AC_DEFINE(USBI_OS_HANDLES_TIMEOUT, [], [Backend handles timeout])
        AC_MSG_RESULT([Darwin/MacOS X])
        backend="darwin"
@@ -36,12 +47,35 @@ case $host in
        THREAD_CFLAGS="-pthread"
        AM_LDFLAGS="-Wl,-framework -Wl,IOKit -Wl,-framework -Wl,CoreFoundation -Wl,-prebind -no-undefined"
        ;;
+*-mingw*)
+       AC_DEFINE(OS_WINDOWS, [], [Windows backend])
+       AC_SUBST(OS_WINDOWS)
+       AC_MSG_RESULT([Windows])
+       backend="windows"
+       threads="windows"
+       LIBS="-lsetupapi -lole32 -ladvapi32"
+       # -avoid-version to avoid a naming scheme such as libusb-0.dll
+       AM_LDFLAGS="-no-undefined -avoid-version"
+       AC_CHECK_TOOL(RC, windres, no)
+       ;;
+*-cygwin*)
+       AC_DEFINE(OS_WINDOWS, [], [Windows backend])
+       AC_SUBST(OS_WINDOWS)
+       AC_DEFINE([THREADS_POSIX], [], [Use Posix Threads])
+       AC_MSG_RESULT([Windows])
+       backend="windows"
+       threads="posix"
+       LIBS="-lsetupapi -lole32 -ladvapi32"
+       AM_LDFLAGS="-no-undefined -avoid-version"
+       AC_CHECK_TOOL(RC, windres, no)
+       ;;
 *)
        AC_MSG_ERROR([unsupported operating system])
 esac
 
 AM_CONDITIONAL([OS_LINUX], [test "x$backend" = "xlinux"])
 AM_CONDITIONAL([OS_DARWIN], [test "x$backend" = "xdarwin"])
+AM_CONDITIONAL([OS_WINDOWS], [test "x$backend" = "xwindows"])
 AM_CONDITIONAL([THREADS_POSIX], [test "x$threads" = "xposix"])
 
 # Library versioning
@@ -80,6 +114,8 @@ else
        fi
 fi
 
+AC_CHECK_TYPES(struct timespec)
+
 # Message logging
 AC_ARG_ENABLE([log], [AS_HELP_STRING([--disable-log], [disable all logging])],
        [log_enabled=$enableval],
@@ -111,7 +147,8 @@ CFLAGS="$saved_cflags"
 
 # check for -fvisibility=hidden compiler support (GCC >= 3.4)
 saved_cflags="$CFLAGS"
-CFLAGS="$CFLAGS -fvisibility=hidden"
+# -Werror required for cygwin
+CFLAGS="$CFLAGS -Werror -fvisibility=hidden"
 AC_COMPILE_IFELSE(AC_LANG_PROGRAM([]),
        [VISIBILITY_CFLAGS="-fvisibility=hidden"
         AC_DEFINE([API_EXPORTED], [__attribute__((visibility("default")))], [Default visibility]) ],
@@ -142,6 +179,6 @@ AC_SUBST(VISIBILITY_CFLAGS)
 AC_SUBST(AM_CFLAGS)
 AC_SUBST(AM_LDFLAGS)
 
-AC_CONFIG_FILES([libusb-1.0.pc] [Makefile] [libusb/Makefile] [examples/Makefile] [doc/Makefile] [doc/doxygen.cfg])
+AC_CONFIG_FILES([libusb-1.0.pc] [Makefile] [libusb/Makefile] [libusb/libusb-1.0.rc] [examples/Makefile] [doc/Makefile] [doc/doxygen.cfg])
 AC_OUTPUT
 
index e0f9f5a..879a3f7 100644 (file)
@@ -1,9 +1,11 @@
 lib_LTLIBRARIES = libusb-1.0.la
 
-LINUX_USBFS_SRC = os/linux_usbfs.h os/linux_usbfs.c
-DARWIN_USB_SRC = os/darwin_usb.h os/darwin_usb.c
+LINUX_USBFS_SRC = os/linux_usbfs.c
+DARWIN_USB_SRC = os/darwin_usb.c
+WINDOWS_USB_SRC = os/poll_windows.c os/windows_usb.c libusb-1.0.rc
 
-EXTRA_DIST = $(LINUX_USBFS_SRC) $(DARWIN_USB_SRC)
+EXTRA_DIST = $(LINUX_USBFS_SRC) $(DARWIN_USB_SRC) $(WINDOWS_USB_SRC) \
+       os/threads_windows.c
 
 if OS_LINUX
 OS_SRC = $(LINUX_USBFS_SRC)
@@ -14,8 +16,22 @@ OS_SRC = $(DARWIN_USB_SRC)
 AM_CFLAGS_EXT = -no-cpp-precomp
 endif
 
+if OS_WINDOWS
+OS_SRC = $(WINDOWS_USB_SRC)
+
+if !THREADS_POSIX
+OS_SRC += os/threads_windows.c
+endif
+
+.rc.lo:
+       $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(RC) $(RCFLAGS) -i $< -o $@
+endif
+
 libusb_1_0_la_CFLAGS = $(VISIBILITY_CFLAGS) $(AM_CFLAGS) $(THREAD_CFLAGS)
-libusb_1_0_la_SOURCES = libusbi.h core.c descriptor.c io.c sync.c os/threads_posix.h os/poll_posix.h $(OS_SRC)
+libusb_1_0_la_SOURCES = libusbi.h core.c descriptor.c io.c sync.c $(OS_SRC) \
+       os/linux_usbfs.h os/darwin_usb.h os/windows_usb.h \
+       os/threads_posix.h os/threads_windows.h \
+       os/poll_posix.h os/poll_windows.h
 
 hdrdir = $(includedir)/libusb-1.0
 hdr_HEADERS = libusb.h
index ac10266..d9f76c8 100644 (file)
 #include <string.h>
 #include <sys/types.h>
 
-#include "os/poll_posix.h"
-
 #include "libusbi.h"
 
 #if defined(OS_LINUX)
 const struct usbi_os_backend * const usbi_backend = &linux_usbfs_backend;
 #elif defined(OS_DARWIN)
 const struct usbi_os_backend * const usbi_backend = &darwin_backend;
+#elif defined(OS_WINDOWS)
+const struct usbi_os_backend * const usbi_backend = &windows_backend;
 #else
 #error "Unsupported OS"
 #endif
index c5cc69e..36f9244 100644 (file)
@@ -30,8 +30,6 @@
 #include <sys/time.h>
 #endif
 
-#include "os/poll_posix.h"
-
 #ifdef USBI_TIMERFD_AVAILABLE
 #include <sys/timerfd.h>
 #endif
diff --git a/libusb/libusb-1.0.rc.in b/libusb/libusb-1.0.rc.in
new file mode 100644 (file)
index 0000000..27db214
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * For Windows: input this file to the Resoure Compiler to produce a binary
+ * .res file. This is then embedded in the resultant library (like any other
+ * compilation object).
+ * The information can then be queried using standard APIs and can also be
+ * viewed with utilities such as Windows Explorer.
+ */
+#include "winresrc.h"
+
+#ifdef _DEBUG
+#define VER_ORIGINALFILENAME_STR "libusb-1.0_debug.dll\0"
+#else
+#define VER_ORIGINALFILENAME_STR "libusb-1.0.dll\0"
+#endif
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION @LIBUSB_VERSION_MAJOR@,@LIBUSB_VERSION_MINOR@,@LIBUSB_VERSION_MICRO@,0
+ PRODUCTVERSION @LIBUSB_VERSION_MAJOR@,@LIBUSB_VERSION_MINOR@,@LIBUSB_VERSION_MICRO@,0
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+       BLOCK "StringFileInfo"
+       BEGIN
+               BLOCK "040904b0"
+               BEGIN
+                       VALUE "Comments", "\0"
+                       VALUE "CompanyName", "libusb.org\0"
+                       VALUE "FileDescription", "libusb-1\0"
+                       VALUE "FileVersion", "@PACKAGE_VERSION@"
+                       VALUE "InternalName", "libusb\0"
+                       VALUE "LegalCopyright", "See individual source files, GNU LGPL v2.1 or later.\0"
+                       VALUE "LegalTrademarks", "http://www.gnu.org/licenses/lgpl-2.1.html\0"
+                       VALUE "OriginalFilename", VER_ORIGINALFILENAME_STR
+                       VALUE "PrivateBuild", "\0"
+                       VALUE "ProductName", "libusb-1\0"
+                       VALUE "ProductVersion", "@PACKAGE_VERSION@"
+                       VALUE "SpecialBuild", "\0"
+               END
+       END
+       BLOCK "VarFileInfo"
+       BEGIN
+               VALUE "Translation", 0x409, 1200
+       END
+END
index 9f298d3..5c88d73 100644 (file)
 #ifndef __LIBUSB_H__
 #define __LIBUSB_H__
 
+/* MSVC doesn't like inline, but does accept __inline ?? */
+#ifdef _MSC_VER
+#define inline __inline
+#endif
+
 #include <stdint.h>
 #include <sys/types.h>
 #include <time.h>
 #include <limits.h>
 
-#if defined(__linux) || defined(__APPLE__)
+#if defined(__linux) || defined(__APPLE__) || defined(__CYGWIN__)
 #include <sys/time.h>
 #endif
 
+/* '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
+ * libusb_config_descriptor has an 'interface' member
+ * As this can be problematic if you include windows.h after libusb.h
+ * in your sources, we force windows.h to be included first. */
+#if defined(_WIN32) || defined(__CYGWIN__)
+#include <windows.h>
+#if defined(interface)
+#undef interface
+#endif
+#endif
+
 #ifdef __cplusplus
 extern "C" {
 #endif
index 1e9b69f..464caa1 100644 (file)
@@ -23,7 +23,6 @@
 
 #include <config.h>
 
-#include <poll.h>
 #include <stddef.h>
 #include <stdint.h>
 #include <time.h>
@@ -177,9 +176,17 @@ static inline void usbi_dbg(const char *format, ...)
 #define ITRANSFER_CTX(transfer) \
        (TRANSFER_CTX(__USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer)))
 
-/* Internal abstraction for thread synchronization */
-#if defined(OS_LINUX) || defined(OS_DARWIN)
+/* Internal abstractions for thread synchronization and poll */
+#if defined(THREADS_POSIX)
 #include <os/threads_posix.h>
+#elif defined(OS_WINDOWS)
+#include <os/threads_windows.h>
+#endif
+
+#if defined(OS_LINUX) || defined(OS_DARWIN)
+#include <os/poll_posix.h>
+#elif defined(OS_WINDOWS)
+#include <os/poll_windows.h>
 #endif
 
 extern struct libusb_context *usbi_default_context;
@@ -850,6 +857,7 @@ extern const struct usbi_os_backend * const usbi_backend;
 
 extern const struct usbi_os_backend linux_usbfs_backend;
 extern const struct usbi_os_backend darwin_backend;
+extern const struct usbi_os_backend windows_backend;
 
 #endif
 
diff --git a/libusb/os/poll_windows.c b/libusb/os/poll_windows.c
new file mode 100644 (file)
index 0000000..59c3b07
--- /dev/null
@@ -0,0 +1,860 @@
+/*
+ * poll_windows: poll compatibility wrapper for Windows
+ * Copyright (C) 2009-2010 Pete Batard <pbatard@gmail.com>
+ * 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
+ *
+ * The pipe pollable synchronous I/O works using the overlapped event associated
+ * with a fake pipe. The read/write functions are only meant to be used in that
+ * context.
+ */
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <io.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
+// 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_mode);
+// _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_mode;
+       switch (flags) {
+       case _O_RDONLY:
+               access_mode = GENERIC_READ;
+               break;
+       case _O_WRONLY:
+               access_mode = GENERIC_WRITE;
+               break;
+       case _O_RDWR:
+               access_mode = GENERIC_READ|GENERIC_WRITE;
+               break;
+       default:
+               usbi_err(NULL, "unsupported access mode");
+               return -1;
+       }
+       return cygwin_attach_handle_to_fd("/dev/null", -1, (HANDLE)osfhandle, -1, access_mode);
+}
+#endif
+
+#define CHECK_INIT_POLLING do {if(!is_polling_set) init_polling();} while(0)
+
+// public fd data
+const struct winfd INVALID_WINFD = {-1, INVALID_HANDLE_VALUE, NULL, RW_NONE};
+struct winfd poll_fd[MAX_FDS];
+// internal fd data
+struct {
+       CRITICAL_SECTION mutex; // lock for fds
+       // Additional variables for XP CancelIoEx partial emulation
+       HANDLE original_handle;
+       DWORD thread_id;
+} _poll_fd[MAX_FDS];
+
+// globals
+BOOLEAN is_polling_set = FALSE;
+#if defined(DYNAMIC_FDS)
+HANDLE fd_update = INVALID_HANDLE_VALUE;       // event to notify poll of fd update
+HANDLE new_fd[MAX_FDS];                // overlapped event handles 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;
+#define CancelIoEx_Available (pCancelIoEx != NULL)
+__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 (CancelIoEx_Available) {
+               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;
+}
+
+// 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",
+                       CancelIoEx_Available?"Ex":"");
+               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);
+               }
+#if defined(DYNAMIC_FDS)
+               // We need to create an update event so that poll is warned when there
+               // are new/deleted fds during a timeout wait operation
+               fd_update = CreateEvent(NULL, TRUE, FALSE, NULL);
+               if (fd_update == NULL) {
+                       usbi_err(NULL, "unable to create update event");
+               }
+               usbi_mutex_init(&new_fd_mutex, NULL);
+               nb_new_fds = 0;
+#endif
+               is_polling_set = TRUE;
+       }
+       compat_spinlock = 0;
+}
+
+// Internal function to retrieve the table index (and lock the fd mutex)
+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;
+}
+
+OVERLAPPED *create_overlapped(void)
+{
+       OVERLAPPED *overlapped = calloc(1, sizeof(OVERLAPPED));
+       if (overlapped == NULL) {
+               return NULL;
+       }
+       overlapped->hEvent = 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<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);
+                       if ( (poll_fd[i].fd > 0) && (poll_fd[i].handle != INVALID_HANDLE_VALUE) && (poll_fd[i].handle != 0)
+                         && (GetFileType(poll_fd[i].handle) == FILE_TYPE_UNKNOWN) ) {
+                               _close(poll_fd[i].fd);
+                       }
+                       free_overlapped(poll_fd[i].overlapped);
+                       if (!CancelIoEx_Available) {
+                               // Close duplicate handle
+                               if (_poll_fd[i].original_handle != INVALID_HANDLE_VALUE) {
+                                       CloseHandle(poll_fd[i].handle);
+                               }
+                       }
+                       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;
+}
+
+/*
+ * 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;
+       HANDLE handle;
+       OVERLAPPED* overlapped;
+
+       CHECK_INIT_POLLING;
+
+       overlapped = calloc(1, sizeof(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;
+
+       // Read end of the "pipe"
+       handle = CreateFileA("NUL", 0, 0, NULL, OPEN_EXISTING, 0, NULL);
+       if (handle == INVALID_HANDLE_VALUE) {
+               usbi_err(NULL, "could not create pipe: errcode %d", (int)GetLastError());
+               goto out1;
+       }
+       filedes[0] = _open_osfhandle((intptr_t)handle, _O_RDONLY);
+       // We can use the same handle for both ends
+       filedes[1] = filedes[0];
+       poll_dbg("pipe filedes = %d", filedes[0]);
+
+       // Note: manual reset must be true (second param) as the reset occurs in read
+       overlapped->hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+       if(!overlapped->hEvent) {
+               goto out2;
+       }
+
+       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;
+                       }
+
+                       poll_fd[i].fd = filedes[0];
+                       poll_fd[i].handle = 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;
+               }
+       }
+
+       CloseHandle(overlapped->hEvent);
+out2:
+       CloseHandle(handle);
+out1:
+       free(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
+ * 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)) {
+               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<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;
+                       }
+                       wfd.fd = fd;
+                       // Attempt to emulate some of the CancelIoEx behaviour on platforms
+                       // that don't have it
+                       if (!CancelIoEx_Available) {
+                               _poll_fd[i].thread_id = GetCurrentThreadId();
+                               if (!DuplicateHandle(GetCurrentProcess(), handle, GetCurrentProcess(),
+                                       &wfd.handle, 0, TRUE, DUPLICATE_SAME_ACCESS)) {
+                                       usbi_warn(NULL, "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);
+#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);
+#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);
+       }
+       // close the duplicate handle (if we have an actual duplicate)
+       if (!CancelIoEx_Available) {
+               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(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<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;
+               }
+       }
+       return INVALID_WINFD;
+}
+
+struct winfd handle_to_winfd(HANDLE handle)
+{
+       int i;
+       struct winfd wfd;
+
+       CHECK_INIT_POLLING;
+
+       if ((handle == 0) || (handle == INVALID_HANDLE_VALUE))
+               return INVALID_WINFD;
+
+       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);
+                               continue;
+                       }
+                       memcpy(&wfd, &poll_fd[i], sizeof(struct winfd));
+                       LeaveCriticalSection(&_poll_fd[i].mutex);
+                       return wfd;
+               }
+       }
+       return INVALID_WINFD;
+}
+
+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
+ * (but you can create multiple fds from the same handle for read and write)
+ */
+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;
+       DWORD ret;
+
+#if defined(DYNAMIC_FDS)
+       DWORD nb_extra_handles = 0;
+       unsigned j;
+
+       // To address the possibility of missing new fds between the time the new
+       // pollable fd set is assembled, and the ResetEvent() call below, an
+       // additional new_fd[] HANDLE table is used for any new fd that was created
+       // since the last call to poll (see below)
+       ResetEvent(fd_update);
+
+       // At this stage, any new fd creation will be detected through the fd_update
+       // event notification, and any previous creation that we may have missed
+       // will be picked up through the existing new_fd[] table.
+#endif
+
+       CHECK_INIT_POLLING;
+
+       triggered = 0;
+       handles_to_wait_on = malloc((nfds+1)*sizeof(HANDLE));   // +1 for fd_update
+       handle_to_index = malloc(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;
+#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<nb_new_fds; j++) {
+                               if (handles_to_wait_on[nb_handles_to_wait_on] == new_fd[j]) {
+                                       new_fd[j] = INVALID_HANDLE_VALUE;
+                                       break;
+                               }
+                       }
+                       usbi_mutex_unlock(&new_fd_mutex);
+#endif
+                       nb_handles_to_wait_on++;
+               }
+               LeaveCriticalSection(&_poll_fd[index].mutex);
+       }
+#if defined(DYNAMIC_FDS)
+       // At this stage, new_fd[] should only contain events from fds that
+       // have been added since the last call to poll, but are not (yet) part
+       // of the pollable fd set. Typically, these would be from fds that have
+       // been created between the construction of the fd set and the calling
+       // of poll.
+       // Event if we won't be able to return usable poll data on these events,
+       // make sure we monitor them to return an EINTR code
+       usbi_mutex_lock(&new_fd_mutex); // We could probably do without
+       for (i=0; i<nb_new_fds; i++) {
+               if (new_fd[i] != INVALID_HANDLE_VALUE) {
+                       handles_to_wait_on[nb_handles_to_wait_on++] = new_fd[i];
+                       nb_extra_handles++;
+               }
+       }
+       usbi_mutex_unlock(&new_fd_mutex);
+       poll_dbg("dynamic_fds: added %d extra handles", nb_extra_handles);
+#endif
+
+       // If nothing was triggered, wait on all fds that require it
+       if ((timeout != 0) && (triggered == 0) && (nb_handles_to_wait_on != 0)) {
+#if defined(DYNAMIC_FDS)
+               // Register for fd update notifications
+               handles_to_wait_on[nb_handles_to_wait_on++] = fd_update;
+               nb_extra_handles++;
+#endif
+               if (timeout < 0) {
+                       poll_dbg("starting infinite wait for %d handles...", (int)nb_handles_to_wait_on);
+               } else {
+                       poll_dbg("starting %d ms wait for %d handles...", timeout, (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)) {
+#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 fake pipe fd
+ */
+int usbi_close(int fd)
+{
+       int index;
+       int r = -1;
+
+       CHECK_INIT_POLLING;
+
+       index = _fd_to_index_and_lock(fd);
+
+       if (index < 0) {
+               errno = EBADF;
+       } else {
+               if (poll_fd[index].overlapped != NULL) {
+                       // Must be a different event for each end of the pipe
+                       CloseHandle(poll_fd[index].overlapped->hEvent);
+                       free(poll_fd[index].overlapped);
+               }
+               if (CloseHandle(poll_fd[index].handle) == 0) {
+                       errno = EIO;
+               } else {
+                       r = 0;
+               }
+               poll_fd[index] = INVALID_WINFD;
+               LeaveCriticalSection(&_poll_fd[index].mutex);
+       }
+       return r;
+}
+
+/*
+ * synchronous write for fake "pipe" signaling
+ */
+ssize_t usbi_write(int fd, const void *buf, size_t count)
+{
+       int index;
+
+       CHECK_INIT_POLLING;
+
+       if (count != sizeof(unsigned char)) {
+               usbi_err(NULL, "this function should only used for signaling");
+               return -1;
+       }
+
+       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;
+       }
+
+       poll_dbg("set pipe event (fd = %d, thread = %08X)", index, 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++;
+
+       LeaveCriticalSection(&_poll_fd[index].mutex);
+       return sizeof(unsigned char);
+}
+
+/*
+ * synchronous read for fake "pipe" signaling
+ */
+ssize_t usbi_read(int fd, void *buf, size_t count)
+{
+       int index;
+       ssize_t r = -1;
+
+       CHECK_INIT_POLLING;
+
+       if (count != sizeof(unsigned char)) {
+               usbi_err(NULL, "this function should only used for signaling");
+               return -1;
+       }
+
+       index = _fd_to_index_and_lock(fd);
+
+       if (index < 0) {
+               errno = EBADF;
+               return -1;
+       }
+
+       if (WaitForSingleObject(poll_fd[index].overlapped->hEvent, INFINITE) != WAIT_OBJECT_0) {
+               usbi_warn(NULL, "waiting for event failed: %d", (int)GetLastError());
+               errno = EIO;
+               goto out;
+       }
+
+       poll_dbg("clr pipe event (fd = %d, thread = %08X)", index, 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;
+       }
+
+       r = sizeof(unsigned char);
+
+out:
+       LeaveCriticalSection(&_poll_fd[index].mutex);
+       return r;
+}
diff --git a/libusb/os/poll_windows.h b/libusb/os/poll_windows.h
new file mode 100644 (file)
index 0000000..a9e3e90
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * Windows compat: POSIX compatibility wrapper
+ * Copyright (C) 2009-2010 Pete Batard <pbatard@gmail.com>
+ * 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
+
+// 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
+
+// Handle synchronous completion through the overlapped structure
+#if !defined(STATUS_REPARSE)   // reuse the REPARSE status code
+#define STATUS_REPARSE ((LONG)0x00000104L)
+#endif
+#define STATUS_COMPLETED_SYNCHRONOUSLY STATUS_REPARSE
+#define HasOverlappedIoCompletedSync(lpOverlapped)     (((DWORD)(lpOverlapped)->Internal) == STATUS_COMPLETED_SYNCHRONOUSLY)
+
+enum windows_version {
+       WINDOWS_UNSUPPORTED,
+       WINDOWS_XP,
+       WINDOWS_2003,   // also includes XP 64
+       WINDOWS_VISTA_AND_LATER,
+};
+extern enum windows_version windows_version;
+
+#define MAX_FDS     256
+
+#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 */
+};
+
+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)
+};
+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);
+
+/*
+ * Timeval operations
+ */
+#if defined(DDKBUILD)
+#include <winsock.h>   // defines timeval functions on DDK
+#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
+
index 728c101..dc558d4 100644 (file)
@@ -41,5 +41,6 @@
 #define usbi_cond_timedwait            pthread_cond_timedwait
 #define usbi_cond_broadcast            pthread_cond_broadcast
 #define usbi_cond_destroy              pthread_cond_destroy
+#define usbi_cond_signal       pthread_cond_signal
 
 #endif /* __LIBUSB_THREADS_POSIX_H__ */
diff --git a/libusb/os/threads_windows.c b/libusb/os/threads_windows.c
new file mode 100644 (file)
index 0000000..8a29920
--- /dev/null
@@ -0,0 +1,208 @@
+/*
+ * libusb synchronization on Microsoft Windows
+ *
+ * Copyright (C) 2010 Michael Plante <michael.plante@gmail.com>
+ *
+ * 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
+ */
+
+#include <config.h>
+#include <objbase.h>
+#include <errno.h>
+#include <stdarg.h>
+
+#include "libusbi.h"
+
+
+int usbi_mutex_init(usbi_mutex_t *mutex,
+                                       const usbi_mutexattr_t *attr) {
+       if(! mutex) return ((errno=EINVAL));
+       *mutex = CreateMutex(NULL, FALSE, NULL);
+       if(!*mutex) return ((errno=ENOMEM));
+       return 0;
+}
+int usbi_mutex_destroy(usbi_mutex_t *mutex) {
+       // It is not clear if CloseHandle failure is due to failure to unlock.
+       //   If so, this should be errno=EBUSY.
+       if(!mutex || !CloseHandle(*mutex)) return ((errno=EINVAL));
+       *mutex = NULL;
+       return 0;
+}
+int usbi_mutex_trylock(usbi_mutex_t *mutex) {
+       DWORD result;
+       if(!mutex) return ((errno=EINVAL));
+       result = WaitForSingleObject(*mutex, 0);
+       if(result == WAIT_OBJECT_0 || result == WAIT_ABANDONED)
+               return 0; // acquired (ToDo: check that abandoned is ok)
+       if(result == WAIT_TIMEOUT)
+               return ((errno=EBUSY));
+       return ((errno=EINVAL)); // don't know how this would happen
+                                                        //   so don't know proper errno
+}
+int usbi_mutex_lock(usbi_mutex_t *mutex) {
+       DWORD result;
+       if(!mutex) return ((errno=EINVAL));
+       result = WaitForSingleObject(*mutex, INFINITE);
+       if(result == WAIT_OBJECT_0 || result == WAIT_ABANDONED)
+               return 0; // acquired (ToDo: check that abandoned is ok)
+       return ((errno=EINVAL)); // don't know how this would happen
+                                                        //   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 ));
+       return 0;
+}
+
+int usbi_mutex_static_lock(usbi_mutex_static_t *mutex) {
+       if(!mutex)               return ((errno=EINVAL));
+       while (InterlockedExchange((LONG *)mutex, 1) == 1) {
+               SleepEx(0, TRUE);
+       }
+       return 0;
+}
+int usbi_mutex_static_unlock(usbi_mutex_static_t *mutex) {
+       if(!mutex)               return ((errno=EINVAL));
+       *mutex = 0;
+       return 0;
+}
+
+
+
+int usbi_cond_init(usbi_cond_t *cond,
+                                  const usbi_condattr_t *attr) {
+       if(!cond)           return ((errno=EINVAL));
+       list_init(&cond->waiters    );
+       list_init(&cond->not_waiting);
+       return 0;
+}
+int usbi_cond_destroy(usbi_cond_t *cond) {
+       // This assumes no one is using this anymore.  The check MAY NOT BE safe.
+       struct usbi_cond_perthread *pos, *prev_pos = NULL;
+       if(!cond) return ((errno=EINVAL));
+       if(!list_empty(&cond->waiters)) return ((errno=EBUSY )); // (!see above!)
+       list_for_each_entry(pos, &cond->not_waiting, list, struct usbi_cond_perthread) {
+               free(prev_pos);
+               list_del(&pos->list);
+               prev_pos = pos;
+       }
+       free(prev_pos);
+       prev_pos = pos = NULL;
+
+       return 0;
+}
+
+int usbi_cond_broadcast(usbi_cond_t *cond) {
+       // Assumes mutex is locked; this is not in keeping with POSIX spec, but
+       //   libusb does this anyway, so we simplify by not adding more sync
+       //   primitives to the CV definition!
+       int fail = 0;
+       struct usbi_cond_perthread *pos;
+       if(!cond)                      return ((errno=EINVAL));
+       list_for_each_entry(pos, &cond->waiters, list, struct usbi_cond_perthread) {
+               if(!SetEvent(pos->event))
+                       fail = 1;
+       }
+       // The wait function will remove its respective item from the list.
+       return fail ? ((errno=EINVAL)) : 0;
+}
+int usbi_cond_signal(usbi_cond_t *cond) {
+       // Assumes mutex is locked; this is not in keeping with POSIX spec, but
+       //   libusb does this anyway, so we simplify by not adding more sync
+       //   primitives to the CV definition!
+       struct usbi_cond_perthread *pos;
+       if(!cond)                      return ((errno=EINVAL));
+       if(list_empty(&cond->waiters)) return 0; // no one to wakeup.
+       pos = list_entry(&cond->waiters.next, struct usbi_cond_perthread, list);
+       // The wait function will remove its respective item from the list.
+       return SetEvent(pos->event) ? 0 : ((errno=EINVAL));
+}
+static int __inline usbi_cond_intwait(usbi_cond_t *cond,
+                                                                         usbi_mutex_t *mutex,
+                                                                         DWORD timeout_ms) {
+       struct usbi_cond_perthread *pos;
+       int found = 0, r;
+       DWORD r2,tid = GetCurrentThreadId();
+       if(!cond || !mutex) return ((errno=EINVAL));
+       list_for_each_entry(pos, &cond->not_waiting, list, struct usbi_cond_perthread) {
+               if(tid == pos->tid) {
+                       found = 1;
+                       break;
+               }
+       }
+       if(!found) {
+               pos      = malloc(sizeof(struct usbi_cond_perthread));
+               if(!pos) return ((errno=ENOMEM)); // This errno is not POSIX-allowed.
+               pos->tid = tid;
+               pos->event = CreateEvent(NULL, FALSE, FALSE, NULL); // auto-reset.
+               if(!pos->event) {
+                       free(pos);
+                       return      ((errno=ENOMEM));
+               }
+               list_add(&pos->list, &cond->not_waiting);
+       }
+
+       list_del(&pos->list); // remove from not_waiting list.
+       list_add(&pos->list, &cond->waiters);
+
+       r  = usbi_mutex_unlock(mutex);
+       if(r) return r;
+       r2 = WaitForSingleObject(pos->event, timeout_ms);
+       r  = usbi_mutex_lock(mutex);
+       if(r) return r;
+
+       list_del(&pos->list);
+       list_add(&pos->list, &cond->not_waiting);
+
+       if(r2 == WAIT_TIMEOUT) return ((errno=ETIMEDOUT));
+
+       return 0;
+}
+// N.B.: usbi_cond_*wait() can also return ENOMEM, even though pthread_cond_*wait cannot!
+int usbi_cond_wait(usbi_cond_t *cond, usbi_mutex_t *mutex) {
+       return usbi_cond_intwait(cond, mutex, INFINITE);
+}
+int usbi_cond_timedwait(usbi_cond_t *cond,
+                                               usbi_mutex_t *mutex,
+                                               const struct timespec *abstime) {
+       FILETIME filetime;
+       ULARGE_INTEGER rtime;
+       struct timeval targ_time, cur_time, delta_time;
+       struct timespec cur_time_ns;
+       DWORD millis;
+       extern const uint64_t epoch_time;
+
+       GetSystemTimeAsFileTime(&filetime);
+       rtime.LowPart   = filetime.dwLowDateTime;
+       rtime.HighPart  = filetime.dwHighDateTime;
+       rtime.QuadPart -= epoch_time;
+       cur_time_ns.tv_sec = (long)(rtime.QuadPart / 10000000);
+       cur_time_ns.tv_nsec = (long)((rtime.QuadPart % 10000000)*100);
+       TIMESPEC_TO_TIMEVAL(&cur_time, &cur_time_ns);
+
+       TIMESPEC_TO_TIMEVAL(&targ_time, abstime);
+       timersub(&targ_time, &cur_time, &delta_time);
+       if(delta_time.tv_sec < 0) // abstime already passed?
+               millis = 0;
+       else {
+               millis  = delta_time.tv_usec/1000;
+               millis += delta_time.tv_sec *1000;
+               if (delta_time.tv_usec % 1000) // round up to next millisecond
+                       millis++;
+       }
+
+       return usbi_cond_intwait(cond, mutex, millis);
+}
+
diff --git a/libusb/os/threads_windows.h b/libusb/os/threads_windows.h
new file mode 100644 (file)
index 0000000..2cd1867
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * libusb synchronization on Microsoft Windows
+ *
+ * Copyright (C) 2010 Michael Plante <michael.plante@gmail.com>
+ *
+ * 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
+ */
+
+#ifndef __LIBUSB_THREADS_WINDOWS_H__
+#define __LIBUSB_THREADS_WINDOWS_H__
+
+#define usbi_mutex_static_t     volatile LONG
+#define USBI_MUTEX_INITIALIZER  0
+
+#define usbi_mutex_t            HANDLE
+
+struct usbi_cond_perthread {
+       struct list_head list;
+       DWORD            tid;
+       HANDLE           event;
+};
+struct usbi_cond_t_ {
+       // Every time a thread touches the CV, it winds up in one of these lists.
+       //   It stays there until the CV is destroyed, even if the thread
+       //   terminates.
+       struct list_head waiters;
+       struct list_head not_waiting;
+};
+typedef struct usbi_cond_t_ usbi_cond_t;
+
+// We *were* getting timespec from pthread.h:
+#if (!defined(HAVE_STRUCT_TIMESPEC) && !defined(_TIMESPEC_DEFINED))
+#define HAVE_STRUCT_TIMESPEC 1
+#define _TIMESPEC_DEFINED 1
+struct timespec {
+               long tv_sec;
+               long tv_nsec;
+};
+#endif /* HAVE_STRUCT_TIMESPEC | _TIMESPEC_DEFINED */
+
+// We *were* getting ETIMEDOUT from pthread.h:
+#ifndef ETIMEDOUT
+#  define ETIMEDOUT 10060     /* This is the value in winsock.h. */
+#endif
+
+#define usbi_mutexattr_t void
+#define usbi_condattr_t  void
+
+
+int usbi_mutex_static_lock(usbi_mutex_static_t *mutex);
+int usbi_mutex_static_unlock(usbi_mutex_static_t *mutex);
+
+
+int usbi_mutex_init(usbi_mutex_t *mutex,
+                                       const usbi_mutexattr_t *attr);
+int usbi_mutex_lock(usbi_mutex_t *mutex);
+int usbi_mutex_unlock(usbi_mutex_t *mutex);
+int usbi_mutex_trylock(usbi_mutex_t *mutex);
+int usbi_mutex_destroy(usbi_mutex_t *mutex);
+
+int usbi_cond_init(usbi_cond_t *cond,
+                                  const usbi_condattr_t *attr);
+int usbi_cond_destroy(usbi_cond_t *cond);
+int usbi_cond_wait(usbi_cond_t *cond, usbi_mutex_t *mutex);
+int usbi_cond_timedwait(usbi_cond_t *cond,
+                                               usbi_mutex_t *mutex,
+                                               const struct timespec *abstime);
+int usbi_cond_broadcast(usbi_cond_t *cond);
+int usbi_cond_signal(usbi_cond_t *cond);
+
+#endif /* __LIBUSB_THREADS_WINDOWS_H__ */
+
diff --git a/libusb/os/windows_usb.c b/libusb/os/windows_usb.c
new file mode 100644 (file)
index 0000000..58d3f4c
--- /dev/null
@@ -0,0 +1,4243 @@
+/*
+ * windows backend for libusb 1.0
+ * Copyright (c) 2009-2010 Pete Batard <pbatard@gmail.com>
+ * With contributions from Michael Plante, Orin Eman et al.
+ * Parts of this code adapted from libusb-win32-v1 by Stephan Meyer
+ * Major code testing contribution by Xiaofan Chen
+ *
+ * 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
+ */
+
+// COMPILATION OPTIONS:
+// - Use HidD_(G/S)et(In/Out)putReport instead of (Read/Write)File for HID
+//   Note that http://msdn.microsoft.com/en-us/library/ms789883.aspx:
+//   "In addition, some devices might not support HidD_GetInputReport,
+//    and will become unresponsive if this routine is used."
+//   => Don't blame libusb if you can't read or write HID reports when the
+//   option below is enabled.
+#define USE_HIDD_FOR_REPORTS
+// - Should libusb automatically claim (and release) the interfaces it requires?
+#define AUTO_CLAIM
+// - Forces instant overlapped completion on timeouts: can prevents extensive
+//   wait in poll, after a timeout, but might affect subsequent API calls.
+//   ***USE AT YOUR OWN RISKS***
+//#define FORCE_INSTANT_TIMEOUTS
+
+#include <config.h>
+#include <windows.h>
+#include <setupapi.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <process.h>
+#include <stdio.h>
+#include <inttypes.h>
+#include <objbase.h>  // for string to GUID conv. requires libole32.a
+
+#include <libusbi.h>
+#include "poll_windows.h"
+#include "windows_usb.h"
+
+// The following prevents "banned API" errors when using the MS's WDK OACR/Prefast
+#if defined(_PREFAST_)
+#pragma warning(disable:28719)
+#endif
+
+// The 2 macros below are used in conjunction with safe loops.
+#define LOOP_CHECK(fcall) { r=fcall; if (r != LIBUSB_SUCCESS) continue; }
+#define LOOP_BREAK(err) { r=err; continue; }
+
+extern void usbi_fd_notification(struct libusb_context *ctx);
+
+// Helper prototypes
+static int windows_get_active_config_descriptor(struct libusb_device *dev, unsigned char *buffer, size_t len, int *host_endian);
+static int windows_clock_gettime(int clk_id, struct timespec *tp);
+unsigned __stdcall windows_clock_gettime_threaded(void* param);
+// WinUSB API prototypes
+static int winusb_init(struct libusb_context *ctx);
+static int winusb_exit(void);
+static int winusb_open(struct libusb_device_handle *dev_handle);
+static void winusb_close(struct libusb_device_handle *dev_handle);
+static int winusb_claim_interface(struct libusb_device_handle *dev_handle, int iface);
+static int winusb_release_interface(struct libusb_device_handle *dev_handle, int iface);
+static int winusb_submit_control_transfer(struct usbi_transfer *itransfer);
+static int winusb_set_interface_altsetting(struct libusb_device_handle *dev_handle, int iface, int altsetting);
+static int winusb_submit_bulk_transfer(struct usbi_transfer *itransfer);
+static int winusb_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint);
+static int winusb_abort_transfers(struct usbi_transfer *itransfer);
+static int winusb_abort_control(struct usbi_transfer *itransfer);
+static int winusb_reset_device(struct libusb_device_handle *dev_handle);
+static int winusb_copy_transfer_data(struct usbi_transfer *itransfer, uint32_t io_size);
+// HID API prototypes
+static int hid_init(struct libusb_context *ctx);
+static int hid_exit(void);
+static int hid_open(struct libusb_device_handle *dev_handle);
+static void hid_close(struct libusb_device_handle *dev_handle);
+static int hid_claim_interface(struct libusb_device_handle *dev_handle, int iface);
+static int hid_release_interface(struct libusb_device_handle *dev_handle, int iface);
+static int hid_set_interface_altsetting(struct libusb_device_handle *dev_handle, int iface, int altsetting);
+static int hid_submit_control_transfer(struct usbi_transfer *itransfer);
+static int hid_submit_bulk_transfer(struct usbi_transfer *itransfer);
+static int hid_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint);
+static int hid_abort_transfers(struct usbi_transfer *itransfer);
+static int hid_reset_device(struct libusb_device_handle *dev_handle);
+static int hid_copy_transfer_data(struct usbi_transfer *itransfer, uint32_t io_size);
+// Composite API prototypes
+static int composite_init(struct libusb_context *ctx);
+static int composite_exit(void);
+static int composite_open(struct libusb_device_handle *dev_handle);
+static void composite_close(struct libusb_device_handle *dev_handle);
+static int composite_claim_interface(struct libusb_device_handle *dev_handle, int iface);
+static int composite_set_interface_altsetting(struct libusb_device_handle *dev_handle, int iface, int altsetting);
+static int composite_release_interface(struct libusb_device_handle *dev_handle, int iface);
+static int composite_submit_control_transfer(struct usbi_transfer *itransfer);
+static int composite_submit_bulk_transfer(struct usbi_transfer *itransfer);
+static int composite_submit_iso_transfer(struct usbi_transfer *itransfer);
+static int composite_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint);
+static int composite_abort_transfers(struct usbi_transfer *itransfer);
+static int composite_abort_control(struct usbi_transfer *itransfer);
+static int composite_reset_device(struct libusb_device_handle *dev_handle);
+static int composite_copy_transfer_data(struct usbi_transfer *itransfer, uint32_t io_size);
+
+
+// Global variables
+struct windows_hcd_priv* hcd_root = NULL;
+uint64_t hires_frequency, hires_ticks_to_ps;
+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;
+#if defined(AUTO_CLAIM)
+usbi_mutex_t autoclaim_lock;
+#endif
+// Timer thread
+// NB: index 0 is for monotonic and 1 is for the thread exit event
+HANDLE timer_thread = NULL;
+HANDLE timer_mutex = NULL;
+struct timespec timer_tp;
+volatile LONG request_count[2] = {0, 1};       // last one must be > 0
+HANDLE timer_request[2] = { NULL, NULL };
+HANDLE timer_response = NULL;
+// API globals
+bool api_winusb_available = false;
+#define CHECK_WINUSB_AVAILABLE do { if (!api_winusb_available) return LIBUSB_ERROR_ACCESS; } while (0)
+bool api_hid_available = false;
+#define CHECK_HID_AVAILABLE do { if (!api_hid_available) return LIBUSB_ERROR_ACCESS; } while (0)
+
+
+/*
+ * Converts a WCHAR string to UTF8 (allocate returned string)
+ * Returns NULL on error
+ */
+static char* wchar_to_utf8(LPCWSTR wstr)
+{
+       int size;
+       char* str;
+
+       // Find out the size we need to allocate for our converted string
+       size = wchar_to_utf8_ms(wstr, NULL, 0);
+       if (size <= 1)  // An empty string would be size 1
+               return NULL;
+
+       if ((str = malloc(size)) == NULL)
+               return NULL;
+
+       if (wchar_to_utf8_ms(wstr, str, size) != size) {
+               safe_free(str);
+               return NULL;
+       }
+
+       return str;
+}
+
+static inline BOOLEAN guid_eq(const GUID *guid1, const GUID *guid2) {
+       if ((guid1 != NULL) && (guid2 != NULL)) {
+               return (memcmp(guid1, guid2, sizeof(GUID)) == 0);
+       }
+       return false;
+}
+
+#if 0
+static char* guid_to_string(const GUID guid)
+{
+       static char guid_string[MAX_GUID_STRING_LENGTH];
+
+       sprintf(guid_string, "{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
+               (unsigned int)guid.Data1, guid.Data2, guid.Data3,
+               guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3],
+               guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]);
+       return guid_string;
+}
+#endif
+
+/*
+ * Converts a windows error to human readable string
+ * uses retval as errorcode, or, if 0, use GetLastError()
+ */
+static char *windows_error_str(uint32_t retval)
+{
+static char err_string[ERR_BUFFER_SIZE];
+
+       DWORD size;
+       uint32_t errcode, format_errcode;
+
+       errcode = retval?retval:GetLastError();
+
+       safe_sprintf(err_string, ERR_BUFFER_SIZE, "[%d] ", errcode);
+
+       size = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, errcode,
+               MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &err_string[strlen(err_string)],
+               ERR_BUFFER_SIZE, NULL);
+       if (size == 0)
+       {
+               format_errcode = GetLastError();
+               if (format_errcode)
+                       safe_sprintf(err_string, ERR_BUFFER_SIZE,
+                               "Windows error code %u (FormatMessage error code %u)", errcode, format_errcode);
+               else
+                       safe_sprintf(err_string, ERR_BUFFER_SIZE, "Unknown error code %u", errcode);
+       }
+       return err_string;
+}
+
+/*
+ * Sanitize Microsoft's paths: convert to uppercase, add prefix and fix backslashes.
+ * Return an allocated sanitized string or NULL on error.
+ */
+static char* sanitize_path(const char* path)
+{
+       const char root_prefix[] = "\\\\.\\";
+       size_t j, size, root_size;
+       char* ret_path = NULL;
+       size_t add_root = 0;
+
+       if (path == NULL)
+               return NULL;
+
+       size = strlen(path)+1;
+       root_size = sizeof(root_prefix)-1;
+
+       // Microsoft indiscriminatly uses '\\?\', '\\.\', '##?#" or "##.#" for root prefixes.
+       if (!((size > 3) && (((path[0] == '\\') && (path[1] == '\\') && (path[3] == '\\')) ||
+               ((path[0] == '#') && (path[1] == '#') && (path[3] == '#'))))) {
+               add_root = root_size;
+               size += add_root;
+       }
+
+       if ((ret_path = (char*)calloc(size, 1)) == NULL)
+               return NULL;
+
+       safe_strncpy(&ret_path[add_root], size-add_root, path, strlen(path));
+
+       // Ensure consistancy with root prefix
+       for (j=0; j<root_size; j++)
+               ret_path[j] = root_prefix[j];
+
+       // Same goes for '\' and '#' after the root prefix. Ensure '#' is used
+       for(j=root_size; j<size; j++) {
+               ret_path[j] = (char)toupper((int)ret_path[j]);  // Fix case too
+               if (ret_path[j] == '\\')
+                       ret_path[j] = '#';
+       }
+
+       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
+ *
+ * Parameters:
+ * dev_info: a pointer to a dev_info list
+ * dev_info_data: a pointer to an SP_DEVINFO_DATA to be filled (or NULL if not needed)
+ * guid: the GUID for which to retrieve interface details
+ * index: zero based index of the interface in the device info list
+ *
+ * Note: it is the responsibility of the caller to free the DEVICE_INTERFACE_DETAIL_DATA
+ * structure returned and call this function repeatedly using the same guid (with an
+ * incremented index starting at zero) until all interfaces have been returned.
+ */
+SP_DEVICE_INTERFACE_DETAIL_DATA *get_interface_details(struct libusb_context *ctx,
+       HDEVINFO *dev_info, SP_DEVINFO_DATA *dev_info_data, GUID guid, unsigned index)
+{
+       SP_DEVICE_INTERFACE_DATA dev_interface_data;
+       SP_DEVICE_INTERFACE_DETAIL_DATA *dev_interface_details = NULL;
+       DWORD size;
+
+       if (index <= 0) {
+               *dev_info = SetupDiGetClassDevs(&guid, NULL, NULL, DIGCF_PRESENT|DIGCF_DEVICEINTERFACE);
+       }
+       if (*dev_info == INVALID_HANDLE_VALUE) {
+               return NULL;
+       }
+
+       if (dev_info_data != NULL) {
+               dev_info_data->cbSize = sizeof(SP_DEVINFO_DATA);
+               if (!SetupDiEnumDeviceInfo(*dev_info, index, dev_info_data)) {
+                       if (GetLastError() != ERROR_NO_MORE_ITEMS) {
+                               usbi_err(ctx, "Could not obtain device info data for index %u: %s",
+                                       index, windows_error_str(0));
+                       }
+                       SetupDiDestroyDeviceInfoList(*dev_info);
+                       *dev_info = INVALID_HANDLE_VALUE;
+                       return NULL;
+               }
+       }
+
+       dev_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
+       if (!SetupDiEnumDeviceInterfaces(*dev_info, NULL, &guid, index, &dev_interface_data)) {
+               if (GetLastError() != ERROR_NO_MORE_ITEMS) {
+                       usbi_err(ctx, "Could not obtain interface data for index %u: %s",
+                               index, windows_error_str(0));
+               }
+               SetupDiDestroyDeviceInfoList(*dev_info);
+               *dev_info = INVALID_HANDLE_VALUE;
+               return NULL;
+       }
+
+       // Read interface data (dummy + actual) to access the device path
+       if (!SetupDiGetDeviceInterfaceDetail(*dev_info, &dev_interface_data, NULL, 0, &size, NULL)) {
+               // The dummy call should fail with ERROR_INSUFFICIENT_BUFFER
+               if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+                       usbi_err(ctx, "could not access interface data (dummy) for index %u: %s",
+                               index, windows_error_str(0));
+                       goto err_exit;
+               }
+       }
+       else {
+               usbi_err(ctx, "program assertion failed - http://msdn.microsoft.com/en-us/library/ms792901.aspx is wrong.");
+               goto err_exit;
+       }
+
+       if ((dev_interface_details = malloc(size)) == NULL) {
+               usbi_err(ctx, "could not allocate interface data for index %u.", index);
+               goto err_exit;
+       }
+
+       dev_interface_details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
+       if (!SetupDiGetDeviceInterfaceDetail(*dev_info, &dev_interface_data,
+               dev_interface_details, size, &size, NULL)) {
+               usbi_err(ctx, "could not access interface data (actual) for index %u: %s",
+                       index, windows_error_str(0));
+       }
+
+       return dev_interface_details;
+
+err_exit:
+       SetupDiDestroyDeviceInfoList(*dev_info);
+       *dev_info = INVALID_HANDLE_VALUE;
+       return NULL;
+}
+
+/*
+ * Populate the endpoints addresses of the device_priv interface helper structs
+ */
+static int windows_assign_endpoints(struct libusb_device *dev, int iface, int altsetting)
+{
+       int i, r;
+       struct windows_device_priv *priv = __device_priv(dev);
+       struct libusb_config_descriptor *conf_desc;
+       const struct libusb_interface_descriptor *if_desc;
+
+       r = libusb_get_config_descriptor(dev, 0, &conf_desc);
+       if (r != LIBUSB_SUCCESS) {
+               usbi_warn(NULL, "could not read config descriptor: error %d", r);
+               return r;
+       }
+
+       if_desc = &conf_desc->interface[iface].altsetting[altsetting];
+       safe_free(priv->usb_interface[iface].endpoint);
+
+       if (if_desc->bNumEndpoints == 0) {
+               usbi_dbg("no endpoints found for interface %d", iface);
+               return LIBUSB_SUCCESS;
+       }
+
+       priv->usb_interface[iface].endpoint = malloc(if_desc->bNumEndpoints);
+       if (priv->usb_interface[iface].endpoint == NULL) {
+               return LIBUSB_ERROR_NO_MEM;
+       }
+
+       priv->usb_interface[iface].nb_endpoints = if_desc->bNumEndpoints;
+       for (i=0; i<if_desc->bNumEndpoints; i++) {
+               priv->usb_interface[iface].endpoint[i] = if_desc->endpoint[i].bEndpointAddress;
+               usbi_dbg("(re)assigned endpoint %02X to interface %d", priv->usb_interface[iface].endpoint[i], iface);
+       }
+       libusb_free_config_descriptor(conf_desc);
+
+       return LIBUSB_SUCCESS;
+}
+
+// Lookup for a match in the list of API driver names
+bool is_api_driver(char* driver, uint8_t api)
+{
+       uint8_t i;
+       for (i=0; i<usb_api_backend[api].nb_driver_names; i++) {
+               if (safe_strcmp(driver, usb_api_backend[api].driver_name_list[i]) == 0) {
+                       return true;
+               }
+       }
+       return false;
+}
+
+/*
+ * auto-claiming and auto-release helper functions
+ */
+#if defined(AUTO_CLAIM)
+static int auto_claim(struct libusb_transfer *transfer, int *interface_number, int api_type)
+{
+       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);
+       int current_interface = *interface_number;
+       int r = LIBUSB_SUCCESS;
+
+       switch(api_type) {
+       case USB_API_WINUSB:
+       case USB_API_HID:
+               break;
+       default:
+               return LIBUSB_ERROR_INVALID_PARAM;
+       }
+
+       usbi_mutex_lock(&autoclaim_lock);
+       if (current_interface < 0)      // No serviceable interface was found
+       {
+               for (current_interface=0; current_interface<USB_MAXINTERFACES; current_interface++) {
+                       // Must claim an interface of the same API type
+                       if ( (priv->usb_interface[current_interface].apib == &usb_api_backend[api_type])
+                         && (libusb_claim_interface(transfer->dev_handle, current_interface) == LIBUSB_SUCCESS) ) {
+                               usbi_dbg("auto-claimed interface %d for control request", current_interface);
+                               if (handle_priv->autoclaim_count[current_interface] != 0) {
+                                       usbi_warn(ctx, "program assertion failed - autoclaim_count was nonzero");
+                               }
+                               handle_priv->autoclaim_count[current_interface]++;
+                               break;
+                       }
+               }
+               if (current_interface == USB_MAXINTERFACES) {
+                       usbi_err(ctx, "could not auto-claim any interface");
+                       r = LIBUSB_ERROR_NOT_FOUND;
+               }
+       } else {
+               // If we have a valid interface that was autoclaimed, we must increment
+               // its autoclaim count so that we can prevent an early release.
+               if (handle_priv->autoclaim_count[current_interface] != 0) {
+                       handle_priv->autoclaim_count[current_interface]++;
+               }
+       }
+       usbi_mutex_unlock(&autoclaim_lock);
+
+       *interface_number = current_interface;
+       return r;
+
+}
+
+static void auto_release(struct usbi_transfer *itransfer)
+{
+       struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
+       struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+       libusb_device_handle *dev_handle = transfer->dev_handle;
+       struct windows_device_handle_priv* handle_priv = __device_handle_priv(dev_handle);
+       int r;
+
+       usbi_mutex_lock(&autoclaim_lock);
+       if (handle_priv->autoclaim_count[transfer_priv->interface_number] > 0) {
+               handle_priv->autoclaim_count[transfer_priv->interface_number]--;
+               if (handle_priv->autoclaim_count[transfer_priv->interface_number] == 0) {
+                       r = libusb_release_interface(dev_handle, transfer_priv->interface_number);
+                       if (r == LIBUSB_SUCCESS) {
+                               usbi_dbg("auto-released interface %d", transfer_priv->interface_number);
+                       } else {
+                               usbi_dbg("failed to auto-release interface %d (%s)",
+                                       transfer_priv->interface_number, libusb_strerror(r));
+                       }
+               }
+       }
+       usbi_mutex_unlock(&autoclaim_lock);
+}
+#endif
+
+
+/*
+ * init: libusb backend init function
+ *
+ * This function enumerates the HCDs (Host Controller Drivers) and populates our private HCD list
+ * In our implementation, we equate Windows' "HCD" to LibUSB's "bus". Note that bus is zero indexed.
+ * HCDs are not expected to change after init (might not hold true for hot pluggable USB PCI card?)
+ */
+static int windows_init(struct libusb_context *ctx)
+{
+       HDEVINFO dev_info;
+       SP_DEVICE_INTERFACE_DETAIL_DATA *dev_interface_details = NULL;
+       GUID guid;
+       libusb_bus_t bus;
+       int i, r = LIBUSB_ERROR_OTHER;
+       OSVERSIONINFO os_version;
+       HANDLE semaphore;
+       struct windows_hcd_priv** _hcd_cur;
+       TCHAR sem_name[11+1+8]; // strlen(libusb_init)+'\0'+(32-bit hex PID)
+
+       sprintf(sem_name, "libusb_init%08X", (unsigned int)GetCurrentProcessId()&0xFFFFFFFF);
+       semaphore = CreateSemaphore(NULL, 1, 1, sem_name);
+       if (semaphore == NULL) {
+               usbi_err(ctx, "could not create semaphore: %s", windows_error_str(0));
+               return LIBUSB_ERROR_NO_MEM;
+       }
+
+       // A successful wait brings our semaphore count to 0 (unsignaled)
+       // => any concurent wait stalls until the semaphore's release
+       if (WaitForSingleObject(semaphore, INFINITE) != WAIT_OBJECT_0) {
+               usbi_err(ctx, "failure to access semaphore: %s", windows_error_str(0));
+               CloseHandle(semaphore);
+               return LIBUSB_ERROR_NO_MEM;
+    }
+
+       // NB: concurrent usage supposes that init calls are equally balanced with
+       // exit calls. If init is called more than exit, we will not exit properly
+       if ( ++concurrent_usage == 0 ) {        // First init?
+               _hcd_cur = &hcd_root;
+
+               // Detect OS version
+               memset(&os_version, 0, sizeof(OSVERSIONINFO));
+               os_version.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+               windows_version = WINDOWS_UNSUPPORTED;
+               if ((GetVersionEx(&os_version) != 0) && (os_version.dwPlatformId == VER_PLATFORM_WIN32_NT)) {
+                       if ((os_version.dwMajorVersion == 5) && (os_version.dwMinorVersion == 1)) {
+                               windows_version = WINDOWS_XP;
+                       } else if ((os_version.dwMajorVersion == 5) && (os_version.dwMinorVersion == 2)) {
+                               windows_version = WINDOWS_2003; // also includes XP 64
+                       } else if (os_version.dwMajorVersion >= 6) {
+                               windows_version = WINDOWS_VISTA_AND_LATER;
+                       }
+               }
+               if (windows_version == WINDOWS_UNSUPPORTED) {
+                       usbi_err(ctx, "This version of Windows is NOT supported");
+                       r = LIBUSB_ERROR_NOT_SUPPORTED;
+                       goto init_exit;
+               }
+
+#if defined(AUTO_CLAIM)
+               // We need a lock for proper auto-release
+               usbi_mutex_init(&autoclaim_lock, NULL);
+#endif
+
+               // 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_NOT_FOUND;
+               }
+
+               // Initialize the low level APIs (we don't care about errors at this stage)
+               for (i=0; i<USB_API_MAX; i++) {
+                       usb_api_backend[i].init(ctx);
+               }
+
+               // Because QueryPerformanceCounter might report different values when
+               // running on different cores, we create a separate thread for the timer
+               // calls, which we glue to the first core always to prevent timing discrepancies.
+               r = LIBUSB_ERROR_NO_MEM;
+               for (i = 0; i < 2; i++) {
+                       timer_request[i] = CreateEvent(NULL, TRUE, FALSE, NULL);
+                       if (timer_request[i] == NULL) {
+                               usbi_err(ctx, "could not create timer request event %d - aborting", i);
+                               goto init_exit;
+                       }
+               }
+               timer_response = CreateSemaphore(NULL, 0, MAX_TIMER_SEMAPHORES, NULL);
+               if (timer_response == NULL) {
+                       usbi_err(ctx, "could not create timer response semaphore - aborting");
+                       goto init_exit;
+               }
+               timer_mutex = CreateMutex(NULL, FALSE, NULL);
+               if (timer_mutex == NULL) {
+                       usbi_err(ctx, "could not create timer mutex - aborting");
+                       goto init_exit;
+               }
+               timer_thread = (HANDLE)_beginthreadex(NULL, 0, windows_clock_gettime_threaded, NULL, 0, NULL);
+               if (timer_thread == NULL) {
+                       usbi_err(ctx, "Unable to create timer thread - aborting");
+                       goto init_exit;
+               }
+               SetThreadAffinityMask(timer_thread, 0);
+
+               guid = GUID_DEVINTERFACE_USB_HOST_CONTROLLER;
+
+               r = LIBUSB_SUCCESS;
+               for (bus = 0; ; bus++)
+               {
+                       // safe loop: free up any (unprotected) dynamic resource
+                       // NB: this is always executed before breaking the loop
+                       safe_free(dev_interface_details);
+                       safe_free(*_hcd_cur);
+
+                       dev_interface_details = get_interface_details(ctx, &dev_info, NULL, guid, bus);
+                       // safe loop: end of loop condition
+                       if ((dev_interface_details == NULL) || (r != LIBUSB_SUCCESS))
+                               break;
+
+                       // Will need to change storage and size of libusb_bus_t if this ever occurs
+                       if (bus == LIBUSB_BUS_MAX) {
+                               usbi_warn(ctx, "program assertion failed - found more than %d buses, skipping the rest.", LIBUSB_BUS_MAX);
+                               continue;
+                       }
+
+                       // Allocate and init a new priv structure to hold our data
+                       if ((*_hcd_cur = malloc(sizeof(struct windows_hcd_priv))) == NULL) {
+                               usbi_err(ctx, "could not allocate private structure for bus %u. aborting.", bus);
+                               LOOP_BREAK(LIBUSB_ERROR_NO_MEM);
+                       }
+                       windows_hcd_priv_init(*_hcd_cur);
+                       (*_hcd_cur)->path = sanitize_path(dev_interface_details->DevicePath);
+
+                       _hcd_cur = &((*_hcd_cur)->next);
+               }
+               // TODO (2nd official release): thread for hotplug (see darwin source)
+       }
+
+       if (hcd_root == NULL)
+               r = LIBUSB_ERROR_NO_DEVICE;
+       else
+               r = LIBUSB_SUCCESS;
+
+init_exit: // Holds semaphore here.
+       if(!concurrent_usage && r != LIBUSB_SUCCESS) { // First init failed?
+               if (timer_thread) {
+                       SetEvent(timer_request[1]); // actually the signal to quit the thread.
+                       if (WAIT_OBJECT_0 != WaitForSingleObject(timer_thread, INFINITE)) {
+                               usbi_warn(ctx, "could not wait for timer thread to quit");
+                               TerminateThread(timer_thread, 1); // shouldn't happen, but we're destroying
+                                                                                                 // all objects it might have held anyway.
+                       }
+                       CloseHandle(timer_thread);
+                       timer_thread = NULL;
+               }
+               for (i = 0; i < 2; i++) {
+                       if (timer_request[i]) {
+                               CloseHandle(timer_request[i]);
+                               timer_request[i] = NULL;
+                       }
+               }
+               if (timer_response) {
+                       CloseHandle(timer_response);
+                       timer_response = NULL;
+               }
+               if (timer_mutex) {
+                       CloseHandle(timer_mutex);
+                       timer_mutex = NULL;
+               }
+       }
+
+       if (r != LIBUSB_SUCCESS)
+               --concurrent_usage; // Not expected to call libusb_exit if we failed.
+
+       ReleaseSemaphore(semaphore, 1, NULL);   // increase count back to 1
+       CloseHandle(semaphore);
+       return r;
+}
+
+/*
+ * Initialize device structure, including active config
+ */
+static int initialize_device(struct libusb_device *dev, libusb_bus_t busnum,
+       libusb_devaddr_t devaddr, char *path, int connection_index, uint8_t active_config,
+       struct libusb_device *parent_dev)
+{
+       struct windows_device_priv *priv = __device_priv(dev);
+
+       windows_device_priv_init(priv);
+
+       dev->bus_number = busnum;
+       dev->device_address = devaddr;
+       priv->path = path;
+       priv->connection_index = connection_index;
+       priv->parent_dev = parent_dev;
+
+       priv->active_config = active_config;
+
+       if (priv->active_config != 0) {
+               usbi_dbg("active config: %d", priv->active_config);
+       } else {
+               // USB devices that don't have a config value are usually missing a driver
+               // TODO (after first official release): use this for automated driver installation
+               // NB: SetupDiGetDeviceRegistryProperty w/ SPDRP_INSTALL_STATE would tell us
+               // if the driver is properly installed, but driverless devices don't seem to
+               // be enumerable by SetupDi...
+               usbi_dbg("* DRIVERLESS DEVICE *");
+       }
+
+       return LIBUSB_SUCCESS;
+}
+
+/*
+ * HCD (root) hubs need to have their device descriptor manually populated
+ *
+ * Note that we follow the Linux convention and use the "Linux Foundation root hub"
+ * vendor ID as well as the product ID to indicate the hub speed
+ */
+static int force_hcd_device_descriptor(struct libusb_device *dev, HANDLE handle)
+{
+       DWORD size;
+       USB_HUB_CAPABILITIES hub_caps;
+       USB_HUB_CAPABILITIES_EX hub_caps_ex;
+       struct windows_device_priv *priv = __device_priv(dev);
+       struct libusb_context *ctx = DEVICE_CTX(dev);
+
+       priv->dev_descriptor.bLength = sizeof(USB_DEVICE_DESCRIPTOR);
+       priv->dev_descriptor.bDescriptorType = USB_DEVICE_DESCRIPTOR_TYPE;
+       dev->num_configurations = priv->dev_descriptor.bNumConfigurations = 1;
+
+       // 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,
+                       size, &hub_caps_ex, size, &size, NULL)) {
+                       // Sanity check. HCD hub should always be root
+                       if (!hub_caps_ex.CapabilityFlags.HubIsRoot) {
+                               usbi_warn(ctx, "program assertion failed - HCD hub is not reported as root hub.");
+                       }
+                       priv->dev_descriptor.idProduct = hub_caps_ex.CapabilityFlags.HubIsHighSpeedCapable?2:1;
+               }
+       } else {
+               size = sizeof(USB_HUB_CAPABILITIES);
+               if (!DeviceIoControl(handle, IOCTL_USB_GET_HUB_CAPABILITIES, &hub_caps,
+                       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));
+                       priv->dev_descriptor.idProduct = 1;     // Indicate 1x speed
+               } else {
+                       priv->dev_descriptor.idProduct = hub_caps.HubIs2xCapable?2:1;
+               }
+       }
+
+       return LIBUSB_SUCCESS;
+}
+
+/*
+ * fetch and cache all the config descriptors through I/O
+ */
+static int cache_config_descriptors(struct libusb_device *dev, HANDLE hub_handle)
+{
+       DWORD size, ret_size;
+       struct libusb_context *ctx = DEVICE_CTX(dev);
+       struct windows_device_priv *priv = __device_priv(dev);
+       int r;
+       uint8_t i;
+
+       USB_CONFIGURATION_DESCRIPTOR_SHORT cd_buf_short;    // dummy request
+       PUSB_DESCRIPTOR_REQUEST cd_buf_actual = NULL;       // actual request
+       PUSB_CONFIGURATION_DESCRIPTOR cd_data = NULL;
+
+       if (dev->num_configurations == 0)
+               return LIBUSB_ERROR_INVALID_PARAM;
+
+       priv->config_descriptor = malloc(dev->num_configurations * sizeof(PUSB_CONFIGURATION_DESCRIPTOR));
+       if (priv->config_descriptor == NULL)
+               return LIBUSB_ERROR_NO_MEM;
+       for (i=0; i<dev->num_configurations; i++)
+               priv->config_descriptor[i] = NULL;
+
+       for (i=0, r=LIBUSB_SUCCESS; ; i++)
+       {
+               // safe loop: release all dynamic resources
+               safe_free(cd_buf_actual);
+
+               // safe loop: end of loop condition
+               if ((i >= dev->num_configurations) || (r != LIBUSB_SUCCESS))
+                       break;
+
+               size = sizeof(USB_CONFIGURATION_DESCRIPTOR_SHORT);
+               memset(&cd_buf_short, 0, size);
+
+               cd_buf_short.req.ConnectionIndex = priv->connection_index;
+               cd_buf_short.req.SetupPacket.bmRequest = LIBUSB_ENDPOINT_IN;
+               cd_buf_short.req.SetupPacket.bRequest = USB_REQUEST_GET_DESCRIPTOR;
+               cd_buf_short.req.SetupPacket.wValue = (USB_CONFIGURATION_DESCRIPTOR_TYPE << 8) | i;
+               cd_buf_short.req.SetupPacket.wIndex = i;
+               cd_buf_short.req.SetupPacket.wLength = (USHORT)(size - sizeof(USB_DESCRIPTOR_REQUEST));
+
+               // Dummy call to get the required data size
+               if (!DeviceIoControl(hub_handle, IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, &cd_buf_short, size,
+                       &cd_buf_short, size, &ret_size, NULL)) {
+                       usbi_err(ctx, "could not access configuration descriptor (dummy): %s", windows_error_str(0));
+                       LOOP_BREAK(LIBUSB_ERROR_IO);
+               }
+
+               if ((ret_size != size) || (cd_buf_short.data.wTotalLength < sizeof(USB_CONFIGURATION_DESCRIPTOR))) {
+                       usbi_err(ctx, "unexpected configuration descriptor size (dummy).");
+                       LOOP_BREAK(LIBUSB_ERROR_IO);
+               }
+
+               size = sizeof(USB_DESCRIPTOR_REQUEST) + cd_buf_short.data.wTotalLength;
+               if ((cd_buf_actual = (PUSB_DESCRIPTOR_REQUEST)malloc(size)) == NULL) {
+                       usbi_err(ctx, "could not allocate configuration descriptor buffer. aborting.");
+                       LOOP_BREAK(LIBUSB_ERROR_NO_MEM);
+               }
+               memset(cd_buf_actual, 0, size);
+
+               // Actual call
+               cd_buf_actual->ConnectionIndex = priv->connection_index;
+               cd_buf_actual->SetupPacket.bmRequest = LIBUSB_ENDPOINT_IN;
+               cd_buf_actual->SetupPacket.bRequest = USB_REQUEST_GET_DESCRIPTOR;
+               cd_buf_actual->SetupPacket.wValue = (USB_CONFIGURATION_DESCRIPTOR_TYPE << 8) | i;
+               cd_buf_actual->SetupPacket.wIndex = i;
+               cd_buf_actual->SetupPacket.wLength = (USHORT)(size - sizeof(USB_DESCRIPTOR_REQUEST));
+
+               if (!DeviceIoControl(hub_handle, IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, cd_buf_actual, size,
+                       cd_buf_actual, size, &ret_size, NULL)) {
+                       usbi_err(ctx, "could not access configuration descriptor (actual): %s", windows_error_str(0));
+                       LOOP_BREAK(LIBUSB_ERROR_IO);
+               }
+
+               cd_data = (PUSB_CONFIGURATION_DESCRIPTOR)((UCHAR*)cd_buf_actual+sizeof(USB_DESCRIPTOR_REQUEST));
+
+               if ((size != ret_size) || (cd_data->wTotalLength != cd_buf_short.data.wTotalLength)) {
+                       usbi_err(ctx, "unexpected configuration descriptor size (actual).");
+                       LOOP_BREAK(LIBUSB_ERROR_IO);
+               }
+
+               if (cd_data->bDescriptorType != USB_CONFIGURATION_DESCRIPTOR_TYPE) {
+                       usbi_err(ctx, "not a configuration descriptor");
+                       LOOP_BREAK(LIBUSB_ERROR_IO);
+               }
+
+               usbi_dbg("cached config descriptor #%d (%d bytes)", i+1, cd_data->wTotalLength);
+
+               // Sanity check. Ensures that indexes for our list of config desc is in the right order
+               if (i != (cd_data->bConfigurationValue-1)) {
+                       usbi_warn(ctx, "program assertion failed - config descriptors are being read out of order");
+                       continue;
+               }
+
+               // Cache the descriptor
+               priv->config_descriptor[i] = malloc(cd_data->wTotalLength);
+               if (priv->config_descriptor[i] == NULL)
+                       return LIBUSB_ERROR_NO_MEM;
+
+               memcpy(priv->config_descriptor[i], cd_data, cd_data->wTotalLength);
+       }
+       return LIBUSB_SUCCESS;
+}
+
+/*
+ * Recursively enumerates and finds all hubs & devices
+ */
+static int usb_enumerate_hub(struct libusb_context *ctx, struct discovered_devs **_discdevs,
+       HANDLE hub_handle, libusb_bus_t busnum, struct libusb_device *parent_dev, uint8_t nb_ports)
+{
+       struct discovered_devs *discdevs = *_discdevs;
+       struct libusb_device *dev = NULL;
+       DWORD size, size_initial, size_fixed, getname_ioctl;
+       HANDLE handle = INVALID_HANDLE_VALUE;
+       USB_HUB_NAME_FIXED s_hubname;
+       USB_NODE_CONNECTION_INFORMATION conn_info;
+       USB_NODE_INFORMATION hub_node;
+       bool is_hcd, need_unref = false;
+       int i, r;
+       LPCWSTR wstr;
+       char *tmp_str = NULL, *path_str = NULL;
+       unsigned long session_id;
+       libusb_devaddr_t devaddr = 0;
+       struct windows_device_priv *priv, *parent_priv;
+
+       // obviously, root (HCD) hubs have no parent
+       is_hcd = (parent_dev == NULL);
+       if (is_hcd)
+       {
+               if (nb_ports != 1) {
+                       usbi_warn(ctx, "program assertion failed - invalid number of ports for HCD.");
+                       return LIBUSB_ERROR_INVALID_PARAM;
+               }
+               parent_priv = NULL;
+               size_initial = sizeof(USB_ROOT_HUB_NAME);
+               size_fixed = sizeof(USB_ROOT_HUB_NAME_FIXED);
+               getname_ioctl = IOCTL_USB_GET_ROOT_HUB_NAME;
+       }
+       else
+       {
+               parent_priv = __device_priv(parent_dev);
+               size_initial = sizeof(USB_NODE_CONNECTION_NAME);
+               size_fixed = sizeof(USB_NODE_CONNECTION_NAME_FIXED);
+               getname_ioctl = IOCTL_USB_GET_NODE_CONNECTION_NAME;
+       }
+
+       // Loop through all the ports on this hub
+       for (i = 1, r = LIBUSB_SUCCESS; ; i++)
+       {
+               // safe loop: release all dynamic resources
+               if (need_unref) {
+                       safe_unref_device(dev);
+                       need_unref = false;
+               }
+               safe_free(tmp_str);
+               safe_free(path_str);
+               safe_closehandle(handle);
+
+               // safe loop: end of loop condition
+               if ((i > nb_ports) || (r != LIBUSB_SUCCESS))
+                       break;
+
+               memset(&conn_info, 0, sizeof(conn_info));
+               // For non HCDs, check if the node on this port is a hub or a regular device
+               if (!is_hcd) {
+                       size = sizeof(USB_NODE_CONNECTION_INFORMATION);
+                       conn_info.ConnectionIndex = i;
+                       if (!DeviceIoControl(hub_handle, IOCTL_USB_GET_NODE_CONNECTION_INFORMATION, &conn_info, size,
+                               &conn_info, size, &size, NULL)) {
+                               usbi_warn(ctx, "could not get node connection information: %s", windows_error_str(0));
+                               continue;
+                       }
+
+                       if (conn_info.ConnectionStatus == NoDeviceConnected) {
+                               continue;
+                       }
+
+                       if (conn_info.DeviceAddress == LIBUSB_DEVADDR_MAX) {
+                               usbi_warn(ctx, "program assertion failed - device address is %d "
+                                       "(conflicts with root hub), ignoring device", LIBUSB_DEVADDR_MAX);
+                               continue;
+                       }
+
+                       s_hubname.u.node.ConnectionIndex = i;   // Only used for non HCDs (s_hubname is an union)
+               }
+               else
+               {
+                       // HCDs have only 1 node, and it's always a hub
+                       conn_info.DeviceAddress = LIBUSB_DEVADDR_MAX;   // using 0 can conflict with driverless devices
+                       conn_info.DeviceIsHub = true;
+                       conn_info.CurrentConfigurationValue = 1;
+               }
+
+               // If this node is a hub (HCD or not), open it
+               if (conn_info.DeviceIsHub) {
+                       size = size_initial;
+                       if (!DeviceIoControl(hub_handle, getname_ioctl, &s_hubname, size,
+                               &s_hubname, size, &size, NULL)) {
+                               usbi_warn(ctx, "could not get hub path (dummy): %s", windows_error_str(0));
+                               continue;
+                       }
+
+                       size = is_hcd?s_hubname.u.root.ActualLength:s_hubname.u.node.ActualLength;
+                       if (size > size_fixed) {
+                               usbi_warn(ctx, "program assertion failed - hub path is too long");
+                               continue;
+                       }
+
+                       if (!is_hcd) {
+                               // previous call trashes some of the data
+                               s_hubname.u.node.ConnectionIndex = i;
+                       }
+                       if (!DeviceIoControl(hub_handle, getname_ioctl, &s_hubname, size,
+                               &s_hubname, size, &size, NULL)) {
+                               usbi_warn(ctx, "could not get hub path (actual): %s", windows_error_str(0));
+                               continue;
+                       }
+
+                       // Add prefix
+                       wstr = is_hcd?s_hubname.u.root.RootHubName:s_hubname.u.node.NodeName;
+                       tmp_str = wchar_to_utf8(wstr);
+                       if (tmp_str == NULL) {
+                               usbi_err(ctx, "could not convert hub path string.");
+                               LOOP_BREAK(LIBUSB_ERROR_NO_MEM);
+                       }
+
+                       path_str = sanitize_path(tmp_str);
+                       if (path_str == NULL) {
+                               usbi_err(ctx, "could not sanitize hub path string.");
+                               LOOP_BREAK(LIBUSB_ERROR_NO_MEM);
+                       }
+
+                       // Open Hub
+                       handle = CreateFileA(path_str, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
+                               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;
+                       }
+               }
+
+               // Generate a session ID
+               // Will need to change the session_id computation if this assertion fails
+               if (conn_info.DeviceAddress > LIBUSB_DEVADDR_MAX) {
+                       usbi_warn(ctx, "program assertion failed - device address is greater than %d, ignoring device",
+                               LIBUSB_DEVADDR_MAX);
+                       continue;
+               } else {
+                       devaddr = (uint8_t)conn_info.DeviceAddress;
+               }
+               // Same trick as linux for session_id, with same caveat
+               session_id = busnum << (sizeof(libusb_devaddr_t)*8) | devaddr;
+               usbi_dbg("busnum %d devaddr %d session_id %ld", busnum, devaddr, session_id);
+
+               // Allocate device if needed
+               dev = usbi_get_device_by_session_id(ctx, session_id);
+               if (dev) {
+                       usbi_dbg("using existing device for session %ld", session_id);
+                       priv = __device_priv(dev);
+                       // Because we are rebuilding the list, there's no guarantee
+                       // the parent device pointer is still the same.
+                       // Other device data should still be reusable
+                       priv->parent_dev = parent_dev;
+               } else {
+                       usbi_dbg("allocating new device for session %ld", session_id);
+                       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));
+                       priv = __device_priv(dev);
+
+                       path_str = NULL;        // protect our path from being freed
+
+                       // Setup the cached descriptors. Note that only non HCDs can fetch descriptors
+                       if (!is_hcd) {
+                               // The device descriptor has been read with conn_info
+                               memcpy(&priv->dev_descriptor, &(conn_info.DeviceDescriptor), sizeof(USB_DEVICE_DESCRIPTOR));
+                               dev->num_configurations = priv->dev_descriptor.bNumConfigurations;
+                               // If we can't read the config descriptors, just set the number of confs to zero
+                               if (cache_config_descriptors(dev, hub_handle) != LIBUSB_SUCCESS) {
+                                       dev->num_configurations = 0;
+                                       priv->dev_descriptor.bNumConfigurations = 0;
+                               }
+                       } else {
+                               LOOP_CHECK(force_hcd_device_descriptor(dev, handle));
+                       }
+                       LOOP_CHECK(usbi_sanitize_device(dev));
+               }
+
+               discdevs = discovered_devs_append(*_discdevs, dev);
+               if (!discdevs) {
+                       LOOP_BREAK(LIBUSB_ERROR_NO_MEM);
+               }
+
+               *_discdevs = discdevs;
+
+               // Finally, if device is a hub, recurse
+               if (conn_info.DeviceIsHub) {
+                       // Find number of ports for this hub
+                       size =  sizeof(USB_NODE_INFORMATION);
+                       if (!DeviceIoControl(handle, IOCTL_USB_GET_NODE_INFORMATION, &hub_node, size,
+                               &hub_node, size, &size, NULL)) {
+                               usbi_warn(ctx, "could not retreive information for hub %s: %s",
+                                       priv->path, windows_error_str(0));
+                               continue;
+                       }
+
+                       if (hub_node.NodeType != UsbHub) {
+                               usbi_warn(ctx, "unexpected hub type (%d) for hub %s", hub_node.NodeType, priv->path);
+                               continue;
+                       }
+
+                       usbi_dbg("%d ports Hub: %s", hub_node.u.HubInformation.HubDescriptor.bNumberOfPorts, priv->path);
+
+                       usb_enumerate_hub(ctx, _discdevs, handle, busnum, dev,
+                               hub_node.u.HubInformation.HubDescriptor.bNumberOfPorts);
+               }
+       }
+
+       return r;
+}
+
+/*
+ * Composite device interfaces are not enumerated using GUID_DEVINTERFACE_USB_DEVICE,
+ * but instead require a different lookup mechanism
+ */
+static int set_composite_device(struct libusb_context *ctx, DEVINST devinst, struct windows_device_priv *priv)
+{
+// indexes for additional interface GUIDs, not available from "USB"
+// SetupDiGetClassDevs enumeration should go here. Typically, these are
+// device interfaces that begin with something else than "\\?\usb\"
+enum libusb_hid_report_type {
+       HID_DEVICE_INTERFACE_GUID_INDEX   = 0,
+       MAX_DEVICE_INTERFACE_GUID_INDEX   = 1
+};
+
+       DEVINST child_devinst, parent_devinst;
+       unsigned i, j, max_guids, nb_paths, interface_number;
+       uint8_t api;
+       bool found;
+       DWORD type, size;
+       CONFIGRET r;
+       HDEVINFO dev_info;
+       SP_DEVINFO_DATA dev_info_data;
+       SP_DEVICE_INTERFACE_DETAIL_DATA *dev_interface_details = NULL;
+       HKEY key;
+       WCHAR guid_string_w[MAX_GUID_STRING_LENGTH];
+       GUID guid, class_guid;
+       GUID guid_table[MAX_USB_DEVICES];
+       char* sanitized_path[MAX_USB_DEVICES];
+       char* hid_path[MAX_USB_DEVICES]; // An extra path is needed for HID
+       uint8_t api_type[MAX_USB_DEVICES];
+       char* sanitized_short = NULL;
+       char path[MAX_PATH_LENGTH];
+       char driver[MAX_KEY_LENGTH];
+
+       dev_info = SetupDiGetClassDevs(NULL, "USB", NULL, DIGCF_PRESENT|DIGCF_ALLCLASSES);
+       if (dev_info == INVALID_HANDLE_VALUE) {
+               return LIBUSB_ERROR_NOT_FOUND;
+       }
+
+       // Manually add the HID GUID as it cannot be read with DeviceInterfaceGUIDs reg key)
+       // NB the value returned by HidD_GetHidGuid, which is for interface class is different
+       // from GUID_HID, which is the device class GUID
+       HidD_GetHidGuid(&guid_table[HID_DEVICE_INTERFACE_GUID_INDEX]);
+       // NB: for other interface guids, SetupDiClassGuidsFromName can be used
+       max_guids = MAX_DEVICE_INTERFACE_GUID_INDEX;
+
+       // First, retrieve all the device interface GUIDs
+       for (i = 0; ; i++)
+       {
+               dev_info_data.cbSize = sizeof(dev_info_data);
+               if (!SetupDiEnumDeviceInfo(dev_info, i, &dev_info_data)) {
+                       break;
+               }
+
+               key = SetupDiOpenDevRegKey(dev_info, &dev_info_data, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ);
+               if (key == INVALID_HANDLE_VALUE) {
+                       usbi_dbg("could not open registry key");
+                       continue;
+               }
+
+               size = sizeof(guid_string_w);
+               r = RegQueryValueExW(key, L"DeviceInterfaceGUIDs", NULL, &type,
+                       (BYTE*)guid_string_w, &size);
+               RegCloseKey(key);
+               if (r != ERROR_SUCCESS) {
+                       continue;
+               }
+               CLSIDFromString(guid_string_w, &guid);
+
+               // identical device interface GUIDs are not supposed to happen, but are a real possibility
+               // => check and ignore duplicates
+               found = false;
+               for (j=0; j<max_guids; j++) {
+                       if (memcmp(&guid_table[j], &guid, sizeof(GUID)) == 0) {
+                               found = true;
+                               break;
+                       }
+               }
+               if (!found) {
+                       guid_table[max_guids++] = guid;
+                       if (max_guids > MAX_USB_DEVICES) {
+                               usbi_warn(ctx, "more than %d devices - ignoring the rest", MAX_USB_DEVICES);
+                               break;
+                       }
+               }
+       }
+       SetupDiDestroyDeviceInfoList(dev_info);
+
+       // Now let's find the device interface paths for all these devices
+       nb_paths = 0;
+       for (j=0; j<max_guids; j++)
+       {
+               guid = guid_table[j];
+
+               for (i = 0; ; i++)
+               {
+                       safe_free(dev_interface_details);
+                       dev_interface_details = get_interface_details(ctx, &dev_info, &dev_info_data, guid, i);
+                       if (dev_interface_details == NULL)
+                               break;
+
+                       // HID devices (and possibly other classes) have an extra indirection
+                       // for an USB path we can recognize
+                       if (j == HID_DEVICE_INTERFACE_GUID_INDEX) {
+                               if (CM_Get_Parent(&parent_devinst, dev_info_data.DevInst, 0) != CR_SUCCESS) {
+                                       usbi_warn(ctx, "could not retrieve HID parent info data for device %s, skipping: %s",
+                                               dev_interface_details->DevicePath, windows_error_str(0));
+                                       continue;
+                               }
+
+                               if (CM_Get_Device_ID(parent_devinst, path, MAX_PATH_LENGTH, 0) != CR_SUCCESS) {
+                                       usbi_warn(ctx, "could not retrieve HID parent's path for device %s, skipping: %s",
+                                               dev_interface_details->DevicePath, windows_error_str(0));
+                                       continue;
+                               }
+                       }
+
+                       // In case we can't read the driver string through SPDRP_SERVICE (which is
+                       // the case for HID), we need the ClassGUID for comparison.
+                       if(!SetupDiGetDeviceRegistryPropertyW(dev_info, &dev_info_data, SPDRP_CLASSGUID,
+                               NULL, (BYTE*)guid_string_w, sizeof(guid_string_w), &size)) {
+                               usbi_warn(ctx, "could not read class GUID for device %s, skipping: %s",
+                                       dev_interface_details->DevicePath, windows_error_str(0));
+                               continue;
+                       }
+                       CLSIDFromString(guid_string_w, &class_guid);
+
+                       // Attempt to read the driver string
+                       if(!SetupDiGetDeviceRegistryProperty(dev_info, &dev_info_data, SPDRP_SERVICE,
+                               NULL, (BYTE*)driver, MAX_KEY_LENGTH, &size)) {
+                               driver[0] = 0;
+                       }
+
+                       for (api=USB_API_WINUSB; api<USB_API_MAX; api++) {
+                               if ( (is_api_driver(driver, api))
+                                 || (guid_eq(&class_guid, usb_api_backend[api].class_guid)) ) {
+                                       api_type[nb_paths] = api;
+                                       if (j == HID_DEVICE_INTERFACE_GUID_INDEX) {
+                                               hid_path[nb_paths] = sanitize_path(path);
+                                       } else {
+                                               hid_path[nb_paths] = NULL;
+                                       }
+                                       sanitized_path[nb_paths++] = sanitize_path(dev_interface_details->DevicePath);
+                                       if (nb_paths > MAX_USB_DEVICES) {
+                                               usbi_warn(ctx, "more than %d devices - ignoring the rest", MAX_USB_DEVICES);
+                                               break;
+                                       }
+                               }
+                       }
+               }
+       }
+
+       // Finally, match the interface paths with the interfaces. We do that
+       // by looking at the children of the composite device
+       // NB: if the interfaces are not found in their expected position,
+       // claim_interface will issue a warning
+       found = false;
+       memset(&child_devinst, 0, sizeof(DEVINST));     // prevents /W4 warning
+       for (i = 0; i<USB_MAXINTERFACES; i++)
+       {
+               if (i == 0) {
+                       r = CM_Get_Child(&child_devinst, devinst, 0);
+               } else {
+                       r = CM_Get_Sibling(&child_devinst, child_devinst, 0);
+               }
+               if (r == CR_NO_SUCH_DEVNODE) {  // end of the siblings
+                       break;
+               } else if (r != CR_SUCCESS) {
+                       usbi_dbg("unable to find interface sibling #%d, error = %X", i, r);
+                       break;
+               }
+
+               r = CM_Get_Device_ID(child_devinst, path, MAX_PATH_LENGTH, 0);
+               if (r != CR_SUCCESS) {
+                       usbi_err(ctx, "could not retrieve simple path for interface %d: CR error %d",
+                               i, r);
+                       continue;
+               }
+               sanitized_short = sanitize_path(path);
+               if (sanitized_short == NULL) {
+                       usbi_err(ctx, "could not sanitize path for interface %d", i);
+                       continue;
+               }
+
+               // Because MI_## are not necessarily in sequential order (some composite HID
+               // devices will have only MI_00 & MI_03 for instance), we retrieve the actual
+               // interface number from the path's MI value
+               interface_number = i;
+               for (j=0; sanitized_short[j] != 0; ) {
+                       if ( (sanitized_short[j++] == 'M') && (sanitized_short[j++] == 'I')
+                         && (sanitized_short[j++] == '_') ) {
+                               interface_number = (sanitized_short[j++] - '0')*10;
+                               interface_number += sanitized_short[j] - '0';
+                               break;
+                       }
+               }
+               if (sanitized_short[j] == 0) {
+                       usbi_warn(ctx, "failure to read interface number for %s. Using default value %d",
+                               sanitized_short, interface_number);
+               }
+
+               for (j=0; j<nb_paths; j++) {
+                       if ( (safe_strncmp(sanitized_path[j], sanitized_short, strlen(sanitized_short)) == 0)
+                         || (safe_strcmp(hid_path[j], sanitized_short) == 0 ) ) {
+                               // HID devices can have multiple collections (COL##) for each MI_## interface
+                               if (priv->usb_interface[interface_number].path != NULL) {
+                                       usbi_dbg("interface_path[%d] already set - ignoring HID collection: %s",
+                                               interface_number, sanitized_path[j]);
+                                       if (api_type[j] != USB_API_HID) {
+                                               usbi_warn(ctx, "program assertion failed - not an HID collection");
+                                       }
+                               } else {
+                                       priv->usb_interface[interface_number].path = sanitized_path[j];
+                                       priv->usb_interface[interface_number].apib = &usb_api_backend[api_type[j]];
+                                       if ((api_type[j] == USB_API_HID) && (priv->hid == NULL)) {
+                                                       priv->hid = calloc(1, sizeof(struct hid_device_priv));
+                                       }
+                                       priv->composite_api_flags |= 1<<api_type[j];
+                                       sanitized_path[j] = NULL;
+                               }
+                       }
+               }
+               safe_free(sanitized_short);
+
+               if (priv->usb_interface[interface_number].path == NULL) {
+                       usbi_warn(ctx, "interface_path[%d]: unhandled API - interface will be disabled",
+                               interface_number);
+                       continue;
+               }
+               usbi_dbg("interface_path[%d]: %s", interface_number, priv->usb_interface[interface_number].path);
+               found = true;
+       }
+
+       for (j=0; j<nb_paths; j++) {
+               safe_free(sanitized_path[j]);
+               safe_free(hid_path[j]);
+       }
+
+       if (found == 0) {
+               usbi_warn(ctx, "composite device: no interfaces were found");
+               return LIBUSB_ERROR_NOT_FOUND;
+       }
+
+       return LIBUSB_SUCCESS;
+}
+
+/*
+ * Likewise, HID device interfaces's path (\\.\HID\...) are not enumerated through the
+ * generic USB devices GUID, but are actually children of one such device
+ */
+static int set_hid_device(struct libusb_context *ctx, struct windows_device_priv *priv)
+ {
+       char path[MAX_PATH_LENGTH];
+       char *sanitized_path = NULL;
+       HDEVINFO dev_info;
+       SP_DEVICE_INTERFACE_DETAIL_DATA *dev_interface_details = NULL;
+       SP_DEVINFO_DATA dev_info_data;
+       DEVINST parent_devinst;
+       GUID guid;
+       int     r = LIBUSB_SUCCESS;
+       unsigned i, interface_number;
+
+       interface_number = 0;
+       HidD_GetHidGuid(&guid);
+       for (i = 0; ; i++)
+       {
+               // safe loop: free up any (unprotected) dynamic resource
+               safe_free(dev_interface_details);
+               safe_free(sanitized_path);
+
+               dev_interface_details = get_interface_details(ctx, &dev_info, &dev_info_data, guid, i);
+               // safe loop: end of loop condition
+               if ( (dev_interface_details == NULL)
+                 || (r != LIBUSB_SUCCESS) )
+                       break;
+
+               // Retrieve parent's path using PnP Configuration Manager (CM)
+               if (CM_Get_Parent(&parent_devinst, dev_info_data.DevInst, 0) != CR_SUCCESS) {
+                       usbi_warn(ctx, "could not retrieve parent info data for device %s, skipping: %s",
+                               dev_interface_details->DevicePath, windows_error_str(0));
+                       continue;
+               }
+
+               if (CM_Get_Device_ID(parent_devinst, path, MAX_PATH_LENGTH, 0) != CR_SUCCESS) {
+                       usbi_warn(ctx, "could not retrieve parent's path for device %s, skipping: %s",
+                               dev_interface_details->DevicePath, windows_error_str(0));
+                       continue;
+               }
+
+               // Fix parent's path inconsistencies before attempting to compare
+               sanitized_path = sanitize_path(path);
+               if (sanitized_path == NULL) {
+                       usbi_warn(ctx, "could not sanitize parent's path for device %s, skipping.",
+                               dev_interface_details->DevicePath);
+                       continue;
+               }
+
+               // NB: we compare strings of different lengths below => strncmp
+               if (safe_strncmp(priv->path, sanitized_path, strlen(sanitized_path)) == 0) {
+                       priv->usb_interface[interface_number].path = sanitize_path(dev_interface_details->DevicePath);
+                       priv->usb_interface[interface_number].apib = &usb_api_backend[USB_API_HID];
+                       usbi_dbg("interface_path[%d]: %s", interface_number, priv->usb_interface[interface_number].path);
+                       interface_number++;
+               }
+       }
+
+       return LIBUSB_SUCCESS;
+}
+
+/*
+ * This function retrieves and sets the paths of all non-hub devices
+ * NB: No I/O with device is required during this call
+ */
+static int set_device_paths(struct libusb_context *ctx, struct discovered_devs *discdevs)
+{
+       struct windows_device_priv *priv;
+       struct windows_device_priv *parent_priv;
+       char path[MAX_PATH_LENGTH];
+       char reg_key[MAX_KEY_LENGTH];
+       char *sanitized_path = NULL;
+       HDEVINFO dev_info;
+       SP_DEVICE_INTERFACE_DETAIL_DATA *dev_interface_details = NULL;
+       SP_DEVINFO_DATA dev_info_data;
+       DEVINST parent_devinst;
+       GUID guid;
+       DWORD size, reg_type, install_state, port_nr;
+       int     r = LIBUSB_SUCCESS;
+       unsigned i, j, k;
+       uint8_t api;
+       bool found;
+
+       // TODO (after first official release): MI_## automated driver installation
+       guid = GUID_DEVINTERFACE_USB_DEVICE;
+       for (i = 0; ; i++)
+       {
+               // safe loop: free up any (unprotected) dynamic resource
+               safe_free(dev_interface_details);
+               safe_free(sanitized_path);
+
+               dev_interface_details = get_interface_details(ctx, &dev_info, &dev_info_data, guid, i);
+               // safe loop: end of loop condition
+               if ( (dev_interface_details == NULL)
+                 || (r != LIBUSB_SUCCESS) )
+                       break;
+
+               // Check that the driver installation is OK
+               if ( (!SetupDiGetDeviceRegistryProperty(dev_info, &dev_info_data, SPDRP_INSTALL_STATE,
+                       &reg_type, (BYTE*)&install_state, 4, &size))
+                 && (size != 4) ){
+                       usbi_warn(ctx, "could not detect installation state of driver for %s: %s",
+                               dev_interface_details->DevicePath, windows_error_str(0));
+               } else if (install_state != 0) {
+                       usbi_warn(ctx, "driver for device %s is reporting an issue (code: %d) - skipping",
+                               dev_interface_details->DevicePath, install_state);
+                       continue;
+               }
+
+               // The SPDRP_ADDRESS for USB devices should be the device port number on the hub
+               if ( (!SetupDiGetDeviceRegistryProperty(dev_info, &dev_info_data, SPDRP_ADDRESS,
+                       &reg_type, (BYTE*)&port_nr, 4, &size))
+                 && (size != 4) ){
+                       usbi_warn(ctx, "could not retrieve port number for device %s, skipping: %s",
+                               dev_interface_details->DevicePath, windows_error_str(0));
+                       continue;
+               }
+
+               // Retrieve parent's path using PnP Configuration Manager (CM)
+               if (CM_Get_Parent(&parent_devinst, dev_info_data.DevInst, 0) != CR_SUCCESS) {
+                       usbi_warn(ctx, "could not retrieve parent info data for device %s, skipping: %s",
+                               dev_interface_details->DevicePath, windows_error_str(0));
+                       continue;
+               }
+
+               if (CM_Get_Device_ID(parent_devinst, path, MAX_PATH_LENGTH, 0) != CR_SUCCESS) {
+                       usbi_warn(ctx, "could not retrieve parent's path for device %s, skipping: %s",
+                               dev_interface_details->DevicePath, windows_error_str(0));
+                       continue;
+               }
+
+               // Fix parent's path inconsistencies before attempting to compare
+               sanitized_path = sanitize_path(path);
+               if (sanitized_path == NULL) {
+                       usbi_warn(ctx, "could not sanitize parent's path for device %s, skipping.",
+                               dev_interface_details->DevicePath);
+                       continue;
+               }
+
+               // With the parent path and port number, we should be able to locate our device
+               // by comparing these values to the ones we got when enumerating hubs
+               found = false;
+               for (j=0; j<discdevs->len; j++) {
+                       priv = __device_priv(discdevs->devices[j]);
+
+                       if (priv->parent_dev == NULL) {
+                               continue;       // ignore HCDs
+                       }
+
+                       parent_priv = __device_priv(priv->parent_dev);
+
+                       // NB: we compare strings of different lengths below => strncmp
+                       if ( (safe_strncmp(parent_priv->path, sanitized_path, strlen(sanitized_path)) == 0)
+                         && (port_nr == priv->connection_index) ) {
+
+                               priv->path = sanitize_path(dev_interface_details->DevicePath);
+
+                               usbi_dbg("path (%d:%d): %s", discdevs->devices[j]->bus_number,
+                                       discdevs->devices[j]->device_address, priv->path);
+
+                               // Check the service name to know what kind of device we have.
+                               // The service name is really the driver name without ".sys" ("WinUSB", "HidUsb", ...)
+                               // It tells us if we can use WinUSB, if we have a composite device, and the API to use
+                               if(!SetupDiGetDeviceRegistryProperty(dev_info, &dev_info_data, SPDRP_SERVICE,
+                                       &reg_type, (BYTE*)reg_key, MAX_KEY_LENGTH, &size)) {
+                                       usbi_err(ctx, "could not retrieve driver information for device %s, skipping: %s",
+                                               dev_interface_details->DevicePath, windows_error_str(0));
+                                       break;
+                               }
+
+                               usbi_dbg("driver: %s", reg_key);
+                               found = true;
+
+                               for (api = 0; api<USB_API_MAX; api++) {
+                                       if (is_api_driver(reg_key, api)) {
+                                               priv->apib = &usb_api_backend[api];
+                                               switch(api) {
+                                               case USB_API_COMPOSITE:
+                                                       set_composite_device(ctx, dev_info_data.DevInst, priv);
+                                                       break;
+                                               case USB_API_HID:
+                                                       safe_free(priv->hid);
+                                                       priv->hid = calloc(1, sizeof(struct hid_device_priv));
+                                                       if (priv->hid == NULL) {
+                                                               usbi_err(ctx, "could not allocate HID data for %s, skipping",
+                                                                       dev_interface_details->DevicePath);
+                                                               priv->apib = &usb_api_backend[USB_API_UNSUPPORTED];
+                                                               safe_free(priv->path);
+                                                       } else {
+                                                               set_hid_device(ctx, priv);
+                                                       }
+                                                       break;
+                                               default:
+                                                       // For other devices, the first interface is the same as the device
+                                                       priv->usb_interface[0].path = malloc(safe_strlen(priv->path));
+                                                       if (priv->usb_interface[0].path != NULL) {
+                                                               safe_strcpy(priv->usb_interface[0].path, safe_strlen(priv->path), priv->path);
+                                                       }
+                                                       // The following is needed if we want to API calls to work for both simple
+                                                       // and composite devices, as
+                                                       for(k=0; k<USB_MAXINTERFACES; k++) {
+                                                               priv->usb_interface[k].apib = &usb_api_backend[api];
+                                                       }
+                                                       break;
+                                               }
+                                       }
+                               }
+                               break;
+                       }
+               }
+               if (!found) {
+                       usbi_warn(ctx, "could not match %s with a libusb device.", dev_interface_details->DevicePath);
+                       continue;
+               }
+       }
+
+       return LIBUSB_SUCCESS;
+}
+
+/*
+ * get_device_list: libusb backend device enumeration function
+ */
+static int windows_get_device_list(struct libusb_context *ctx, struct discovered_devs **_discdevs)
+{
+       struct windows_hcd_priv* hcd;
+       HANDLE handle = INVALID_HANDLE_VALUE;
+       int r = LIBUSB_SUCCESS;
+       libusb_bus_t bus;
+
+       // Use the index of the HCD in the chained list as bus #
+       for (hcd = hcd_root, bus = 0; ; hcd = hcd->next, bus++)
+       {
+               safe_closehandle(handle);
+
+               if ( (hcd == NULL) || (r != LIBUSB_SUCCESS) )
+                       break;
+
+               if (bus == LIBUSB_BUS_MAX) {
+                       usbi_warn(ctx, "program assertion failed - got more than %d buses, skipping the rest.", LIBUSB_BUS_MAX);
+                       continue;
+               }
+
+               handle = CreateFileA(hcd->path, GENERIC_WRITE, FILE_SHARE_WRITE,
+                       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;
+               }
+
+               LOOP_CHECK(usb_enumerate_hub(ctx, _discdevs, handle, bus, NULL, 1));
+       }
+
+       // Set the interface path for non-hubs
+       r = set_device_paths(ctx, *_discdevs);
+
+       return r;
+}
+
+/*
+ * exit: libusb backend deinitialization function
+ */
+static void windows_exit(void)
+{
+       struct windows_hcd_priv* hcd_tmp;
+       int i;
+       HANDLE semaphore;
+       TCHAR sem_name[11+1+8]; // strlen(libusb_init)+'\0'+(32-bit hex PID)
+
+       sprintf(sem_name, "libusb_init%08X", (unsigned int)GetCurrentProcessId()&0xFFFFFFFF);
+       semaphore = CreateSemaphore(NULL, 1, 1, sem_name);
+       if (semaphore == NULL) {
+               return;
+       }
+
+       // A successful wait brings our semaphore count to 0 (unsignaled)
+       // => any concurent wait stalls until the semaphore release
+       if (WaitForSingleObject(semaphore, INFINITE) != WAIT_OBJECT_0) {
+               CloseHandle(semaphore);
+               return;
+    }
+
+       // Only works if exits and inits are balanced exactly
+       if (--concurrent_usage < 0) {   // Last exit
+               while (hcd_root != NULL)
+               {
+                       hcd_tmp = hcd_root;     // Keep a copy for free
+                       hcd_root = hcd_root->next;
+                       windows_hcd_priv_release(hcd_tmp);
+                       safe_free(hcd_tmp);
+               }
+
+               for (i=0; i<USB_API_MAX; i++) {
+                       usb_api_backend[i].exit();
+               }
+               exit_polling();
+
+               if (timer_thread) {
+                       SetEvent(timer_request[1]); // actually the signal to quit the thread.
+                       if (WAIT_OBJECT_0 != WaitForSingleObject(timer_thread, INFINITE)) {
+                               usbi_dbg("could not wait for timer thread to quit");
+                               TerminateThread(timer_thread, 1);
+                       }
+                       CloseHandle(timer_thread);
+                       timer_thread = NULL;
+               }
+               for (i = 0; i < 2; i++) {
+                       if (timer_request[i]) {
+                               CloseHandle(timer_request[i]);
+                               timer_request[i] = NULL;
+                       }
+               }
+               if (timer_response) {
+                       CloseHandle(timer_response);
+                       timer_response = NULL;
+               }
+               if (timer_mutex) {
+                       CloseHandle(timer_mutex);
+                       timer_mutex = NULL;
+               }
+       }
+
+       ReleaseSemaphore(semaphore, 1, NULL);   // increase count back to 1
+       CloseHandle(semaphore);
+}
+
+static int windows_get_device_descriptor(struct libusb_device *dev, unsigned char *buffer, int *host_endian)
+{
+       struct windows_device_priv *priv = __device_priv(dev);
+
+       memcpy(buffer, &(priv->dev_descriptor), DEVICE_DESC_LENGTH);
+       *host_endian = 0;
+
+       return LIBUSB_SUCCESS;
+}
+
+static int windows_get_config_descriptor(struct libusb_device *dev, uint8_t config_index, unsigned char *buffer, size_t len, int *host_endian)
+{
+       struct windows_device_priv *priv = __device_priv(dev);
+       PUSB_CONFIGURATION_DESCRIPTOR config_header;
+       size_t size;
+
+       // config index is zero based
+       if (config_index >= dev->num_configurations)
+               return LIBUSB_ERROR_INVALID_PARAM;
+
+       if ((priv->config_descriptor == NULL) || (priv->config_descriptor[config_index] == NULL))
+               return LIBUSB_ERROR_NOT_FOUND;
+
+       config_header = (PUSB_CONFIGURATION_DESCRIPTOR)priv->config_descriptor[config_index];
+
+       size = min(config_header->wTotalLength, len);
+       memcpy(buffer, priv->config_descriptor[config_index], size);
+
+       return LIBUSB_SUCCESS;
+}
+
+/*
+ * return the cached copy of the active config descriptor
+ */
+static int windows_get_active_config_descriptor(struct libusb_device *dev, unsigned char *buffer, size_t len, int *host_endian)
+{
+       struct windows_device_priv *priv = __device_priv(dev);
+
+       if (priv->active_config == 0)
+               return LIBUSB_ERROR_NOT_FOUND;
+
+       // config index is zero based
+       return windows_get_config_descriptor(dev, (uint8_t)(priv->active_config-1), buffer, len, host_endian);
+}
+
+static int windows_open(struct libusb_device_handle *dev_handle)
+{
+       struct windows_device_priv *priv = __device_priv(dev_handle->dev);
+       struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev);
+
+       if (priv->apib == NULL) {
+               usbi_err(ctx, "program assertion failed - device is not initialized");
+               return LIBUSB_ERROR_NO_DEVICE;
+       }
+
+       return priv->apib->open(dev_handle);
+}
+
+static void windows_close(struct libusb_device_handle *dev_handle)
+{
+       struct windows_device_priv *priv = __device_priv(dev_handle->dev);
+
+       priv->apib->close(dev_handle);
+}
+
+static int windows_get_configuration(struct libusb_device_handle *dev_handle, int *config)
+{
+       struct windows_device_priv *priv = __device_priv(dev_handle->dev);
+
+       if (priv->active_config == 0) {
+               *config = 0;
+               return LIBUSB_ERROR_NOT_FOUND;
+       }
+
+       *config = priv->active_config;
+       return LIBUSB_SUCCESS;
+}
+
+/*
+ * from http://msdn.microsoft.com/en-us/library/ms793522.aspx: "The port driver
+ * does not currently expose a service that allows higher-level drivers to set
+ * the configuration."
+ */
+static int windows_set_configuration(struct libusb_device_handle *dev_handle, int config)
+{
+       struct windows_device_priv *priv = __device_priv(dev_handle->dev);
+       int r = LIBUSB_SUCCESS;
+
+       if (config >= USB_MAXCONFIG)
+               return LIBUSB_ERROR_INVALID_PARAM;
+
+       r = libusb_control_transfer(dev_handle, LIBUSB_ENDPOINT_OUT |
+               LIBUSB_REQUEST_TYPE_STANDARD | LIBUSB_RECIPIENT_DEVICE,
+               LIBUSB_REQUEST_SET_CONFIGURATION, (uint16_t)config,
+               0, NULL, 0, 1000);
+
+       if (r == LIBUSB_SUCCESS) {
+               priv->active_config = (uint8_t)config;
+       }
+       return r;
+}
+
+static int windows_claim_interface(struct libusb_device_handle *dev_handle, int iface)
+{
+       int r = LIBUSB_SUCCESS;
+       struct windows_device_priv *priv = __device_priv(dev_handle->dev);
+
+       if (iface >= USB_MAXINTERFACES)
+               return LIBUSB_ERROR_INVALID_PARAM;
+
+       safe_free(priv->usb_interface[iface].endpoint);
+       priv->usb_interface[iface].nb_endpoints= 0;
+
+       r = priv->apib->claim_interface(dev_handle, iface);
+
+       if (r == LIBUSB_SUCCESS) {
+               r = windows_assign_endpoints(dev_handle->dev, iface, 0);
+       }
+
+       return r;
+}
+
+static int windows_set_interface_altsetting(struct libusb_device_handle *dev_handle, int iface, int altsetting)
+{
+       int r = LIBUSB_SUCCESS;
+       struct windows_device_priv *priv = __device_priv(dev_handle->dev);
+
+       safe_free(priv->usb_interface[iface].endpoint);
+       priv->usb_interface[iface].nb_endpoints= 0;
+
+       r = priv->apib->set_interface_altsetting(dev_handle, iface, altsetting);
+
+       if (r == LIBUSB_SUCCESS) {
+               r = windows_assign_endpoints(dev_handle->dev, iface, altsetting);
+       }
+
+       return r;
+}
+
+static int windows_release_interface(struct libusb_device_handle *dev_handle, int iface)
+{
+       struct windows_device_priv *priv = __device_priv(dev_handle->dev);
+
+       windows_set_interface_altsetting(dev_handle, iface, 0);
+       return priv->apib->release_interface(dev_handle, iface);
+}
+
+static int windows_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint)
+{
+       struct windows_device_priv *priv = __device_priv(dev_handle->dev);
+       return priv->apib->clear_halt(dev_handle, endpoint);
+}
+
+static int windows_reset_device(struct libusb_device_handle *dev_handle)
+{
+       struct windows_device_priv *priv = __device_priv(dev_handle->dev);
+       return priv->apib->reset_device(dev_handle);
+}
+
+// The 3 functions below are unlikely to ever get supported on Windows
+static int windows_kernel_driver_active(struct libusb_device_handle *dev_handle, int iface)
+{
+       return LIBUSB_ERROR_NOT_SUPPORTED;
+}
+
+static int windows_attach_kernel_driver(struct libusb_device_handle *dev_handle, int iface)
+{
+       return LIBUSB_ERROR_NOT_SUPPORTED;
+}
+
+static int windows_detach_kernel_driver(struct libusb_device_handle *dev_handle, int iface)
+{
+       return LIBUSB_ERROR_NOT_SUPPORTED;
+}
+
+static void windows_destroy_device(struct libusb_device *dev)
+{
+       struct windows_device_priv *priv = __device_priv(dev);
+       windows_device_priv_release(priv, dev->num_configurations);
+}
+
+static 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.fd);
+       safe_free(transfer_priv->hid_buffer);
+#if defined(AUTO_CLAIM)
+       // When auto claim is in use, attempt to release the auto-claimed interface
+       auto_release(itransfer);
+#endif
+}
+
+static int submit_bulk_transfer(struct usbi_transfer *itransfer)
+{
+       struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+       struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev);
+       struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
+       struct windows_device_priv *priv = __device_priv(transfer->dev_handle->dev);
+       int r;
+
+       r = priv->apib->submit_bulk_transfer(itransfer);
+       if (r != LIBUSB_SUCCESS) {
+               return r;
+       }
+
+       usbi_add_pollfd(ctx, transfer_priv->pollable_fd.fd,
+               (short)((transfer->endpoint & LIBUSB_ENDPOINT_IN)?POLLIN:POLLOUT));
+#if !defined(DYNAMIC_FDS)
+       usbi_fd_notification(ctx);
+#endif
+
+       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;
+
+       r = priv->apib->submit_iso_transfer(itransfer);
+       if (r != LIBUSB_SUCCESS) {
+               return r;
+       }
+
+       usbi_add_pollfd(ctx, transfer_priv->pollable_fd.fd,
+               (short)((transfer->endpoint & LIBUSB_ENDPOINT_IN)?POLLIN:POLLOUT));
+#if !defined(DYNAMIC_FDS)
+       usbi_fd_notification(ctx);
+#endif
+
+       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;
+
+       r = priv->apib->submit_control_transfer(itransfer);
+       if (r != LIBUSB_SUCCESS) {
+               return r;
+       }
+
+       usbi_add_pollfd(ctx, transfer_priv->pollable_fd.fd, POLLIN);
+#if !defined(DYNAMIC_FDS)
+       usbi_fd_notification(ctx);
+#endif
+
+       return LIBUSB_SUCCESS;
+
+}
+
+static int windows_submit_transfer(struct usbi_transfer *itransfer)
+{
+       struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+
+       switch (transfer->type) {
+       case LIBUSB_TRANSFER_TYPE_CONTROL:
+               return submit_control_transfer(itransfer);
+       case LIBUSB_TRANSFER_TYPE_BULK:
+       case LIBUSB_TRANSFER_TYPE_INTERRUPT:
+               return submit_bulk_transfer(itransfer);
+       case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
+               return submit_iso_transfer(itransfer);
+       default:
+               usbi_err(TRANSFER_CTX(transfer), "unknown endpoint type %d", transfer->type);
+               return LIBUSB_ERROR_INVALID_PARAM;
+       }
+}
+
+static int windows_abort_control(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);
+
+       return priv->apib->abort_control(itransfer);
+}
+
+static int windows_abort_transfers(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);
+
+       return priv->apib->abort_transfers(itransfer);
+}
+
+static int windows_cancel_transfer(struct usbi_transfer *itransfer)
+{
+       struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+#if defined(FORCE_INSTANT_TIMEOUTS)
+       struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
+
+       // Forces instant overlapped completion on timeouts - use at your own risks
+       if (itransfer->flags & USBI_TRANSFER_TIMED_OUT) {
+               transfer_priv->pollable_fd.overlapped->Internal &= ~STATUS_PENDING;
+       }
+#endif
+       switch (transfer->type) {
+       case LIBUSB_TRANSFER_TYPE_CONTROL:
+               return windows_abort_control(itransfer);
+       case LIBUSB_TRANSFER_TYPE_BULK:
+       case LIBUSB_TRANSFER_TYPE_INTERRUPT:
+       case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
+               return windows_abort_transfers(itransfer);
+       default:
+               usbi_err(ITRANSFER_CTX(itransfer), "unknown endpoint type %d", transfer->type);
+               return LIBUSB_ERROR_INVALID_PARAM;
+       }
+}
+
+static void windows_transfer_callback(struct usbi_transfer *itransfer, uint32_t io_result, uint32_t io_size)
+{
+       struct libusb_transfer *transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+       struct windows_device_priv *priv = __device_priv(transfer->dev_handle->dev);
+       int status;
+
+       usbi_dbg("handling I/O completion with errcode %d", io_result);
+
+       switch(io_result) {
+       case NO_ERROR:
+               status = priv->apib->copy_transfer_data(itransfer, io_size);
+               break;
+       case ERROR_GEN_FAILURE:
+               usbi_dbg("detected endpoint stall");
+               status = LIBUSB_TRANSFER_STALL;
+               break;
+       case ERROR_SEM_TIMEOUT:
+               usbi_dbg("detected semaphore timeout");
+               status = LIBUSB_TRANSFER_TIMED_OUT;
+               break;
+       case ERROR_OPERATION_ABORTED:
+               if (itransfer->flags & USBI_TRANSFER_TIMED_OUT) {
+                       usbi_dbg("detected timeout");
+                       status = LIBUSB_TRANSFER_TIMED_OUT;
+               } else {
+                       usbi_dbg("detected operation aborted");
+                       status = LIBUSB_TRANSFER_CANCELLED;
+               }
+               break;
+       default:
+               usbi_err(ITRANSFER_CTX(itransfer), "detected I/O error: %s", windows_error_str(0));
+               status = LIBUSB_TRANSFER_ERROR;
+               break;
+       }
+       windows_clear_transfer_priv(itransfer); // Cancel polling
+       usbi_handle_transfer_completion(itransfer, status);
+}
+
+static 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);
+
+       switch (transfer->type) {
+       case LIBUSB_TRANSFER_TYPE_CONTROL:
+       case LIBUSB_TRANSFER_TYPE_BULK:
+       case LIBUSB_TRANSFER_TYPE_INTERRUPT:
+       case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
+               windows_transfer_callback (itransfer, io_result, io_size);
+               break;
+       default:
+               usbi_err(ITRANSFER_CTX(itransfer), "unknown endpoint type %d", transfer->type);
+       }
+}
+
+static int windows_handle_events(struct libusb_context *ctx, struct pollfd *fds, nfds_t nfds, int num_ready)
+{
+       struct windows_transfer_priv* transfer_priv = NULL;
+       nfds_t i = 0;
+       bool found = false;
+       struct usbi_transfer *transfer;
+       DWORD io_size, io_result;
+
+       usbi_mutex_lock(&ctx->open_devs_lock);
+       for (i = 0; i < nfds && num_ready > 0; i++) {
+
+               usbi_dbg("checking fd %d with revents = %04x", fds[i].fd, fds[i].revents);
+
+               if (!fds[i].revents) {
+                       continue;
+               }
+
+               num_ready--;
+
+               // 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);
+               list_for_each_entry(transfer, &ctx->flying_transfers, list, struct usbi_transfer) {
+                       transfer_priv = usbi_transfer_get_os_priv(transfer);
+                       if (transfer_priv->pollable_fd.fd == fds[i].fd) {
+                               found = true;
+                               break;
+                       }
+               }
+               usbi_mutex_unlock(&ctx->flying_transfers_lock);
+
+               if (found) {
+                       // Handle async requests that completed synchronously first
+                       if (HasOverlappedIoCompletedSync(transfer_priv->pollable_fd.overlapped)) {
+                               io_result = NO_ERROR;
+                               io_size = (DWORD)transfer_priv->pollable_fd.overlapped->InternalHigh;
+                       // Regular async overlapped
+                       } else if (GetOverlappedResult(transfer_priv->pollable_fd.handle,
+                               transfer_priv->pollable_fd.overlapped, &io_size, false)) {
+                               io_result = NO_ERROR;
+                       } else {
+                               io_result = GetLastError();
+                       }
+                       usbi_remove_pollfd(ctx, transfer_priv->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);
+               } else {
+                       usbi_err(ctx, "could not find a matching transfer for fd %x", fds[i]);
+                       return LIBUSB_ERROR_NOT_FOUND;
+               }
+       }
+
+       usbi_mutex_unlock(&ctx->open_devs_lock);
+       return LIBUSB_SUCCESS;
+}
+
+/*
+ * Monotonic and real time functions
+ */
+unsigned __stdcall windows_clock_gettime_threaded(void* param)
+{
+       LARGE_INTEGER hires_counter, li_frequency;
+       LONG nb_responses;
+       int timer_index;
+
+       // Init - find out if we have access to a monotonic (hires) timer
+       if (!QueryPerformanceFrequency(&li_frequency)) {
+               usbi_dbg("no hires timer available on this platform");
+               hires_frequency = 0;
+               hires_ticks_to_ps = 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 = UINT64_C(1000000000000) / hires_frequency;
+               usbi_dbg("hires timer available (Frequency: %"PRIu64" Hz)", hires_frequency);
+       }
+
+       // Main loop - wait for requests
+       while (1) {
+               timer_index = WaitForMultipleObjects(2, timer_request, FALSE, INFINITE) - WAIT_OBJECT_0;
+               if ( (timer_index != 0) && (timer_index != 1) ) {
+                       usbi_dbg("failure to wait on requests: %s", windows_error_str(0));
+                       continue;
+               }
+               if (request_count[timer_index] == 0) {
+                       // Request already handled
+                       ResetEvent(timer_request[timer_index]);
+                       // There's still a possiblity that a thread sends a request between the
+                       // time we test request_count[] == 0 and we reset the event, in which case
+                       // the request would be ignored. The simple solution to that is to test
+                       // request_count again and process requests if non zero.
+                       if (request_count[timer_index] == 0)
+                               continue;
+               }
+               switch (timer_index) {
+               case 0:
+                       WaitForSingleObject(timer_mutex, INFINITE);
+                       // Requests to this thread are for hires always
+                       if (QueryPerformanceCounter(&hires_counter) != 0) {
+                               timer_tp.tv_sec = (long)(hires_counter.QuadPart / hires_frequency);
+                               timer_tp.tv_nsec = (long)(((hires_counter.QuadPart % hires_frequency)/1000) * hires_ticks_to_ps);
+                       } else {
+                               // Fallback to real-time if we can't get monotonic value
+                               // Note that real-time clock does not wait on the mutex or this thread.
+                               windows_clock_gettime(USBI_CLOCK_REALTIME, &timer_tp);
+                       }
+                       ReleaseMutex(timer_mutex);
+
+                       nb_responses = InterlockedExchange((LONG*)&request_count[0], 0);
+                       if ( (nb_responses)
+                         && (ReleaseSemaphore(timer_response, nb_responses, NULL) == 0) ) {
+                               usbi_dbg("unable to release timer semaphore %d: %s", windows_error_str(0));
+                       }
+                       continue;
+               case 1: // time to quit
+                       usbi_dbg("timer thread quitting");
+                       return 0;
+               }
+       }
+       usbi_dbg("ERROR: broken timer thread");
+       return 1;
+}
+
+static int windows_clock_gettime(int clk_id, struct timespec *tp)
+{
+       FILETIME filetime;
+       ULARGE_INTEGER rtime;
+       DWORD r;
+       switch(clk_id) {
+       case USBI_CLOCK_MONOTONIC:
+               if (hires_frequency != 0) {
+                       while (1) {
+                               InterlockedIncrement((LONG*)&request_count[0]);
+                               SetEvent(timer_request[0]);
+                               r = WaitForSingleObject(timer_response, TIMER_REQUEST_RETRY_MS);
+                               switch(r) {
+                               case WAIT_OBJECT_0:
+                                       WaitForSingleObject(timer_mutex, INFINITE);
+                                       *tp = timer_tp;
+                                       ReleaseMutex(timer_mutex);
+                                       return LIBUSB_SUCCESS;
+                               case WAIT_TIMEOUT:
+                                       usbi_dbg("could not obtain a timer value within reasonable timeframe - too much load?");
+                                       break; // Retry until successful
+                               default:
+                                       usbi_dbg("WaitForSingleObject failed: %s", windows_error_str(0));
+                                       return LIBUSB_ERROR_OTHER;
+                               }
+                       }
+               }
+               // Fall through and return real-time if monotonic was not detected @ timer init
+       case USBI_CLOCK_REALTIME:
+               // We follow http://msdn.microsoft.com/en-us/library/ms724928%28VS.85%29.aspx
+               // with a predef epoch_time to have an epoch that starts at 1970.01.01 00:00
+               // Note however that our resolution is bounded by the Windows system time
+               // functions and is at best of the order of 1 ms (or, usually, worse)
+               GetSystemTimeAsFileTime(&filetime);
+               rtime.LowPart = filetime.dwLowDateTime;
+               rtime.HighPart = filetime.dwHighDateTime;
+               rtime.QuadPart -= epoch_time;
+               tp->tv_sec = (long)(rtime.QuadPart / 10000000);
+               tp->tv_nsec = (long)((rtime.QuadPart % 10000000)*100);
+               return LIBUSB_SUCCESS;
+       default:
+               return LIBUSB_ERROR_INVALID_PARAM;
+       }
+}
+
+
+// NB: MSVC6 does not support named initializers.
+const struct usbi_os_backend windows_backend = {
+       "Windows",
+       windows_init,
+       windows_exit,
+
+       windows_get_device_list,
+       windows_open,
+       windows_close,
+
+       windows_get_device_descriptor,
+       windows_get_active_config_descriptor,
+       windows_get_config_descriptor,
+
+       windows_get_configuration,
+       windows_set_configuration,
+       windows_claim_interface,
+       windows_release_interface,
+
+       windows_set_interface_altsetting,
+       windows_clear_halt,
+       windows_reset_device,
+
+       windows_kernel_driver_active,
+       windows_detach_kernel_driver,
+       windows_attach_kernel_driver,
+
+       windows_destroy_device,
+
+       windows_submit_transfer,
+       windows_cancel_transfer,
+       windows_clear_transfer_priv,
+
+       windows_handle_events,
+
+       windows_clock_gettime,
+#if defined(USBI_TIMERFD_AVAILABLE)
+       NULL,
+#endif
+       sizeof(struct windows_device_priv),
+       sizeof(struct windows_device_handle_priv),
+       sizeof(struct windows_transfer_priv),
+       0,
+};
+
+
+/*
+ * USB API backends
+ */
+static int unsupported_init(struct libusb_context *ctx) {
+       return LIBUSB_SUCCESS;
+}
+static int unsupported_exit(void) {
+       return LIBUSB_SUCCESS;
+}
+static int unsupported_open(struct libusb_device_handle *dev_handle) {
+       PRINT_UNSUPPORTED_API(open);
+}
+static void unsupported_close(struct libusb_device_handle *dev_handle) {
+       usbi_dbg("unsupported API call for 'close'");
+}
+static int unsupported_claim_interface(struct libusb_device_handle *dev_handle, int iface) {
+       PRINT_UNSUPPORTED_API(claim_interface);
+}
+static int unsupported_set_interface_altsetting(struct libusb_device_handle *dev_handle, int iface, int altsetting) {
+       PRINT_UNSUPPORTED_API(set_interface_altsetting);
+}
+static int unsupported_release_interface(struct libusb_device_handle *dev_handle, int iface) {
+       PRINT_UNSUPPORTED_API(release_interface);
+}
+static int unsupported_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint) {
+       PRINT_UNSUPPORTED_API(clear_halt);
+}
+static int unsupported_reset_device(struct libusb_device_handle *dev_handle) {
+       PRINT_UNSUPPORTED_API(reset_device);
+}
+static int unsupported_submit_bulk_transfer(struct usbi_transfer *itransfer) {
+       PRINT_UNSUPPORTED_API(submit_bulk_transfer);
+}
+static int unsupported_submit_iso_transfer(struct usbi_transfer *itransfer) {
+       PRINT_UNSUPPORTED_API(submit_iso_transfer);
+}
+static int unsupported_submit_control_transfer(struct usbi_transfer *itransfer) {
+       PRINT_UNSUPPORTED_API(submit_control_transfer);
+}
+static int unsupported_abort_control(struct usbi_transfer *itransfer) {
+       PRINT_UNSUPPORTED_API(abort_control);
+}
+static int unsupported_abort_transfers(struct usbi_transfer *itransfer) {
+       PRINT_UNSUPPORTED_API(abort_transfers);
+}
+static int unsupported_copy_transfer_data(struct usbi_transfer *itransfer, uint32_t io_size) {
+       PRINT_UNSUPPORTED_API(copy_transfer_data);
+}
+
+const char* composite_driver_names[] = {"usbccgp"};
+const char* winusb_driver_names[] = {"WinUSB"};
+const char* hid_driver_names[] = {"HidUsb", "mouhid", "kbdhid"};
+const struct windows_usb_api_backend usb_api_backend[USB_API_MAX] = {
+       {
+               USB_API_UNSUPPORTED,
+               &CLASS_GUID_UNSUPPORTED,
+               NULL,
+               0,
+               unsupported_init,
+               unsupported_exit,
+               unsupported_open,
+               unsupported_close,
+               unsupported_claim_interface,
+               unsupported_set_interface_altsetting,
+               unsupported_release_interface,
+               unsupported_clear_halt,
+               unsupported_reset_device,
+               unsupported_submit_bulk_transfer,
+               unsupported_submit_iso_transfer,
+               unsupported_submit_control_transfer,
+               unsupported_abort_control,
+               unsupported_abort_transfers,
+               unsupported_copy_transfer_data,
+       }, {
+               USB_API_COMPOSITE,
+               &CLASS_GUID_COMPOSITE,
+               composite_driver_names,
+               1,
+               composite_init,
+               composite_exit,
+               composite_open,
+               composite_close,
+               composite_claim_interface,
+               composite_set_interface_altsetting,
+               composite_release_interface,
+               composite_clear_halt,
+               composite_reset_device,
+               composite_submit_bulk_transfer,
+               composite_submit_iso_transfer,
+               composite_submit_control_transfer,
+               composite_abort_control,
+               composite_abort_transfers,
+               composite_copy_transfer_data,
+       }, {
+               USB_API_WINUSB,
+               &CLASS_GUID_LIBUSB_WINUSB,
+               winusb_driver_names,
+               1,
+               winusb_init,
+               winusb_exit,
+               winusb_open,
+               winusb_close,
+               winusb_claim_interface,
+               winusb_set_interface_altsetting,
+               winusb_release_interface,
+               winusb_clear_halt,
+               winusb_reset_device,
+               winusb_submit_bulk_transfer,
+               unsupported_submit_iso_transfer,
+               winusb_submit_control_transfer,
+               winusb_abort_control,
+               winusb_abort_transfers,
+               winusb_copy_transfer_data,
+       }, {
+               USB_API_HID,
+               &CLASS_GUID_HID,
+               hid_driver_names,
+               3,
+               hid_init,
+               hid_exit,
+               hid_open,
+               hid_close,
+               hid_claim_interface,
+               hid_set_interface_altsetting,
+               hid_release_interface,
+               hid_clear_halt,
+               hid_reset_device,
+               hid_submit_bulk_transfer,
+               unsupported_submit_iso_transfer,
+               hid_submit_control_transfer,
+               hid_abort_transfers,
+               hid_abort_transfers,
+               hid_copy_transfer_data,
+       },
+};
+
+
+/*
+ * WinUSB API functions
+ */
+static int winusb_init(struct libusb_context *ctx)
+{
+       DLL_LOAD(winusb.dll, WinUsb_Initialize, TRUE);
+       DLL_LOAD(winusb.dll, WinUsb_Free, TRUE);
+       DLL_LOAD(winusb.dll, WinUsb_GetAssociatedInterface, TRUE);
+       DLL_LOAD(winusb.dll, WinUsb_GetDescriptor, TRUE);
+       DLL_LOAD(winusb.dll, WinUsb_QueryInterfaceSettings, TRUE);
+       DLL_LOAD(winusb.dll, WinUsb_QueryDeviceInformation, TRUE);
+       DLL_LOAD(winusb.dll, WinUsb_SetCurrentAlternateSetting, TRUE);
+       DLL_LOAD(winusb.dll, WinUsb_GetCurrentAlternateSetting, TRUE);
+       DLL_LOAD(winusb.dll, WinUsb_QueryPipe, TRUE);
+       DLL_LOAD(winusb.dll, WinUsb_SetPipePolicy, TRUE);
+       DLL_LOAD(winusb.dll, WinUsb_GetPipePolicy, TRUE);
+       DLL_LOAD(winusb.dll, WinUsb_ReadPipe, TRUE);
+       DLL_LOAD(winusb.dll, WinUsb_WritePipe, TRUE);
+       DLL_LOAD(winusb.dll, WinUsb_ControlTransfer, TRUE);
+       DLL_LOAD(winusb.dll, WinUsb_ResetPipe, TRUE);
+       DLL_LOAD(winusb.dll, WinUsb_AbortPipe, TRUE);
+       DLL_LOAD(winusb.dll, WinUsb_FlushPipe, TRUE);
+
+       api_winusb_available = true;
+       return LIBUSB_SUCCESS;
+}
+
+static int winusb_exit(void)
+{
+       return LIBUSB_SUCCESS;
+}
+
+// NB: open and close must ensure that they only handle interface of
+// the right API type, as these functions can be called wholesale from
+// composite_open(), with interfaces belonging to different APIs
+static int winusb_open(struct libusb_device_handle *dev_handle)
+{
+       struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev);
+       struct windows_device_priv *priv = __device_priv(dev_handle->dev);
+       struct windows_device_handle_priv *handle_priv = __device_handle_priv(dev_handle);
+
+       HANDLE file_handle;
+       int i;
+
+       CHECK_WINUSB_AVAILABLE;
+
+       // WinUSB requires a seperate handle for each interface
+       for (i = 0; i < USB_MAXINTERFACES; i++) {
+               if ( (priv->usb_interface[i].path != NULL)
+                 && (priv->usb_interface[i].apib->id == USB_API_WINUSB) ) {
+                       file_handle = CreateFileA(priv->usb_interface[i].path, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ,
+                               NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
+                       if (file_handle == INVALID_HANDLE_VALUE) {
+                               usbi_err(ctx, "could not open device %s (interface %d): %s", priv->path, i, windows_error_str(0));
+                               switch(GetLastError()) {
+                               case ERROR_FILE_NOT_FOUND:      // The device was disconnected
+                                       return LIBUSB_ERROR_NO_DEVICE;
+                               case ERROR_ACCESS_DENIED:
+                                       return LIBUSB_ERROR_ACCESS;
+                               default:
+                                       return LIBUSB_ERROR_IO;
+                               }
+                       }
+                       handle_priv->interface_handle[i].dev_handle = file_handle;
+               }
+       }
+
+       return LIBUSB_SUCCESS;
+}
+
+static void winusb_close(struct libusb_device_handle *dev_handle)
+{
+       struct windows_device_handle_priv *handle_priv = __device_handle_priv(dev_handle);
+       struct windows_device_priv *priv = __device_priv(dev_handle->dev);
+       HANDLE file_handle;
+       int i;
+
+       if (!api_winusb_available)
+               return;
+
+       for (i = 0; i < USB_MAXINTERFACES; i++) {
+               if (priv->usb_interface[i].apib->id == USB_API_WINUSB) {
+                       file_handle = handle_priv->interface_handle[i].dev_handle;
+                       if ( (file_handle != 0) && (file_handle != INVALID_HANDLE_VALUE)) {
+                               CloseHandle(file_handle);
+                       }
+               }
+       }
+}
+
+static int winusb_claim_interface(struct libusb_device_handle *dev_handle, int iface)
+{
+       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);
+       bool is_composite = (priv->apib->id == USB_API_COMPOSITE);
+       HANDLE file_handle, winusb_handle;
+       USB_INTERFACE_DESCRIPTOR if_desc;
+       UCHAR policy;
+       uint8_t endpoint_address;
+       int i;
+
+       CHECK_WINUSB_AVAILABLE;
+
+       // interfaces for composite devices are always independent, therefore
+       // "alt" interfaces are only found on non-composite
+       if ((!is_composite) && (iface != 0)) {
+               winusb_handle = handle_priv->interface_handle[0].api_handle;
+               // It is a requirement on Windows that to claim an interface >= 1
+               // on a non-composite WinUSB device, you must first have claimed interface 0
+               if ((winusb_handle == 0) || (winusb_handle == INVALID_HANDLE_VALUE)) {
+#if defined(AUTO_CLAIM)
+                       file_handle = handle_priv->interface_handle[0].dev_handle;
+                       if (WinUsb_Initialize(file_handle, &winusb_handle)) {
+                               handle_priv->interface_handle[0].api_handle = winusb_handle;
+                               usbi_warn(ctx, "auto-claimed interface 0 (required to claim %d with WinUSB)", iface);
+                       } else {
+                               usbi_warn(ctx, "failed to auto-claim interface 0 (required to claim %d with WinUSB)", iface);
+                               return LIBUSB_ERROR_ACCESS;
+                       }
+#else
+                       usbi_warn(ctx, "you must claim interface 0 before you can claim %d with WinUSB", iface);
+                       return LIBUSB_ERROR_ACCESS;
+#endif
+               }
+               if (!WinUsb_GetAssociatedInterface(winusb_handle, (UCHAR)(iface-1),
+                       &handle_priv->interface_handle[iface].api_handle)) {
+                       handle_priv->interface_handle[iface].api_handle = INVALID_HANDLE_VALUE;
+                       switch(GetLastError()) {
+                       case ERROR_NO_MORE_ITEMS:   // invalid iface
+                               return LIBUSB_ERROR_NOT_FOUND;
+                       case ERROR_BAD_COMMAND:     // The device was disconnected
+                               return LIBUSB_ERROR_NO_DEVICE;
+                       case ERROR_ALREADY_EXISTS:  // already claimed
+                               return LIBUSB_ERROR_BUSY;
+                       default:
+                               usbi_err(ctx, "could not claim interface %d: %s", iface, windows_error_str(0));
+                               return LIBUSB_ERROR_ACCESS;
+                       }
+               }
+       } else {
+               // composite device (independent interfaces) or interface 0
+               winusb_handle = handle_priv->interface_handle[iface].api_handle;
+               file_handle = handle_priv->interface_handle[iface].dev_handle;
+               if ((file_handle == 0) || (file_handle == INVALID_HANDLE_VALUE)) {
+                       return LIBUSB_ERROR_NOT_FOUND;
+               }
+
+               if (!WinUsb_Initialize(file_handle, &winusb_handle)) {
+                       usbi_err(ctx, "could not access interface %d: %s", iface, windows_error_str(0));
+                       handle_priv->interface_handle[iface].api_handle = INVALID_HANDLE_VALUE;
+
+                       switch(GetLastError()) {
+                       case ERROR_BAD_COMMAND: // The device was disconnected
+                               return LIBUSB_ERROR_NO_DEVICE;
+                       default:
+                               usbi_err(ctx, "could not claim interface %d: %s", iface, windows_error_str(0));
+                               return LIBUSB_ERROR_ACCESS;
+                       }
+               }
+               handle_priv->interface_handle[iface].api_handle = winusb_handle;
+       }
+       if (!WinUsb_QueryInterfaceSettings(winusb_handle, 0, &if_desc)) {
+               usbi_err(ctx, "could not query interface settings for interface %d: %s", iface, windows_error_str(0));
+       } else if (if_desc.bInterfaceNumber != iface) {
+               usbi_warn(ctx, "program assertion failed - WinUSB interface %d found at position %d",
+                       if_desc.bInterfaceNumber, iface);
+       }
+
+       usbi_dbg("claimed interface %d", iface);
+       handle_priv->active_interface = iface;
+
+       // With handle and enpoints set (in parent), we can setup the default
+       // pipe properties (copied from libusb-win32-v1)
+       // see http://download.microsoft.com/download/D/1/D/D1DD7745-426B-4CC3-A269-ABBBE427C0EF/DVC-T705_DDC08.pptx
+       for (i=0; i<priv->usb_interface[iface].nb_endpoints; i++) {
+               endpoint_address = priv->usb_interface[iface].endpoint[i];
+               policy = false;
+               if (!WinUsb_SetPipePolicy(winusb_handle, endpoint_address,
+                       SHORT_PACKET_TERMINATE, sizeof(UCHAR), &policy)) {
+                       usbi_dbg("failed to disable SHORT_PACKET_TERMINATE for endpoint %02X", endpoint_address);
+               }
+               if (!WinUsb_SetPipePolicy(winusb_handle, endpoint_address,
+                       IGNORE_SHORT_PACKETS, sizeof(UCHAR), &policy)) {
+                       usbi_dbg("failed to disable IGNORE_SHORT_PACKETS for endpoint %02X", endpoint_address);
+               }
+               if (!WinUsb_SetPipePolicy(winusb_handle, endpoint_address,
+                       ALLOW_PARTIAL_READS, sizeof(UCHAR), &policy)) {
+                       usbi_dbg("failed to disable ALLOW_PARTIAL_READS for endpoint %02X", endpoint_address);
+               }
+               policy = true;
+               if (!WinUsb_SetPipePolicy(winusb_handle, endpoint_address,
+                       AUTO_CLEAR_STALL, sizeof(UCHAR), &policy)) {
+                       usbi_dbg("failed to enable AUTO_CLEAR_STALL for endpoint %02X", endpoint_address);
+               }
+       }
+
+       return LIBUSB_SUCCESS;
+}
+
+static int winusb_release_interface(struct libusb_device_handle *dev_handle, int iface)
+{
+       struct windows_device_handle_priv *handle_priv = __device_handle_priv(dev_handle);
+       HANDLE winusb_handle;
+
+       CHECK_WINUSB_AVAILABLE;
+
+       winusb_handle = handle_priv->interface_handle[iface].api_handle;
+       if ((winusb_handle == 0) || (winusb_handle == INVALID_HANDLE_VALUE)) {
+               return LIBUSB_ERROR_NOT_FOUND;
+       }
+
+       WinUsb_Free(winusb_handle);
+
+       return LIBUSB_SUCCESS;
+}
+
+/*
+ * Return the first valid interface (of the same API type), for control transfers
+ */
+static int get_valid_interface(struct libusb_device_handle *dev_handle, int api_id)
+{
+       struct windows_device_handle_priv *handle_priv = __device_handle_priv(dev_handle);
+       struct windows_device_priv *priv = __device_priv(dev_handle->dev);
+       int i;
+
+       if ((api_id < USB_API_WINUSB) || (api_id > USB_API_HID)) {
+               usbi_dbg("unsupported API ID");
+               return -1;
+       }
+
+       for (i=0; i<USB_MAXINTERFACES; i++) {
+               if ( (handle_priv->interface_handle[i].dev_handle != 0)
+                 && (handle_priv->interface_handle[i].dev_handle != INVALID_HANDLE_VALUE)
+                 && (handle_priv->interface_handle[i].api_handle != 0)
+                 && (handle_priv->interface_handle[i].api_handle != INVALID_HANDLE_VALUE)
+                 && (priv->usb_interface[i].apib == &usb_api_backend[api_id]) ) {
+                       return i;
+               }
+       }
+       return -1;
+}
+
+/*
+ * Lookup interface by endpoint address. -1 if not found
+ */
+static int interface_by_endpoint(struct windows_device_priv *priv,
+       struct windows_device_handle_priv *handle_priv, uint8_t endpoint_address)
+{
+       int i, j;
+       for (i=0; i<USB_MAXINTERFACES; i++) {
+               if (handle_priv->interface_handle[i].api_handle == INVALID_HANDLE_VALUE)
+                       continue;
+               if (handle_priv->interface_handle[i].api_handle == 0)
+                       continue;
+               if (priv->usb_interface[i].endpoint == NULL)
+                       continue;
+               for (j=0; j<priv->usb_interface[i].nb_endpoints; j++) {
+                       if (priv->usb_interface[i].endpoint[j] == endpoint_address) {
+                               return i;
+                       }
+               }
+       }
+       return -1;
+}
+
+static int winusb_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_handle_priv *handle_priv = __device_handle_priv(
+               transfer->dev_handle);
+       WINUSB_SETUP_PACKET *setup = (WINUSB_SETUP_PACKET *) transfer->buffer;
+       ULONG size;
+       HANDLE winusb_handle;
+       int current_interface;
+       struct winfd wfd;
+
+       CHECK_WINUSB_AVAILABLE;
+
+       transfer_priv->pollable_fd = INVALID_WINFD;
+       size = transfer->length - LIBUSB_CONTROL_SETUP_SIZE;
+
+       if (size > MAX_CTRL_BUFFER_LENGTH)
+               return LIBUSB_ERROR_INVALID_PARAM;
+
+       current_interface = get_valid_interface(transfer->dev_handle, USB_API_WINUSB);
+       if (current_interface < 0) {
+#if defined(AUTO_CLAIM)
+               if (auto_claim(transfer, &current_interface, USB_API_WINUSB) != LIBUSB_SUCCESS) {
+                       return LIBUSB_ERROR_NOT_FOUND;
+               }
+#else
+               usbi_warn(ctx, "no interface available for control transfer");
+               return LIBUSB_ERROR_NOT_FOUND;
+#endif
+       }
+
+       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);
+       // Always use the handle returned from usbi_create_fd (wfd.handle)
+       if (wfd.fd < 0) {
+               return LIBUSB_ERROR_NO_MEM;
+       }
+
+       if (!WinUsb_ControlTransfer(wfd.handle, *setup, transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE, size, NULL, wfd.overlapped)) {
+               if(GetLastError() != ERROR_IO_PENDING) {
+                       usbi_err(ctx, "WinUsb_ControlTransfer failed: %s", windows_error_str(0));
+                       usbi_free_fd(wfd.fd);
+                       return LIBUSB_ERROR_IO;
+               }
+       } else {
+               wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY;
+               wfd.overlapped->InternalHigh = (DWORD)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;
+}
+
+static int winusb_set_interface_altsetting(struct libusb_device_handle *dev_handle, int iface, int altsetting)
+{
+       struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev);
+       struct windows_device_handle_priv *handle_priv = __device_handle_priv(dev_handle);
+       HANDLE winusb_handle;
+
+       CHECK_WINUSB_AVAILABLE;
+
+       if (altsetting > 255) {
+               return LIBUSB_ERROR_INVALID_PARAM;
+       }
+
+       winusb_handle = handle_priv->interface_handle[iface].api_handle;
+       if ((winusb_handle == 0) || (winusb_handle == INVALID_HANDLE_VALUE)) {
+               usbi_err(ctx, "interface must be claimed first");
+               return LIBUSB_ERROR_NOT_FOUND;
+       }
+
+       if (!WinUsb_SetCurrentAlternateSetting(winusb_handle, (UCHAR)altsetting)) {
+               usbi_err(ctx, "WinUsb_SetCurrentAlternateSetting failed: %s", windows_error_str(0));
+               return LIBUSB_ERROR_IO;
+       }
+
+       return LIBUSB_SUCCESS;
+}
+
+static int winusb_submit_bulk_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_handle_priv *handle_priv = __device_handle_priv(transfer->dev_handle);
+       struct windows_device_priv *priv = __device_priv(transfer->dev_handle->dev);
+       HANDLE winusb_handle;
+       bool direction_in, ret;
+       int current_interface;
+       struct winfd wfd;
+
+       CHECK_WINUSB_AVAILABLE;
+
+       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");
+               return LIBUSB_ERROR_NOT_FOUND;
+       }
+
+       usbi_dbg("matched endpoint %02X with interface %d", transfer->endpoint, current_interface);
+
+       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);
+       // Always use the handle returned from usbi_create_fd (wfd.handle)
+       if (wfd.fd < 0) {
+               return LIBUSB_ERROR_NO_MEM;
+       }
+
+       if (direction_in) {
+               usbi_dbg("reading %d bytes", transfer->length);
+               ret = WinUsb_ReadPipe(wfd.handle, transfer->endpoint, transfer->buffer, transfer->length, NULL, wfd.overlapped);
+       } else {
+               usbi_dbg("writing %d bytes", transfer->length);
+               ret = WinUsb_WritePipe(wfd.handle, transfer->endpoint, transfer->buffer, transfer->length, NULL, wfd.overlapped);
+       }
+       if (!ret) {
+               if(GetLastError() != ERROR_IO_PENDING) {
+                       usbi_err(ctx, "WinUsb_Pipe Transfer failed: %s", windows_error_str(0));
+                       usbi_free_fd(wfd.fd);
+                       return LIBUSB_ERROR_IO;
+               }
+       } else {
+               wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY;
+               wfd.overlapped->InternalHigh = (DWORD)transfer->length;
+       }
+
+       transfer_priv->pollable_fd = wfd;
+       transfer_priv->interface_number = (uint8_t)current_interface;
+
+       return LIBUSB_SUCCESS;
+}
+
+static int winusb_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint)
+{
+       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);
+       HANDLE winusb_handle;
+       int current_interface;
+
+       CHECK_WINUSB_AVAILABLE;
+
+       current_interface = interface_by_endpoint(priv, handle_priv, endpoint);
+       if (current_interface < 0) {
+               usbi_err(ctx, "unable to match endpoint to an open interface - cannot clear");
+               return LIBUSB_ERROR_NOT_FOUND;
+       }
+
+       usbi_dbg("matched endpoint %02X with interface %d", endpoint, current_interface);
+       winusb_handle = handle_priv->interface_handle[current_interface].api_handle;
+
+       if (!WinUsb_ResetPipe(winusb_handle, endpoint)) {
+               usbi_err(ctx, "WinUsb_ResetPipe failed: %s", windows_error_str(0));
+               return LIBUSB_ERROR_NO_DEVICE;
+       }
+
+       return LIBUSB_SUCCESS;
+}
+
+/*
+ * from http://www.winvistatips.com/winusb-bugchecks-t335323.html (confirmed
+ * through testing as well):
+ * "You can not call WinUsb_AbortPipe on control pipe. You can possibly cancel
+ * the control transfer using CancelIo"
+ */
+static int winusb_abort_control(struct usbi_transfer *itransfer)
+{
+       // Cancelling of the I/O is done in the parent
+       return LIBUSB_SUCCESS;
+}
+
+static int winusb_abort_transfers(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_device_handle_priv *handle_priv = __device_handle_priv(transfer->dev_handle);
+       struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
+       HANDLE winusb_handle;
+       int current_interface;
+
+       CHECK_WINUSB_AVAILABLE;
+
+       current_interface = transfer_priv->interface_number;
+       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);
+
+       winusb_handle = handle_priv->interface_handle[current_interface].api_handle;
+
+       if (!WinUsb_AbortPipe(winusb_handle, transfer->endpoint)) {
+               usbi_err(ctx, "WinUsb_AbortPipe failed: %s", windows_error_str(0));
+               return LIBUSB_ERROR_NO_DEVICE;
+       }
+
+       return LIBUSB_SUCCESS;
+}
+
+/*
+ * from the "How to Use WinUSB to Communicate with a USB Device" Microsoft white paper
+ * (http://www.microsoft.com/whdc/connect/usb/winusb_howto.mspx):
+ * "WinUSB does not support host-initiated reset port and cycle port operations" and
+ * IOCTL_INTERNAL_USB_CYCLE_PORT is only available in kernel mode and the
+ * IOCTL_USB_HUB_CYCLE_PORT ioctl was removed from Vista => the best we can do is
+ * cycle the pipes (and even then, the control pipe can not be reset using WinUSB)
+ */
+// TODO (2nd official release): see if we can force eject the device and redetect it (reuse hotplug?)
+static int winusb_reset_device(struct libusb_device_handle *dev_handle)
+{
+       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;
+
+       CHECK_WINUSB_AVAILABLE;
+
+       // 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.fd);
+                       wfd = handle_to_winfd(winusb_handle);
+               }
+
+               if ( (winusb_handle != 0) && (winusb_handle != INVALID_HANDLE_VALUE)) {
+                       for (j=0; j<priv->usb_interface[i].nb_endpoints; j++) {
+                               usbi_dbg("resetting ep %02X", priv->usb_interface[i].endpoint[j]);
+                               if (!WinUsb_AbortPipe(winusb_handle, priv->usb_interface[i].endpoint[j])) {
+                                       usbi_err(ctx, "WinUsb_AbortPipe (pipe address %02X) failed: %s",
+                                               priv->usb_interface[i].endpoint[j], windows_error_str(0));
+                               }
+                               // FlushPipe seems to fail on OUT pipes
+                               if ( (priv->usb_interface[i].endpoint[j] & LIBUSB_ENDPOINT_IN)
+                                 && (!WinUsb_FlushPipe(winusb_handle, priv->usb_interface[i].endpoint[j])) ) {
+                                       usbi_err(ctx, "WinUsb_FlushPipe (pipe address %02X) failed: %s",
+                                               priv->usb_interface[i].endpoint[j], windows_error_str(0));
+                               }
+                               if (!WinUsb_ResetPipe(winusb_handle, priv->usb_interface[i].endpoint[j])) {
+                                       usbi_err(ctx, "WinUsb_ResetPipe (pipe address %02X) failed: %s",
+                                               priv->usb_interface[i].endpoint[j], windows_error_str(0));
+                               }
+                       }
+               }
+       }
+
+       return LIBUSB_SUCCESS;
+}
+
+static int winusb_copy_transfer_data(struct usbi_transfer *itransfer, uint32_t io_size)
+{
+       itransfer->transferred += io_size;
+       return LIBUSB_TRANSFER_COMPLETED;
+}
+
+/*
+ * Internal HID Support functions (from libusb-win32)
+ * Note that functions that complete data transfer synchronously must return
+ * LIBUSB_COMPLETED instead of LIBUSB_SUCCESS
+ */
+static int _hid_get_hid_descriptor(struct hid_device_priv* dev, void *data, size_t *size);
+static int _hid_get_report_descriptor(struct hid_device_priv* dev, void *data, size_t *size);
+
+static int _hid_wcslen(WCHAR *str)
+{
+       int i = 0;
+       while (str[i] && (str[i] != 0x409)) {
+               i++;
+       }
+       return i;
+}
+
+static int _hid_get_device_descriptor(struct hid_device_priv* dev, void *data, size_t *size)
+{
+       struct libusb_device_descriptor d;
+
+       d.bLength = LIBUSB_DT_DEVICE_SIZE;
+       d.bDescriptorType = LIBUSB_DT_DEVICE;
+       d.bcdUSB = 0x0200; /* 2.00 */
+       d.bDeviceClass = 0;
+       d.bDeviceSubClass = 0;
+       d.bDeviceProtocol = 0;
+       d.bMaxPacketSize0 = 64; /* fix this! */
+       d.idVendor = (uint16_t)dev->vid;
+       d.idProduct = (uint16_t)dev->pid;
+       d.bcdDevice = 0x0100;
+       d.iManufacturer = dev->string_index[0];
+       d.iProduct = dev->string_index[1];
+       d.iSerialNumber = dev->string_index[2];
+       d.bNumConfigurations = 1;
+
+       if (*size > LIBUSB_DT_DEVICE_SIZE)
+               *size = LIBUSB_DT_DEVICE_SIZE;
+       memcpy(data, &d, *size);
+       return LIBUSB_COMPLETED;
+}
+
+static int _hid_get_config_descriptor(struct hid_device_priv* dev, void *data, size_t *size)
+{
+       char num_endpoints = 0;
+       size_t config_total_len = 0;
+       char tmp[HID_MAX_CONFIG_DESC_SIZE];
+       struct libusb_config_descriptor *cd;
+       struct libusb_interface_descriptor *id;
+       struct libusb_hid_descriptor *hd;
+       struct libusb_endpoint_descriptor *ed;
+       size_t tmp_size;
+
+       if (dev->input_report_size)
+               num_endpoints++;
+       if (dev->output_report_size)
+               num_endpoints++;
+
+       config_total_len = LIBUSB_DT_CONFIG_SIZE + LIBUSB_DT_INTERFACE_SIZE
+               + LIBUSB_DT_HID_SIZE + num_endpoints * LIBUSB_DT_ENDPOINT_SIZE;
+
+
+       cd = (struct libusb_config_descriptor *)tmp;
+       id = (struct libusb_interface_descriptor *)(tmp + LIBUSB_DT_CONFIG_SIZE);
+       hd = (struct libusb_hid_descriptor *)(tmp + LIBUSB_DT_CONFIG_SIZE
+               + LIBUSB_DT_INTERFACE_SIZE);
+       ed = (struct libusb_endpoint_descriptor *)(tmp + LIBUSB_DT_CONFIG_SIZE
+               + LIBUSB_DT_INTERFACE_SIZE
+               + LIBUSB_DT_HID_SIZE);
+
+       cd->bLength = LIBUSB_DT_CONFIG_SIZE;
+       cd->bDescriptorType = LIBUSB_DT_CONFIG;
+       cd->wTotalLength = (uint16_t) config_total_len;
+       cd->bNumInterfaces = 1;
+       cd->bConfigurationValue = 1;
+       cd->iConfiguration = 0;
+       cd->bmAttributes = 1 << 7; /* bus powered */
+       cd->MaxPower = 50;
+
+       id->bLength = LIBUSB_DT_INTERFACE_SIZE;
+       id->bDescriptorType = LIBUSB_DT_INTERFACE;
+       id->bInterfaceNumber = 0;
+       id->bAlternateSetting = 0;
+       id->bNumEndpoints = num_endpoints;
+       id->bInterfaceClass = 3;
+       id->bInterfaceSubClass = 0;
+       id->bInterfaceProtocol = 0;
+       id->iInterface = 0;
+
+       tmp_size = LIBUSB_DT_HID_SIZE;
+       _hid_get_hid_descriptor(dev, hd, &tmp_size);
+
+       if (dev->input_report_size) {
+               ed->bLength = LIBUSB_DT_ENDPOINT_SIZE;
+               ed->bDescriptorType = LIBUSB_DT_ENDPOINT;
+               ed->bEndpointAddress = HID_IN_EP;
+               ed->bmAttributes = 3;
+               ed->wMaxPacketSize = dev->input_report_size - 1;
+               ed->bInterval = 10;
+
+               ed++;
+       }
+
+       if (dev->output_report_size) {
+               ed->bLength = LIBUSB_DT_ENDPOINT_SIZE;
+               ed->bDescriptorType = LIBUSB_DT_ENDPOINT;
+               ed->bEndpointAddress = HID_OUT_EP;
+               ed->bmAttributes = 3;
+               ed->wMaxPacketSize = dev->output_report_size - 1;
+               ed->bInterval = 10;
+       }
+
+       if (*size > config_total_len)
+               *size = config_total_len;
+       memcpy(data, tmp, *size);
+       return LIBUSB_COMPLETED;
+}
+
+static int _hid_get_string_descriptor(struct hid_device_priv* dev, int index,
+                                                                         void *data, size_t *size)
+{
+       void *tmp = NULL;
+       size_t tmp_size = 0;
+       int i;
+
+       /* language ID, EN-US */
+       char string_langid[] = {
+               0x09,
+               0x04
+       };
+
+       if ((*size < 2) || (*size > 255)) {
+               return LIBUSB_ERROR_OVERFLOW;
+       }
+
+       if (index == 0) {
+               tmp = string_langid;
+               tmp_size = sizeof(string_langid)+2;
+       } else {
+               for (i=0; i<3; i++) {
+                       if (index == (dev->string_index[i])) {
+                               tmp = dev->string[i];
+                               tmp_size = (_hid_wcslen(dev->string[i])+1) * sizeof(WCHAR);
+                               break;
+                       }
+               }
+               if (i == 3) {   // not found
+                       return LIBUSB_ERROR_INVALID_PARAM;
+               }
+       }
+
+       if(!tmp_size) {
+               return LIBUSB_ERROR_INVALID_PARAM;
+       }
+
+       if (tmp_size < *size) {
+               *size = tmp_size;
+       }
+       // 2 byte header
+       ((uint8_t*)data)[0] = (uint8_t)*size;
+       ((uint8_t*)data)[1] = LIBUSB_DT_STRING;
+       memcpy((uint8_t*)data+2, tmp, *size-2);
+       return LIBUSB_COMPLETED;
+}
+
+static int _hid_get_hid_descriptor(struct hid_device_priv* dev, void *data, size_t *size)
+{
+       struct libusb_hid_descriptor d;
+       uint8_t tmp[MAX_HID_DESCRIPTOR_SIZE];
+       size_t report_len = MAX_HID_DESCRIPTOR_SIZE;
+
+       _hid_get_report_descriptor(dev, tmp, &report_len);
+
+       d.bLength = LIBUSB_DT_HID_SIZE;
+       d.bDescriptorType = LIBUSB_DT_HID;
+       d.bcdHID = 0x0110; /* 1.10 */
+       d.bCountryCode = 0;
+       d.bNumDescriptors = 1;
+       d.bClassDescriptorType = LIBUSB_DT_REPORT;
+       d.wClassDescriptorLength = (uint16_t)report_len;
+
+       if (*size > LIBUSB_DT_HID_SIZE)
+               *size = LIBUSB_DT_HID_SIZE;
+       memcpy(data, &d, *size);
+       return LIBUSB_COMPLETED;
+}
+
+static int _hid_get_report_descriptor(struct hid_device_priv* dev, void *data, size_t *size)
+{
+       uint8_t d[MAX_HID_DESCRIPTOR_SIZE];
+       size_t i = 0;
+
+       /* usage page (0xFFA0 == vendor defined) */
+       d[i++] = 0x06; d[i++] = 0xA0; d[i++] = 0xFF;
+       /* usage (vendor defined) */
+       d[i++] = 0x09; d[i++] = 0x01;
+       /* start collection (application) */
+       d[i++] = 0xA1; d[i++] = 0x01;
+       /* input report */
+       if (dev->input_report_size) {
+               /* usage (vendor defined) */
+               d[i++] = 0x09; d[i++] = 0x01;
+               /* logical minimum (0) */
+               d[i++] = 0x15; d[i++] = 0x00;
+               /* logical maximum (255) */
+               d[i++] = 0x25; d[i++] = 0xFF;
+               /* report size (8 bits) */
+               d[i++] = 0x75; d[i++] = 0x08;
+               /* report count */
+               d[i++] = 0x95; d[i++] = (uint8_t)dev->input_report_size - 1;
+               /* input (data, variable, absolute) */
+               d[i++] = 0x81; d[i++] = 0x00;
+       }
+       /* output report */
+       if (dev->output_report_size) {
+               /* usage (vendor defined) */
+               d[i++] = 0x09; d[i++] = 0x02;
+               /* logical minimum (0) */
+               d[i++] = 0x15; d[i++] = 0x00;
+               /* logical maximum (255) */
+               d[i++] = 0x25; d[i++] = 0xFF;
+               /* report size (8 bits) */
+               d[i++] = 0x75; d[i++] = 0x08;
+               /* report count */
+               d[i++] = 0x95; d[i++] = (uint8_t)dev->output_report_size - 1;
+               /* output (data, variable, absolute) */
+               d[i++] = 0x91; d[i++] = 0x00;
+       }
+       /* feature report */
+       if (dev->feature_report_size) {
+               /* usage (vendor defined) */
+               d[i++] = 0x09; d[i++] = 0x03;
+               /* logical minimum (0) */
+               d[i++] = 0x15; d[i++] = 0x00;
+               /* logical maximum (255) */
+               d[i++] = 0x25; d[i++] = 0xFF;
+               /* report size (8 bits) */
+               d[i++] = 0x75; d[i++] = 0x08;
+               /* report count */
+               d[i++] = 0x95; d[i++] = (uint8_t)dev->feature_report_size - 1;
+               /* feature (data, variable, absolute) */
+               d[i++] = 0xb2; d[i++] = 0x02; d[i++] = 0x01;
+       }
+
+       /* end collection */
+       d[i++] = 0xC0;
+
+       if (*size > i)
+               *size = i;
+       memcpy(data, d, *size);
+       return LIBUSB_COMPLETED;
+}
+
+static int _hid_get_descriptor(struct hid_device_priv* dev, HANDLE hid_handle, int recipient,
+                                                          int type, int index, void *data, size_t *size)
+{
+       switch(type) {
+       case LIBUSB_DT_DEVICE:
+               usbi_dbg("LIBUSB_DT_DEVICE");
+               return _hid_get_device_descriptor(dev, data, size);
+       case LIBUSB_DT_CONFIG:
+               usbi_dbg("LIBUSB_DT_CONFIG");
+               if (!index)
+                       return _hid_get_config_descriptor(dev, data, size);
+               return LIBUSB_ERROR_INVALID_PARAM;
+       case LIBUSB_DT_STRING:
+               usbi_dbg("LIBUSB_DT_STRING");
+               return _hid_get_string_descriptor(dev, index, data, size);
+       case LIBUSB_DT_HID:
+               usbi_dbg("LIBUSB_DT_HID");
+               if (!index)
+                       return _hid_get_hid_descriptor(dev, data, size);
+               return LIBUSB_ERROR_INVALID_PARAM;
+       case LIBUSB_DT_REPORT:
+               usbi_dbg("LIBUSB_DT_REPORT");
+               if (!index)
+                       return _hid_get_report_descriptor(dev, data, size);
+               return LIBUSB_ERROR_INVALID_PARAM;
+       case LIBUSB_DT_PHYSICAL:
+               usbi_dbg("LIBUSB_DT_PHYSICAL");
+               if (HidD_GetPhysicalDescriptor(hid_handle, data, (ULONG)*size))
+                       return LIBUSB_COMPLETED;
+               return LIBUSB_ERROR_OTHER;
+       }
+       usbi_dbg("unsupported");
+       return LIBUSB_ERROR_INVALID_PARAM;
+}
+
+static int _hid_get_report(struct hid_device_priv* dev, HANDLE hid_handle, int id, void *data,
+                                                  struct windows_transfer_priv *tp, size_t *size, OVERLAPPED* overlapped)
+{
+       uint8_t *buf;
+       DWORD read_size, expected_size = (DWORD)*size;
+       int r = LIBUSB_SUCCESS;
+
+       if (tp->hid_buffer != NULL) {
+               usbi_dbg("program assertion failed: hid_buffer is not NULL");
+       }
+
+       if ((*size == 0) || (*size > MAX_HID_REPORT_SIZE)) {
+               usbi_dbg("invalid size (%d)", *size);
+               return LIBUSB_ERROR_INVALID_PARAM;
+       }
+
+       // When report IDs are not in use, add an extra byte for the report ID
+       if (id==0) {
+               expected_size++;
+       }
+
+       // Add a trailing byte to detect overflows
+       buf = (uint8_t*)calloc(expected_size+1, 1);
+       if (buf == NULL) {
+               return LIBUSB_ERROR_NO_MEM;
+       }
+       buf[0] = (uint8_t)id;   // Must be set always
+       usbi_dbg("report ID: 0x%02X", buf[0]);
+
+       // NB: HidD_GetInputReport sends an request to the device for the Input Report
+       // (and blocks until response) whereas ReadFile waits for input to be generated
+       // asynchronously
+#if !defined(USE_HIDD_FOR_REPORTS)
+       // Use ReadFile instead of HidD_GetInputReport for async I/O
+       // TODO: send a request paquet?
+       tp->hid_expected_size = expected_size;
+       if (!ReadFile(hid_handle, buf, expected_size+1, &read_size, overlapped)) {
+               if (GetLastError() != ERROR_IO_PENDING) {
+                       usbi_dbg("Failed to Read HID Input Report: %s", windows_error_str(0));
+                       safe_free(buf);
+                       return LIBUSB_ERROR_IO;
+               }
+               // Asynchronous wait
+               tp->hid_buffer = buf;
+               tp->hid_dest = data; // copy dest, as not necessarily the start of the transfer buffer
+               return LIBUSB_SUCCESS;
+       }
+#else
+       // Synchronous request for the Input Report
+       if (!HidD_GetInputReport(hid_handle, buf, expected_size)) {
+               usbi_dbg("Failed to Read HID Input Report: %s", windows_error_str(0));
+               safe_free(buf);
+               return LIBUSB_ERROR_IO;
+       }
+       read_size = expected_size;      // Can't detect overflows with this API
+#endif
+       // Transfer completed synchronously => copy and discard extra buffer
+       if (read_size == 0) {
+               usbi_dbg("program assertion failed - read completed synchronously, but no data was read");
+               *size = 0;
+       } else {
+               if (buf[0] != id) {
+                       usbi_warn(NULL, "mismatched report ID (data is %02X, parameter is %02X)", buf[0], id);
+               }
+               if ((size_t)read_size > expected_size) {
+                       r = LIBUSB_ERROR_OVERFLOW;
+                       usbi_dbg("OVERFLOW!");
+               } else {
+                       r = LIBUSB_COMPLETED;
+               }
+
+               if (id == 0) {
+                       // Discard report ID
+                       *size = MIN((size_t)read_size-1, *size);
+                       memcpy(data, buf+1, *size);
+               } else {
+                       *size = MIN((size_t)read_size, *size);
+                       memcpy(data, buf, *size);
+               }
+       }
+       safe_free(buf);
+       return r;
+}
+
+static int _hid_set_report(struct hid_device_priv* dev, HANDLE hid_handle, int id, void *data,
+                                                  struct windows_transfer_priv *tp, size_t *size, OVERLAPPED* overlapped)
+{
+       uint8_t *buf = NULL;
+       DWORD write_size= (DWORD)*size;
+
+       if (tp->hid_buffer != NULL) {
+               usbi_dbg("program assertion failed: hid_buffer is not NULL");
+       }
+
+       if ((*size == 0) || (*size > MAX_HID_REPORT_SIZE)) {
+               usbi_dbg("invalid size (%d)", *size);
+               return LIBUSB_ERROR_INVALID_PARAM;
+       }
+
+       usbi_dbg("report ID: 0x%02X", id);
+       // When report IDs are not used (i.e. when id == 0), we must add
+       // a null report ID. Otherwise, we just use original data buffer
+       if (id == 0) {
+               write_size++;
+       }
+       buf = malloc(write_size);
+       if (buf == NULL) {
+               return LIBUSB_ERROR_NO_MEM;
+       }
+       if (id == 0) {
+               buf[0] = 0;
+               memcpy(buf + 1, data, *size);
+       } else {
+               // This seems like a waste, but if we don't duplicate the
+               // data, we'll get issues when freeing hid_buffer
+               memcpy(buf, data, *size);
+               if (buf[0] != id) {
+                       usbi_warn(NULL, "mismatched report ID (data is %02X, parameter is %02X)", buf[0], id);
+               }
+       }
+
+#if !defined(USE_HIDD_FOR_REPORTS)
+       // Une WriteFile instead of HidD_SetOutputReport for async I/O
+       if (!WriteFile(hid_handle, buf, write_size, &write_size, overlapped)) {
+               if (GetLastError() != ERROR_IO_PENDING) {
+                       usbi_dbg("Failed to Write HID Output Report: %s", windows_error_str(0));
+                       safe_free(buf);
+                       return LIBUSB_ERROR_IO;
+               }
+               tp->hid_buffer = buf;
+               tp->hid_dest = NULL;
+               return LIBUSB_SUCCESS;
+       }
+#else
+       if (!HidD_SetOutputReport(hid_handle, buf, write_size)) {
+               usbi_dbg("Failed to Write HID Output Report: %s", windows_error_str(0));
+               if (id == 0) {
+                       safe_free(buf);
+               }
+               return LIBUSB_ERROR_IO;
+       }
+#endif
+       // Transfer completed synchronously
+       if (write_size == 0) {
+               usbi_dbg("program assertion failed - write completed synchronously, but no data was written");
+               *size = 0;
+       } else {
+               *size = write_size - ((id == 0)?1:0);
+       }
+       safe_free(buf);
+       return LIBUSB_COMPLETED;
+}
+
+static int _hid_get_feature(struct hid_device_priv* dev, HANDLE hid_handle, int id, void *data, size_t *size)
+{
+       uint8_t *buf = (uint8_t*)data;  // default with report ID is to use data
+       ULONG read_size = (ULONG)*size;
+       int r = LIBUSB_ERROR_OTHER;
+       uint32_t err;
+
+       if ((*size == 0) || (*size > MAX_HID_REPORT_SIZE)) {
+               usbi_dbg("invalid size (%d)", *size);
+               return LIBUSB_ERROR_INVALID_PARAM;
+       }
+
+       // When report IDs are not in use, we must prefix an extra zero ID
+       if (id == 0) {
+               read_size++;
+               buf = (uint8_t*)calloc(1, read_size);
+               if (buf == NULL) {
+                       return LIBUSB_ERROR_NO_MEM;
+               }
+       }
+       buf[0] = (uint8_t)id;
+       usbi_dbg("report ID: 0x%02X", buf[0]);
+
+       if (HidD_GetFeature(hid_handle, buf, read_size)) {
+               if (buf[0] != id) {
+                       usbi_warn(NULL, "mismatched report ID (data is %02X, parameter is %02X)", buf[0], id);
+               }
+               if (id == 0) {
+                       memcpy(data, buf+1, *size);
+               }
+               r = LIBUSB_COMPLETED;
+       } else {
+               err = GetLastError();
+               switch (err) {
+               case ERROR_INVALID_FUNCTION:
+                       r = LIBUSB_ERROR_NOT_FOUND;
+                       break;
+               default:
+                       usbi_dbg("error %s", windows_error_str(err));
+                       r = LIBUSB_ERROR_OTHER;
+                       break;
+               }
+       }
+       if (id == 0) {
+               safe_free(buf);
+       }
+       return r;
+}
+
+static int _hid_set_feature(struct hid_device_priv* dev, HANDLE hid_handle, int id, void *data, size_t *size)
+{
+       uint8_t *buf = (uint8_t*)data;
+       uint32_t err;
+       int r = LIBUSB_ERROR_OTHER;
+       ULONG write_size = (ULONG)*size;
+
+       if ((*size == 0) || (*size > MAX_HID_REPORT_SIZE)) {
+               usbi_dbg("invalid size (%d)", *size);
+               return LIBUSB_ERROR_INVALID_PARAM;
+       }
+
+       if (id == 0) {
+               write_size++;
+               buf = (uint8_t*)calloc(write_size, 1);
+               if (buf == NULL) {
+                       return LIBUSB_ERROR_NO_MEM;
+               }
+               memcpy(buf+1, data, *size);
+               buf[0] = (uint8_t)id;
+       } else if (buf[0] != id) {
+               usbi_warn(NULL, "mismatched report ID (data is %02X, parameter is %02X)", buf[0], id);
+               return LIBUSB_ERROR_INVALID_PARAM;
+       }
+
+       usbi_dbg("report ID: 0x%02X", buf[0]);
+
+       if (HidD_SetFeature(hid_handle, buf, write_size)) {
+               r = LIBUSB_COMPLETED;
+       } else {
+               err = GetLastError();
+               switch (err) {
+               case ERROR_INVALID_FUNCTION:
+                       r = LIBUSB_ERROR_NOT_FOUND;
+               default:
+                       usbi_dbg("error %s", windows_error_str(err));
+                       r = LIBUSB_ERROR_OTHER;
+               }
+       }
+       if (id == 0) {
+               safe_free(buf);
+       }
+       return r;
+}
+
+static int _hid_class_request(struct hid_device_priv* dev, HANDLE hid_handle, int request_type,
+                                                         int request, int value, int index, void *data, struct windows_transfer_priv *tp,
+                                                         size_t *size, OVERLAPPED* overlapped)
+{
+       int report_type = (value >> 8) & 0xFF;
+       int report_id = value & 0xFF;
+
+       if ( (LIBUSB_REQ_RECIPIENT(request_type) != LIBUSB_RECIPIENT_INTERFACE)
+         && (LIBUSB_REQ_RECIPIENT(request_type) != LIBUSB_RECIPIENT_DEVICE) )
+               return LIBUSB_ERROR_INVALID_PARAM;
+
+       if (LIBUSB_REQ_OUT(request_type)
+               && request == HID_REQ_SET_REPORT
+               && report_type == HID_REPORT_TYPE_OUTPUT)
+               return _hid_set_report(dev, hid_handle, report_id, data, tp, size, overlapped);
+
+       if (LIBUSB_REQ_IN(request_type)
+               && request == HID_REQ_GET_REPORT
+               && report_type == HID_REPORT_TYPE_INPUT)
+               return _hid_get_report(dev, hid_handle, report_id, data, tp, size, overlapped);
+
+       if (LIBUSB_REQ_OUT(request_type)
+               && request == HID_REQ_SET_REPORT
+               && report_type == HID_REPORT_TYPE_FEATURE)
+               return _hid_set_feature(dev, hid_handle, report_id, data, size);
+
+       if (LIBUSB_REQ_IN(request_type)
+               && request == HID_REQ_GET_REPORT
+               && report_type == HID_REPORT_TYPE_FEATURE)
+               return _hid_get_feature(dev, hid_handle, report_id, data, size);
+
+       return LIBUSB_ERROR_INVALID_PARAM;
+}
+
+
+/*
+ * HID API functions
+ */
+static int hid_init(struct libusb_context *ctx)
+{
+       DLL_LOAD(hid.dll, HidD_GetAttributes, TRUE);
+       DLL_LOAD(hid.dll, HidD_GetHidGuid, TRUE);
+       DLL_LOAD(hid.dll, HidD_GetPreparsedData, TRUE);
+       DLL_LOAD(hid.dll, HidD_FreePreparsedData, TRUE);
+       DLL_LOAD(hid.dll, HidD_GetManufacturerString, TRUE);
+       DLL_LOAD(hid.dll, HidD_GetProductString, TRUE);
+       DLL_LOAD(hid.dll, HidD_GetSerialNumberString, TRUE);
+       DLL_LOAD(hid.dll, HidP_GetCaps, TRUE);
+       DLL_LOAD(hid.dll, HidD_SetNumInputBuffers, TRUE);
+       DLL_LOAD(hid.dll, HidD_SetFeature, TRUE);
+       DLL_LOAD(hid.dll, HidD_GetFeature, TRUE);
+       DLL_LOAD(hid.dll, HidD_GetPhysicalDescriptor, TRUE);
+       DLL_LOAD(hid.dll, HidD_GetInputReport, FALSE);
+       DLL_LOAD(hid.dll, HidD_SetOutputReport, FALSE);
+       DLL_LOAD(hid.dll, HidD_FlushQueue, TRUE);
+       DLL_LOAD(hid.dll, HidP_GetValueCaps, TRUE);
+
+       api_hid_available = true;
+       return LIBUSB_SUCCESS;
+}
+
+static int hid_exit(void)
+{
+       return LIBUSB_SUCCESS;
+}
+
+// NB: open and close must ensure that they only handle interface of
+// the right API type, as these functions can be called wholesale from
+// composite_open(), with interfaces belonging to different APIs
+static int hid_open(struct libusb_device_handle *dev_handle)
+{
+       struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev);
+       struct windows_device_priv *priv = __device_priv(dev_handle->dev);
+       struct windows_device_handle_priv *handle_priv = __device_handle_priv(dev_handle);
+
+       HIDD_ATTRIBUTES hid_attributes;
+       PHIDP_PREPARSED_DATA preparsed_data = NULL;
+       HIDP_CAPS capabilities;
+       HIDP_VALUE_CAPS *value_caps;
+
+       HANDLE hid_handle = INVALID_HANDLE_VALUE;
+       int i, j;
+       // report IDs handling
+       ULONG size[3];
+       char* type[3] = {"input", "output", "feature"};
+       int nb_ids[2];  // zero and nonzero report IDs
+
+       CHECK_HID_AVAILABLE;
+       if (priv->hid == NULL) {
+               usbi_err(ctx, "program assertion failed - private HID structure is unitialized");
+               return LIBUSB_ERROR_NOT_FOUND;
+       }
+
+       for (i = 0; i < USB_MAXINTERFACES; i++) {
+               if ( (priv->usb_interface[i].path != NULL)
+                 && (priv->usb_interface[i].apib->id == USB_API_HID) ) {
+                       hid_handle = CreateFileA(priv->usb_interface[i].path, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ,
+                               NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
+                       /*
+                        * http://www.lvr.com/hidfaq.htm: Why do I receive "Access denied" when attempting to access my HID?
+                        * "Windows 2000 and later have exclusive read/write access to HIDs that are configured as a system
+                        * keyboards or mice. An application can obtain a handle to a system keyboard or mouse by not
+                        * requesting READ or WRITE access with CreateFile. Applications can then use HidD_SetFeature and
+                        * HidD_GetFeature (if the device supports Feature reports)."
+                        */
+                       if (hid_handle == INVALID_HANDLE_VALUE) {
+                               usbi_warn(ctx, "could not open HID device in R/W mode (keyboard or mouse?) - trying without");
+                               hid_handle = CreateFileA(priv->usb_interface[i].path, 0, FILE_SHARE_WRITE | FILE_SHARE_READ,
+                                       NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL);
+                               if (hid_handle == INVALID_HANDLE_VALUE) {
+                                       usbi_err(ctx, "could not open device %s (interface %d): %s", priv->path, i, windows_error_str(0));
+                                       switch(GetLastError()) {
+                                       case ERROR_FILE_NOT_FOUND:      // The device was disconnected
+                                               return LIBUSB_ERROR_NO_DEVICE;
+                                       case ERROR_ACCESS_DENIED:
+                                               return LIBUSB_ERROR_ACCESS;
+                                       default:
+                                               return LIBUSB_ERROR_IO;
+                                       }
+                               }
+                               priv->usb_interface[i].restricted_functionality = true;
+                       }
+                       handle_priv->interface_handle[i].api_handle = hid_handle;
+               }
+       }
+
+       hid_attributes.Size = sizeof(hid_attributes);
+       do {
+               if (!HidD_GetAttributes(hid_handle, &hid_attributes)) {
+                       usbi_err(ctx, "could not gain access to HID top collection (HidD_GetAttributes)");
+                       break;
+               }
+
+               priv->hid->vid = hid_attributes.VendorID;
+               priv->hid->pid = hid_attributes.ProductID;
+
+               // Set the maximum available input buffer size
+               for (i=32; HidD_SetNumInputBuffers(hid_handle, i); i*=2);
+               usbi_dbg("set maximum input buffer size to %d", i/2);
+
+               // Get the maximum input and output report size
+               if (!HidD_GetPreparsedData(hid_handle, &preparsed_data) || !preparsed_data) {
+                       usbi_err(ctx, "could not read HID preparsed data (HidD_GetPreparsedData)");
+                       break;
+               }
+               if (HidP_GetCaps(preparsed_data, &capabilities) != HIDP_STATUS_SUCCESS) {
+                       usbi_err(ctx, "could not parse HID capabilities (HidP_GetCaps)");
+                       break;
+               }
+
+               // Find out if interrupt will need report IDs
+               size[0] = capabilities.NumberInputValueCaps;
+               size[1] = capabilities.NumberOutputValueCaps;
+               size[2] = capabilities.NumberFeatureValueCaps;
+               for (j=0; j<3; j++) {
+                       usbi_dbg("%d HID %s report value(s) found", size[j], type[j]);
+                       priv->hid->uses_report_ids[j] = false;
+                       if (size[j] > 0) {
+                               value_caps = malloc(size[j] * sizeof(HIDP_VALUE_CAPS));
+                               if ( (value_caps != NULL)
+                                 && (HidP_GetValueCaps(j, value_caps, &size[j], preparsed_data) == HIDP_STATUS_SUCCESS)
+                                 && (size[j] >= 1) ) {
+                                       nb_ids[0] = 0;
+                                       nb_ids[1] = 0;
+                                       for (i=0; i<(int)size[j]; i++) {
+                                               usbi_dbg("  Report ID: 0x%02X", value_caps[i].ReportID);
+                                               if (value_caps[i].ReportID != 0) {
+                                                       nb_ids[1]++;
+                                               } else {
+                                                       nb_ids[0]++;
+                                               }
+                                       }
+                                       if (nb_ids[1] != 0) {
+                                               if (nb_ids[0] != 0) {
+                                                       usbi_warn(ctx, "program assertion failed: zero and nonzero report IDs used for %s",
+                                                               type[j]);
+                                               }
+                                               priv->hid->uses_report_ids[j] = true;
+                                       }
+                               } else {
+                                       usbi_warn(ctx, "  could not process %s report IDs", type[j]);
+                               }
+                               safe_free(value_caps);
+                       }
+               }
+
+               // Set the report sizes
+               priv->hid->input_report_size = capabilities.InputReportByteLength;
+               priv->hid->output_report_size = capabilities.OutputReportByteLength;
+               priv->hid->feature_report_size = capabilities.FeatureReportByteLength;
+
+               // Fetch string descriptors
+               priv->hid->string_index[0] = priv->dev_descriptor.iManufacturer;
+               if (priv->hid->string_index[0] != 0) {
+                       HidD_GetManufacturerString(hid_handle, priv->hid->string[0],
+                               sizeof(priv->hid->string[0]));
+               } else {
+                       priv->hid->string[0][0] = 0;
+               }
+               priv->hid->string_index[1] = priv->dev_descriptor.iProduct;
+               if (priv->hid->string_index[1] != 0) {
+                       HidD_GetProductString(hid_handle, priv->hid->string[1],
+                               sizeof(priv->hid->string[1]));
+               } else {
+                       priv->hid->string[1][0] = 0;
+               }
+               priv->hid->string_index[2] = priv->dev_descriptor.iSerialNumber;
+               if (priv->hid->string_index[2] != 0) {
+                       HidD_GetSerialNumberString(hid_handle, priv->hid->string[2],
+                               sizeof(priv->hid->string[2]));
+               } else {
+                       priv->hid->string[2][0] = 0;
+               }
+       } while(0);
+
+       if (preparsed_data) {
+               HidD_FreePreparsedData(preparsed_data);
+       }
+
+       return LIBUSB_SUCCESS;
+}
+
+static void hid_close(struct libusb_device_handle *dev_handle)
+{
+       struct windows_device_priv *priv = __device_priv(dev_handle->dev);
+       struct windows_device_handle_priv *handle_priv = __device_handle_priv(dev_handle);
+       HANDLE file_handle;
+       int i;
+
+       if (!api_hid_available)
+               return;
+
+       for (i = 0; i < USB_MAXINTERFACES; i++) {
+               if (priv->usb_interface[i].apib->id == USB_API_HID) {
+                       file_handle = handle_priv->interface_handle[i].api_handle;
+                       if ( (file_handle != 0) && (file_handle != INVALID_HANDLE_VALUE)) {
+                               CloseHandle(file_handle);
+                       }
+               }
+       }
+}
+
+static int hid_claim_interface(struct libusb_device_handle *dev_handle, int iface)
+{
+       struct windows_device_handle_priv *handle_priv = __device_handle_priv(dev_handle);
+       struct windows_device_priv *priv = __device_priv(dev_handle->dev);
+
+       CHECK_HID_AVAILABLE;
+
+       // NB: Disconnection detection is not possible in this function
+       if (priv->usb_interface[iface].path == NULL) {
+               return LIBUSB_ERROR_NOT_FOUND;  // invalid iface
+       }
+
+       // We use dev_handle as a flag for interface claimed
+       if (handle_priv->interface_handle[iface].dev_handle == INTERFACE_CLAIMED) {
+               return LIBUSB_ERROR_BUSY;       // already claimed
+       }
+
+       handle_priv->interface_handle[iface].dev_handle = INTERFACE_CLAIMED;
+
+       usbi_dbg("claimed interface %d", iface);
+       handle_priv->active_interface = iface;
+
+       return LIBUSB_SUCCESS;
+}
+
+static int hid_release_interface(struct libusb_device_handle *dev_handle, int iface)
+{
+       struct windows_device_handle_priv *handle_priv = __device_handle_priv(dev_handle);
+       struct windows_device_priv *priv = __device_priv(dev_handle->dev);
+
+       CHECK_HID_AVAILABLE;
+
+       if (priv->usb_interface[iface].path == NULL) {
+               return LIBUSB_ERROR_NOT_FOUND;  // invalid iface
+       }
+
+       if (handle_priv->interface_handle[iface].dev_handle != INTERFACE_CLAIMED) {
+               return LIBUSB_ERROR_NOT_FOUND;  // invalid iface
+       }
+
+       handle_priv->interface_handle[iface].dev_handle = INVALID_HANDLE_VALUE;
+
+       return LIBUSB_SUCCESS;
+}
+
+static int hid_set_interface_altsetting(struct libusb_device_handle *dev_handle, int iface, int altsetting)
+{
+       struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev);
+
+       CHECK_HID_AVAILABLE;
+
+       if (altsetting > 255) {
+               return LIBUSB_ERROR_INVALID_PARAM;
+       }
+
+       if (altsetting != 0) {
+               usbi_err(ctx, "set interface altsetting not supported for altsetting >0");
+               return LIBUSB_ERROR_NOT_SUPPORTED;
+       }
+
+       return LIBUSB_SUCCESS;
+}
+
+static int hid_submit_control_transfer(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_handle_priv *handle_priv = __device_handle_priv(transfer->dev_handle);
+       struct windows_device_priv *priv = __device_priv(transfer->dev_handle->dev);
+       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;
+       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;
+
+       if (size > MAX_CTRL_BUFFER_LENGTH) {
+               return LIBUSB_ERROR_INVALID_PARAM;
+       }
+
+       current_interface = get_valid_interface(transfer->dev_handle, USB_API_HID);
+       if (current_interface < 0) {
+#if defined(AUTO_CLAIM)
+               if (auto_claim(transfer, &current_interface, USB_API_HID) != LIBUSB_SUCCESS) {
+                       return LIBUSB_ERROR_NOT_FOUND;
+               }
+#else
+               usbi_warn(ctx, "no interface available for control transfer");
+               return LIBUSB_ERROR_NOT_FOUND;
+#endif
+       }
+
+       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, _O_RDONLY);
+       if (wfd.fd < 0) {
+               return LIBUSB_ERROR_NO_MEM;
+       }
+
+       switch(LIBUSB_REQ_TYPE(setup->request_type)) {
+       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->request_type),
+                               (setup->value >> 8) & 0xFF, setup->value & 0xFF, transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE, &size);
+                       break;
+               case LIBUSB_REQUEST_GET_CONFIGURATION:
+                       r = windows_get_configuration(transfer->dev_handle, &config);
+                       if (r == LIBUSB_SUCCESS) {
+                               size = 1;
+                               ((uint8_t*)transfer->buffer)[LIBUSB_CONTROL_SETUP_SIZE] = (uint8_t)config;
+                               r = LIBUSB_COMPLETED;
+                       }
+                       break;
+               case LIBUSB_REQUEST_SET_CONFIGURATION:
+                       if (setup->value == priv->active_config) {
+                               r = LIBUSB_COMPLETED;
+                       } else {
+                               usbi_warn(ctx, "cannot set configuration other than the default one");
+                               r = LIBUSB_ERROR_INVALID_PARAM;
+                       }
+                       break;
+               case LIBUSB_REQUEST_GET_INTERFACE:
+                       size = 1;
+                       ((uint8_t*)transfer->buffer)[LIBUSB_CONTROL_SETUP_SIZE] = 0;
+                       r = LIBUSB_COMPLETED;
+                       break;
+               case LIBUSB_REQUEST_SET_INTERFACE:
+                       r = hid_set_interface_altsetting(transfer->dev_handle, setup->index, setup->value);
+                       if (r == LIBUSB_SUCCESS) {
+                               r = LIBUSB_COMPLETED;
+                       }
+                       break;
+               default:
+                       usbi_warn(ctx, "unsupported HID control request");
+                       r = LIBUSB_ERROR_INVALID_PARAM;
+                       break;
+               }
+               break;
+       case LIBUSB_REQUEST_TYPE_CLASS:
+               r =_hid_class_request(priv->hid, wfd.handle, setup->request_type, setup->request, setup->value,
+                       setup->index, transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE, transfer_priv,
+                       &size, wfd.overlapped);
+               break;
+       default:
+               usbi_warn(ctx, "unsupported HID control request");
+               r = LIBUSB_ERROR_INVALID_PARAM;
+               break;
+       }
+
+       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;
+               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.fd);
+       }
+
+       return r;
+}
+
+static int hid_submit_bulk_transfer(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 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;
+       bool direction_in, ret;
+       int current_interface, length;
+       DWORD size;
+       int r = LIBUSB_SUCCESS;
+
+       CHECK_HID_AVAILABLE;
+
+       transfer_priv->pollable_fd = INVALID_WINFD;
+       transfer_priv->hid_dest = NULL;
+       safe_free(transfer_priv->hid_buffer);
+
+       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");
+               return LIBUSB_ERROR_NOT_FOUND;
+       }
+
+       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?_O_RDONLY:_O_WRONLY);
+       // Always use the handle returned from usbi_create_fd (wfd.handle)
+       if (wfd.fd < 0) {
+               return LIBUSB_ERROR_NO_MEM;
+       }
+
+       // If report IDs are not in use, an extra prefix byte must be added
+       if ( ((direction_in) && (!priv->hid->uses_report_ids[0]))
+         || ((!direction_in) && (!priv->hid->uses_report_ids[1])) ) {
+               length = transfer->length+1;
+       } else {
+               length = transfer->length;
+       }
+       // Add a trailing byte to detect overflows on input
+       transfer_priv->hid_buffer = (uint8_t*)calloc(length+1, 1);
+       if (transfer_priv->hid_buffer == NULL) {
+               return LIBUSB_ERROR_NO_MEM;
+       }
+       transfer_priv->hid_expected_size = length;
+
+       if (direction_in) {
+               transfer_priv->hid_dest = transfer->buffer;
+               usbi_dbg("reading %d bytes (report ID: 0x%02X)", length, transfer_priv->hid_buffer[0]);
+               ret = ReadFile(wfd.handle, transfer_priv->hid_buffer, length+1, &size, wfd.overlapped);
+       } else {
+               if (!priv->hid->uses_report_ids[1]) {
+                       memcpy(transfer_priv->hid_buffer+1, transfer->buffer, transfer->length);
+               } else {
+                       // We could actually do without the calloc and memcpy in this case
+                       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);
+       }
+       if (!ret) {
+               if (GetLastError() != ERROR_IO_PENDING) {
+                       usbi_err(ctx, "HID transfer failed: %s", windows_error_str(0));
+                       usbi_free_fd(wfd.fd);
+                       safe_free(transfer_priv->hid_buffer);
+                       return LIBUSB_ERROR_IO;
+               }
+       } else {
+               // Only write operations that completed synchronously need to free up
+               // hid_buffer. For reads, copy_transfer_data() handles that process.
+               if (!direction_in) {
+                       safe_free(transfer_priv->hid_buffer);
+               }
+               if (size == 0) {
+                       usbi_err(ctx, "program assertion failed - no data was transferred");
+                       size = 1;
+               }
+               if (size > (size_t)length) {
+                       usbi_err(ctx, "OVERFLOW!");
+                       r = LIBUSB_ERROR_OVERFLOW;
+               }
+               wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY;
+               wfd.overlapped->InternalHigh = size;
+       }
+
+       transfer_priv->pollable_fd = wfd;
+       transfer_priv->interface_number = (uint8_t)current_interface;
+
+       return r;
+}
+
+static int hid_abort_transfers(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_handle_priv *handle_priv = __device_handle_priv(transfer->dev_handle);
+       HANDLE hid_handle;
+       int current_interface;
+
+       CHECK_HID_AVAILABLE;
+
+       current_interface = transfer_priv->interface_number;
+       hid_handle = handle_priv->interface_handle[current_interface].api_handle;
+       CancelIo(hid_handle);
+
+       return LIBUSB_SUCCESS;
+}
+
+static int hid_reset_device(struct libusb_device_handle *dev_handle)
+{
+       struct windows_device_handle_priv *handle_priv = __device_handle_priv(dev_handle);
+       HANDLE hid_handle;
+       int current_interface;
+
+       CHECK_HID_AVAILABLE;
+
+       // Flushing the queues on all interfaces is the best we can achieve
+       for (current_interface = 0; current_interface < USB_MAXINTERFACES; current_interface++) {
+               hid_handle = handle_priv->interface_handle[current_interface].api_handle;
+               if ((hid_handle != 0) && (hid_handle != INVALID_HANDLE_VALUE)) {
+                       HidD_FlushQueue(hid_handle);
+               }
+       }
+       return LIBUSB_SUCCESS;
+}
+
+static int hid_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint)
+{
+       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);
+       HANDLE hid_handle;
+       int current_interface;
+
+       CHECK_HID_AVAILABLE;
+
+       current_interface = interface_by_endpoint(priv, handle_priv, endpoint);
+       if (current_interface < 0) {
+               usbi_err(ctx, "unable to match endpoint to an open interface - cannot clear");
+               return LIBUSB_ERROR_NOT_FOUND;
+       }
+
+       usbi_dbg("matched endpoint %02X with interface %d", endpoint, current_interface);
+       hid_handle = handle_priv->interface_handle[current_interface].api_handle;
+
+       // No endpoint selection with Microsoft's implementation, so we try to flush the
+       // whole interface. Should be OK for most case scenarios
+       if (!HidD_FlushQueue(hid_handle)) {
+               usbi_err(ctx, "Flushing of HID queue failed: %s", windows_error_str(0));
+               // Device was probably disconnected
+               return LIBUSB_ERROR_NO_DEVICE;
+       }
+
+       return LIBUSB_SUCCESS;
+}
+
+// This extra function is only needed for HID
+static int hid_copy_transfer_data(struct usbi_transfer *itransfer, uint32_t io_size) {
+       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);
+       int r = LIBUSB_TRANSFER_COMPLETED;
+       uint32_t corrected_size = io_size;
+
+       if (transfer_priv->hid_buffer != NULL) {
+               // If we have a valid hid_buffer, it means the transfer was async
+               if (transfer_priv->hid_dest != NULL) {  // Data readout
+                       // First, check for overflow
+                       if (corrected_size > transfer_priv->hid_expected_size) {
+                               usbi_err(ctx, "OVERFLOW!");
+                               corrected_size = (uint32_t)transfer_priv->hid_expected_size;
+                               r = LIBUSB_TRANSFER_OVERFLOW;
+                       }
+
+                       if (transfer_priv->hid_buffer[0] == 0) {
+                               // Discard the 1 byte report ID prefix
+                               corrected_size--;
+                               memcpy(transfer_priv->hid_dest, transfer_priv->hid_buffer+1, corrected_size);
+                       } else {
+                               memcpy(transfer_priv->hid_dest, transfer_priv->hid_buffer, corrected_size);
+                       }
+                       transfer_priv->hid_dest = NULL;
+               }
+               // For write, we just need to free the hid buffer
+               safe_free(transfer_priv->hid_buffer);
+       }
+       itransfer->transferred += corrected_size;
+       return r;
+}
+
+
+/*
+ * Composite API functions
+ */
+static int composite_init(struct libusb_context *ctx)
+{
+       return LIBUSB_SUCCESS;
+}
+
+static int composite_exit(void)
+{
+       return LIBUSB_SUCCESS;
+}
+
+static int composite_open(struct libusb_device_handle *dev_handle)
+{
+       struct windows_device_priv *priv = __device_priv(dev_handle->dev);
+       unsigned api;
+       int r;
+       uint8_t flag = 1<<USB_API_WINUSB;
+
+       for (api=USB_API_WINUSB; api<USB_API_MAX; api++) {
+               if (priv->composite_api_flags & flag) {
+                       r = usb_api_backend[api].open(dev_handle);
+                       if (r != LIBUSB_SUCCESS) {
+                               return r;
+                       }
+               }
+               flag <<= 1;
+       }
+       return LIBUSB_SUCCESS;
+}
+
+static void composite_close(struct libusb_device_handle *dev_handle)
+{
+       struct windows_device_priv *priv = __device_priv(dev_handle->dev);
+       unsigned api;
+       uint8_t flag = 1<<USB_API_WINUSB;
+
+       for (api=USB_API_WINUSB; api<USB_API_MAX; api++) {
+               if (priv->composite_api_flags & flag) {
+                       usb_api_backend[api].close(dev_handle);
+               }
+               flag <<= 1;
+       }
+}
+
+static int composite_claim_interface(struct libusb_device_handle *dev_handle, int iface)
+{
+       struct windows_device_priv *priv = __device_priv(dev_handle->dev);
+       return priv->usb_interface[iface].apib->claim_interface(dev_handle, iface);
+}
+
+static int composite_set_interface_altsetting(struct libusb_device_handle *dev_handle, int iface, int altsetting)
+{
+       struct windows_device_priv *priv = __device_priv(dev_handle->dev);
+       return priv->usb_interface[iface].apib->set_interface_altsetting(dev_handle, iface, altsetting);
+}
+
+static int composite_release_interface(struct libusb_device_handle *dev_handle, int iface)
+{
+       struct windows_device_priv *priv = __device_priv(dev_handle->dev);
+       return priv->usb_interface[iface].apib->release_interface(dev_handle, iface);
+}
+
+static int composite_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_device_priv *priv = __device_priv(transfer->dev_handle->dev);
+       int i, pass;
+
+       // Interface shouldn't matter for control, but it does in practice, with Windows'
+       // restrictions with regards to accessing HID keyboards and mice. Try a 2 pass approach
+       for (pass = 0; pass < 2; pass++) {
+               for (i=0; i<USB_MAXINTERFACES; i++) {
+                       if (priv->usb_interface[i].path != NULL) {
+                               if ((pass == 0) && (priv->usb_interface[i].restricted_functionality)) {
+                                       usbi_dbg("trying to skip restricted interface #%d (HID keyboard or mouse?)", i);
+                                       continue;
+                               }
+                               usbi_dbg("using interface %d", i);
+                               return priv->usb_interface[i].apib->submit_control_transfer(itransfer);
+                       }
+               }
+       }
+
+       usbi_err(ctx, "no libusb supported interfaces to complete request");
+       return LIBUSB_ERROR_NOT_FOUND;
+}
+
+static int composite_submit_bulk_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_device_handle_priv *handle_priv = __device_handle_priv(transfer->dev_handle);
+       struct windows_device_priv *priv = __device_priv(transfer->dev_handle->dev);
+       int current_interface;
+
+       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");
+               return LIBUSB_ERROR_NOT_FOUND;
+       }
+
+       return priv->usb_interface[current_interface].apib->submit_bulk_transfer(itransfer);
+}
+
+static int composite_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_device_handle_priv *handle_priv = __device_handle_priv(transfer->dev_handle);
+       struct windows_device_priv *priv = __device_priv(transfer->dev_handle->dev);
+       int current_interface;
+
+       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");
+               return LIBUSB_ERROR_NOT_FOUND;
+       }
+
+       return priv->usb_interface[current_interface].apib->submit_iso_transfer(itransfer);
+}
+
+static int composite_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint)
+{
+       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);
+       int current_interface;
+
+       current_interface = interface_by_endpoint(priv, handle_priv, endpoint);
+       if (current_interface < 0) {
+               usbi_err(ctx, "unable to match endpoint to an open interface - cannot clear");
+               return LIBUSB_ERROR_NOT_FOUND;
+       }
+
+       return priv->usb_interface[current_interface].apib->clear_halt(dev_handle, endpoint);
+}
+
+static int composite_abort_control(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);
+
+       return priv->usb_interface[transfer_priv->interface_number].apib->abort_control(itransfer);
+}
+
+static int composite_abort_transfers(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);
+
+       return priv->usb_interface[transfer_priv->interface_number].apib->abort_transfers(itransfer);
+}
+
+static int composite_reset_device(struct libusb_device_handle *dev_handle)
+{
+       struct windows_device_priv *priv = __device_priv(dev_handle->dev);
+       unsigned api;
+       int r;
+       uint8_t flag = 1<<USB_API_WINUSB;
+
+       for (api=USB_API_WINUSB; api<USB_API_MAX; api++) {
+               if (priv->composite_api_flags & flag) {
+                       r = usb_api_backend[api].reset_device(dev_handle);
+                       if (r != LIBUSB_SUCCESS) {
+                               return r;
+                       }
+               }
+               flag <<= 1;
+       }
+       return LIBUSB_SUCCESS;
+}
+
+static int composite_copy_transfer_data(struct usbi_transfer *itransfer, uint32_t io_size)
+{
+       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);
+
+       return priv->usb_interface[transfer_priv->interface_number].apib->copy_transfer_data(itransfer, io_size);
+}
diff --git a/libusb/os/windows_usb.h b/libusb/os/windows_usb.h
new file mode 100644 (file)
index 0000000..8639b70
--- /dev/null
@@ -0,0 +1,759 @@
+/*
+ * Windows backend for libusb 1.0
+ * Copyright (C) 2009-2010 Pete Batard <pbatard@gmail.com>
+ * With contributions from Michael Plante, Orin Eman et al.
+ * Parts of this code adapted from libusb-win32-v1 by Stephan Meyer
+ * Major code testing contribution by Xiaofan Chen
+ *
+ * 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
+#pragma warning(disable:4100) // unreferenced formal parameter
+#pragma warning(disable:4214) // bit field types other than int
+#pragma warning(disable:4201) // nameless struct/union
+#endif
+
+// Windows API default is uppercase - ugh!
+#if !defined(bool)
+#define bool BOOL
+#endif
+#if !defined(true)
+#define true TRUE
+#endif
+#if !defined(false)
+#define false FALSE
+#endif
+
+#if !defined(libusb_bus_t)
+#define libusb_bus_t uint8_t
+#define LIBUSB_BUS_MAX UINT8_MAX
+#endif
+#if !defined(libusb_devaddr_t)
+#define libusb_devaddr_t uint8_t
+#define LIBUSB_DEVADDR_MAX UINT8_MAX
+#endif
+
+// Missing from MSVC6 setupapi.h
+#if !defined(SPDRP_ADDRESS)
+#define SPDRP_ADDRESS  28
+#endif
+#if !defined(SPDRP_INSTALL_STATE)
+#define SPDRP_INSTALL_STATE    34
+#endif
+
+#if defined(__CYGWIN__ )
+// cygwin produces a warning unless these prototypes are defined
+extern int _snprintf(char *buffer, size_t count, const char *format, ...);
+extern char *_strdup(const char *strSource);
+// _beginthreadex is MSVCRT => unavailable for cygwin. Fallback to using CreateThread
+#define _beginthreadex(a, b, c, d, e, f) CreateThread(a, b, (LPTHREAD_START_ROUTINE)c, d, e, f)
+#endif
+#define safe_free(p) do {if (p != NULL) {free(p); p = NULL;}} while(0)
+#define safe_closehandle(h) do {if (h != INVALID_HANDLE_VALUE) {CloseHandle(h); h = INVALID_HANDLE_VALUE;}} while(0)
+#define safe_strncpy(dst, dst_max, src, count) strncpy(dst, src, min(count, dst_max - 1))
+#define safe_strcpy(dst, dst_max, src) safe_strncpy(dst, dst_max, src, strlen(src)+1)
+#define safe_strncat(dst, dst_max, src, count) strncat(dst, src, min(count, dst_max - strlen(dst) - 1))
+#define safe_strcat(dst, dst_max, src) safe_strncat(dst, dst_max, src, strlen(src)+1)
+#define safe_strcmp(str1, str2) strcmp(((str1==NULL)?"<NULL>":str1), ((str2==NULL)?"<NULL>":str2))
+#define safe_strncmp(str1, str2, count) strncmp(((str1==NULL)?"<NULL>":str1), ((str2==NULL)?"<NULL>":str2), count)
+#define safe_strlen(str) ((str==NULL)?0:strlen(str))
+#define safe_sprintf _snprintf
+#define safe_unref_device(dev) do {if (dev != NULL) {libusb_unref_device(dev); dev = NULL;}} while(0)
+#define wchar_to_utf8_ms(wstr, str, strlen) WideCharToMultiByte(CP_UTF8, 0, wstr, -1, str, strlen, NULL, NULL)
+inline void upperize(char* str) {
+       size_t i;
+       if (str == NULL) return;
+       for (i=0; i<strlen(str); i++)
+               str[i] = (char)toupper((int)str[i]);
+}
+
+#define MAX_CTRL_BUFFER_LENGTH      4096
+#define MAX_USB_DEVICES             256
+#define MAX_USB_STRING_LENGTH       128
+#define MAX_HID_REPORT_SIZE         1024
+#define MAX_HID_DESCRIPTOR_SIZE     256
+#define MAX_GUID_STRING_LENGTH      40
+#define MAX_PATH_LENGTH             128
+#define MAX_KEY_LENGTH              256
+#define MAX_TIMER_SEMAPHORES        128
+#define TIMER_REQUEST_RETRY_MS         100
+#define ERR_BUFFER_SIZE             256
+
+// Handle code for HID interface that have been claimed ("dibs")
+#define INTERFACE_CLAIMED           ((HANDLE)(intptr_t)0xD1B5)
+// Additional return code for HID operations that completed synchronously
+#define LIBUSB_COMPLETED            (LIBUSB_SUCCESS + 1)
+
+// http://msdn.microsoft.com/en-us/library/bb663109.aspx
+// http://msdn.microsoft.com/en-us/library/bb663093.aspx
+#if !defined(GUID_DEVINTERFACE_USB_HOST_CONTROLLER)
+const GUID GUID_DEVINTERFACE_USB_HOST_CONTROLLER = { 0x3ABF6F2D, 0x71C4, 0x462A, {0x8A, 0x92, 0x1E, 0x68, 0x61, 0xE6, 0xAF, 0x27} };
+#endif
+#if !defined(GUID_DEVINTERFACE_USB_DEVICE)
+const GUID GUID_DEVINTERFACE_USB_DEVICE = { 0xA5DCBF10, 0x6530, 0x11D2, {0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED} };
+#endif
+
+
+/*
+ * Multiple USB API backend support
+ */
+#define USB_API_UNSUPPORTED 0
+#define USB_API_COMPOSITE   1
+#define USB_API_WINUSB      2
+#define USB_API_HID         3
+#define USB_API_MAX         4
+
+const GUID CLASS_GUID_UNSUPPORTED   = { 0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x57, 0xDA} };
+const GUID CLASS_GUID_HID           = { 0x745A17A0, 0x74D3, 0x11D0, {0xB6, 0xFE, 0x00, 0xA0, 0xC9, 0x0F, 0x57, 0xDA} };
+const GUID CLASS_GUID_LIBUSB_WINUSB = { 0x78A1C341, 0x4539, 0x11D3, {0xB8, 0x8D, 0x00, 0xC0, 0x4F, 0xAD, 0x51, 0x71} };
+const GUID CLASS_GUID_COMPOSITE     = { 0x36FC9E60, 0xC465, 0x11cF, {0x80, 0x56, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00} };
+
+struct windows_usb_api_backend {
+       const uint8_t id;
+       const GUID *class_guid;  // The Class GUID (for fallback in case the driver name cannot be read)
+       const char **driver_name_list; // Driver name, without .sys, e.g. "usbccgp"
+       const uint8_t nb_driver_names;
+       int (*init)(struct libusb_context *ctx);
+       int (*exit)(void);
+       int (*open)(struct libusb_device_handle *dev_handle);
+       void (*close)(struct libusb_device_handle *dev_handle);
+       int (*claim_interface)(struct libusb_device_handle *dev_handle, int iface);
+       int (*set_interface_altsetting)(struct libusb_device_handle *dev_handle, int iface, int altsetting);
+       int (*release_interface)(struct libusb_device_handle *dev_handle, int iface);
+       int (*clear_halt)(struct libusb_device_handle *dev_handle, unsigned char endpoint);
+       int (*reset_device)(struct libusb_device_handle *dev_handle);
+       int (*submit_bulk_transfer)(struct usbi_transfer *itransfer);
+       int (*submit_iso_transfer)(struct usbi_transfer *itransfer);
+       int (*submit_control_transfer)(struct usbi_transfer *itransfer);
+       int (*abort_control)(struct usbi_transfer *itransfer);
+       int (*abort_transfers)(struct usbi_transfer *itransfer);
+       int (*copy_transfer_data)(struct usbi_transfer *itransfer, uint32_t io_size);
+};
+
+extern const struct windows_usb_api_backend usb_api_backend[USB_API_MAX];
+
+#define PRINT_UNSUPPORTED_API(fname)              \
+       usbi_dbg("unsupported API call for '"         \
+               #fname "' (unrecognized device driver)"); \
+       return LIBUSB_ERROR_NOT_SUPPORTED;
+
+/*
+ * private structures definition
+ * with inline pseudo constructors/destructors
+ */
+
+// HCDs
+struct windows_hcd_priv {
+       char *path;
+       struct windows_hcd_priv *next;
+};
+
+static inline void windows_hcd_priv_init(struct windows_hcd_priv* p) {
+       p->path = NULL;
+       p->next = NULL;
+}
+
+static inline void windows_hcd_priv_release(struct windows_hcd_priv* p) {
+       safe_free(p->path);
+}
+
+// TODO (v2+): move hid desc to libusb.h?
+struct libusb_hid_descriptor {
+       uint8_t  bLength;
+       uint8_t  bDescriptorType;
+       uint16_t bcdHID;
+       uint8_t  bCountryCode;
+       uint8_t  bNumDescriptors;
+       uint8_t  bClassDescriptorType;
+       uint16_t wClassDescriptorLength;
+};
+#define LIBUSB_DT_HID_SIZE              9
+#define HID_MAX_CONFIG_DESC_SIZE (LIBUSB_DT_CONFIG_SIZE + LIBUSB_DT_INTERFACE_SIZE \
+       + LIBUSB_DT_HID_SIZE + 2 * LIBUSB_DT_ENDPOINT_SIZE)
+#define HID_MAX_REPORT_SIZE             1024
+#define HID_IN_EP                       0x81
+#define HID_OUT_EP                      0x02
+#define LIBUSB_REQ_RECIPIENT(request_type) ((request_type) & 0x1F)
+#define LIBUSB_REQ_TYPE(request_type) ((request_type) & (0x03 << 5))
+#define LIBUSB_REQ_IN(request_type) ((request_type) & LIBUSB_ENDPOINT_IN)
+#define LIBUSB_REQ_OUT(request_type) (!LIBUSB_REQ_IN(request_type))
+
+enum libusb_hid_request_type {
+       HID_REQ_GET_REPORT = 0x01,
+       HID_REQ_GET_IDLE = 0x02,
+       HID_REQ_GET_PROTOCOL = 0x03,
+       HID_REQ_SET_REPORT = 0x09,
+       HID_REQ_SET_IDLE = 0x0A,
+       HID_REQ_SET_PROTOCOL = 0x0B
+};
+
+enum libusb_hid_report_type {
+       HID_REPORT_TYPE_INPUT = 0x01,
+       HID_REPORT_TYPE_OUTPUT = 0x02,
+       HID_REPORT_TYPE_FEATURE = 0x03
+};
+
+struct hid_device_priv {
+       uint16_t vid;
+       uint16_t pid;
+       uint8_t config;
+       bool uses_report_ids[3];        // input, ouptput, feature
+       uint16_t input_report_size;
+       uint16_t output_report_size;
+       uint16_t feature_report_size;
+       WCHAR string[3][MAX_USB_STRING_LENGTH];
+       uint8_t string_index[3];        // man, prod, ser
+};
+
+typedef struct libusb_device_descriptor USB_DEVICE_DESCRIPTOR, *PUSB_DEVICE_DESCRIPTOR;
+struct windows_device_priv {
+       struct libusb_device *parent_dev;   // access to parent is required for usermode ops
+       ULONG connection_index;             // also required for some usermode ops
+       char *path;                         // path used by Windows to reference the USB node
+       struct windows_usb_api_backend const *apib;
+       struct {
+               char *path;                     // each interface needs a Windows device interface path,
+               struct windows_usb_api_backend const *apib; // an API backend (multiple drivers support),
+               int8_t nb_endpoints;            // and a set of endpoint addresses (USB_MAXENDPOINTS)
+               uint8_t *endpoint;
+               bool restricted_functionality;  // indicates if the interface functionality is restricted
+                                                                               // by Windows (eg. HID keyboards or mice cannot do R/W)
+       } usb_interface[USB_MAXINTERFACES];
+       uint8_t composite_api_flags;        // HID and composite devices require additional data
+       struct hid_device_priv *hid;
+       uint8_t active_config;
+       USB_DEVICE_DESCRIPTOR dev_descriptor;
+       unsigned char **config_descriptor;  // list of pointers to the cached config descriptors
+};
+
+static inline void windows_device_priv_init(struct windows_device_priv* p) {
+       int i;
+       p->parent_dev = NULL;
+       p->connection_index = 0;
+       p->path = NULL;
+       p->apib = &usb_api_backend[USB_API_UNSUPPORTED];
+       p->composite_api_flags = 0;
+       p->hid = NULL;
+       p->active_config = 0;
+       p->config_descriptor = NULL;
+       memset(&(p->dev_descriptor), 0, sizeof(USB_DEVICE_DESCRIPTOR));
+       for (i=0; i<USB_MAXINTERFACES; i++) {
+               p->usb_interface[i].path = NULL;
+               p->usb_interface[i].apib = &usb_api_backend[USB_API_UNSUPPORTED];
+               p->usb_interface[i].nb_endpoints = 0;
+               p->usb_interface[i].endpoint = NULL;
+               p->usb_interface[i].restricted_functionality = false;
+       }
+}
+
+static inline void windows_device_priv_release(struct windows_device_priv* p, int num_configurations) {
+       int i;
+       safe_free(p->path);
+       if ((num_configurations > 0) && (p->config_descriptor != NULL)) {
+               for (i=0; i < num_configurations; i++)
+                       safe_free(p->config_descriptor[i]);
+       }
+       safe_free(p->config_descriptor);
+       safe_free(p->hid);
+       for (i=0; i<USB_MAXINTERFACES; i++) {
+               safe_free(p->usb_interface[i].path);
+               safe_free(p->usb_interface[i].endpoint);
+       }
+}
+
+static inline struct windows_device_priv *__device_priv(struct libusb_device *dev) {
+       return (struct windows_device_priv *)dev->os_priv;
+}
+
+struct interface_handle_t {
+       HANDLE dev_handle; // WinUSB needs an extra handle for the file
+       HANDLE api_handle; // used by the API to communicate with the device
+};
+
+struct windows_device_handle_priv {
+       int active_interface;
+       struct interface_handle_t interface_handle[USB_MAXINTERFACES];
+#if defined(AUTO_CLAIM)
+       int autoclaim_count[USB_MAXINTERFACES]; // For auto-release
+#endif
+};
+
+static inline struct windows_device_handle_priv *__device_handle_priv(
+       struct libusb_device_handle *handle)
+{
+       return (struct windows_device_handle_priv *) handle->os_priv;
+}
+
+// used for async polling functions
+struct windows_transfer_priv {
+       struct winfd pollable_fd;
+       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
+       size_t hid_expected_size;
+};
+
+
+/*
+ * API macros - from libusb-win32 1.x
+ */
+#define DLL_DECLARE(api, ret, name, args)                     \
+  typedef ret (api * __dll_##name##_t)args; __dll_##name##_t name
+
+#define DLL_LOAD(dll, name, ret_on_failure)                   \
+       do {                                                      \
+               HMODULE h = GetModuleHandle(#dll);                    \
+       if (!h)                                                   \
+               h = LoadLibrary(#dll);                                \
+       if (!h) {                                                 \
+               if (ret_on_failure) { return LIBUSB_ERROR_NOT_FOUND; }\
+               else { break; }                                       \
+       }                                                         \
+       name = (__dll_##name##_t)GetProcAddress(h, #name);        \
+       if (name) break;                                          \
+       name = (__dll_##name##_t)GetProcAddress(h, #name "A");    \
+       if (name) break;                                          \
+       name = (__dll_##name##_t)GetProcAddress(h, #name "W");    \
+       if (name) break;                                          \
+       if(ret_on_failure)                                        \
+               return LIBUSB_ERROR_NOT_FOUND;                        \
+       } while(0)
+
+
+/*
+ * Windows DDK API definitions. Most of it copied from MinGW's includes
+ */
+typedef DWORD DEVNODE, DEVINST;
+typedef DEVNODE *PDEVNODE, *PDEVINST;
+typedef DWORD RETURN_TYPE;
+typedef RETURN_TYPE    CONFIGRET;
+
+#define CR_SUCCESS                              0x00000000
+#define CR_NO_SUCH_DEVNODE                      0x0000000D
+
+#define USB_DEVICE_DESCRIPTOR_TYPE              LIBUSB_DT_DEVICE
+#define USB_CONFIGURATION_DESCRIPTOR_TYPE       LIBUSB_DT_CONFIG
+#define USB_STRING_DESCRIPTOR_TYPE              LIBUSB_DT_STRING
+#define USB_INTERFACE_DESCRIPTOR_TYPE           LIBUSB_DT_INTERFACE
+#define USB_ENDPOINT_DESCRIPTOR_TYPE            LIBUSB_DT_ENDPOINT
+
+#define USB_REQUEST_GET_STATUS                  LIBUSB_REQUEST_GET_STATUS
+#define USB_REQUEST_CLEAR_FEATURE               LIBUSB_REQUEST_CLEAR_FEATURE
+#define USB_REQUEST_SET_FEATURE                 LIBUSB_REQUEST_SET_FEATURE
+#define USB_REQUEST_SET_ADDRESS                 LIBUSB_REQUEST_SET_ADDRESS
+#define USB_REQUEST_GET_DESCRIPTOR              LIBUSB_REQUEST_GET_DESCRIPTOR
+#define USB_REQUEST_SET_DESCRIPTOR              LIBUSB_REQUEST_SET_DESCRIPTOR
+#define USB_REQUEST_GET_CONFIGURATION           LIBUSB_REQUEST_GET_CONFIGURATION
+#define USB_REQUEST_SET_CONFIGURATION           LIBUSB_REQUEST_SET_CONFIGURATION
+#define USB_REQUEST_GET_INTERFACE               LIBUSB_REQUEST_GET_INTERFACE
+#define USB_REQUEST_SET_INTERFACE               LIBUSB_REQUEST_SET_INTERFACE
+#define USB_REQUEST_SYNC_FRAME                  LIBUSB_REQUEST_SYNCH_FRAME
+
+#define HCD_GET_ROOT_HUB_NAME                   258
+#define USB_GET_NODE_INFORMATION                258
+#define USB_GET_NODE_CONNECTION_INFORMATION     259
+#define USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION 260
+#define USB_GET_NODE_CONNECTION_NAME            261
+#define USB_GET_HUB_CAPABILITIES                271
+#if !defined(USB_GET_HUB_CAPABILITIES_EX)
+#define USB_GET_HUB_CAPABILITIES_EX             276
+#endif
+
+#ifndef METHOD_BUFFERED
+#define METHOD_BUFFERED                         0
+#endif
+#ifndef FILE_ANY_ACCESS
+#define FILE_ANY_ACCESS                         0x00000000
+#endif
+#ifndef FILE_DEVICE_UNKNOWN
+#define FILE_DEVICE_UNKNOWN                     0x00000022
+#endif
+#ifndef FILE_DEVICE_USB
+#define FILE_DEVICE_USB                         FILE_DEVICE_UNKNOWN
+#endif
+
+#ifndef CTL_CODE
+#define CTL_CODE(DeviceType, Function, Method, Access)( \
+  ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method))
+#endif
+
+typedef enum _USB_CONNECTION_STATUS {
+       NoDeviceConnected,
+       DeviceConnected,
+       DeviceFailedEnumeration,
+       DeviceGeneralFailure,
+       DeviceCausedOvercurrent,
+       DeviceNotEnoughPower,
+       DeviceNotEnoughBandwidth,
+       DeviceHubNestedTooDeeply,
+       DeviceInLegacyHub
+} USB_CONNECTION_STATUS, *PUSB_CONNECTION_STATUS;
+
+typedef enum _USB_HUB_NODE {
+       UsbHub,
+       UsbMIParent
+} USB_HUB_NODE;
+
+/* Cfgmgr32.dll interface */
+DLL_DECLARE(WINAPI, CONFIGRET, CM_Get_Parent, (PDEVINST, DEVINST, ULONG));
+DLL_DECLARE(WINAPI, CONFIGRET, CM_Get_Child, (PDEVINST, DEVINST, ULONG));
+DLL_DECLARE(WINAPI, CONFIGRET, CM_Get_Sibling, (PDEVINST, DEVINST, ULONG));
+DLL_DECLARE(WINAPI, CONFIGRET, CM_Get_Device_IDA, (DEVINST, PCHAR, ULONG, ULONG));
+DLL_DECLARE(WINAPI, CONFIGRET, CM_Get_Device_IDW, (DEVINST, PWCHAR, ULONG, ULONG));
+
+#ifdef UNICODE
+#define CM_Get_Device_ID CM_Get_Device_IDW
+#else
+#define CM_Get_Device_ID CM_Get_Device_IDA
+#endif /* UNICODE */
+
+#define IOCTL_USB_GET_HUB_CAPABILITIES_EX \
+  CTL_CODE( FILE_DEVICE_USB, USB_GET_HUB_CAPABILITIES_EX, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define IOCTL_USB_GET_HUB_CAPABILITIES \
+  CTL_CODE(FILE_DEVICE_USB, USB_GET_HUB_CAPABILITIES, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION \
+  CTL_CODE(FILE_DEVICE_USB, USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define IOCTL_USB_GET_ROOT_HUB_NAME \
+  CTL_CODE(FILE_DEVICE_USB, HCD_GET_ROOT_HUB_NAME, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define IOCTL_USB_GET_NODE_INFORMATION \
+  CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_INFORMATION, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define IOCTL_USB_GET_NODE_CONNECTION_INFORMATION \
+  CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_CONNECTION_INFORMATION, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define IOCTL_USB_GET_NODE_CONNECTION_ATTRIBUTES \
+  CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_CONNECTION_ATTRIBUTES, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define IOCTL_USB_GET_NODE_CONNECTION_NAME \
+  CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_CONNECTION_NAME, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+// Most of the structures below need to be packed
+#pragma pack(push, 1)
+
+typedef struct _USB_INTERFACE_DESCRIPTOR {
+  UCHAR  bLength;
+  UCHAR  bDescriptorType;
+  UCHAR  bInterfaceNumber;
+  UCHAR  bAlternateSetting;
+  UCHAR  bNumEndpoints;
+  UCHAR  bInterfaceClass;
+  UCHAR  bInterfaceSubClass;
+  UCHAR  bInterfaceProtocol;
+  UCHAR  iInterface;
+} USB_INTERFACE_DESCRIPTOR, *PUSB_INTERFACE_DESCRIPTOR;
+
+typedef struct _USB_CONFIGURATION_DESCRIPTOR {
+  UCHAR  bLength;
+  UCHAR  bDescriptorType;
+  USHORT  wTotalLength;
+  UCHAR  bNumInterfaces;
+  UCHAR  bConfigurationValue;
+  UCHAR  iConfiguration;
+  UCHAR  bmAttributes;
+  UCHAR  MaxPower;
+} USB_CONFIGURATION_DESCRIPTOR, *PUSB_CONFIGURATION_DESCRIPTOR;
+
+typedef struct _USB_CONFIGURATION_DESCRIPTOR_SHORT {
+       struct {
+               ULONG ConnectionIndex;
+               struct {
+                       UCHAR bmRequest;
+                       UCHAR bRequest;
+                       USHORT wValue;
+                       USHORT wIndex;
+                       USHORT wLength;
+               } SetupPacket;
+       } req;
+       USB_CONFIGURATION_DESCRIPTOR data;
+} USB_CONFIGURATION_DESCRIPTOR_SHORT;
+
+typedef struct _USB_ENDPOINT_DESCRIPTOR {
+  UCHAR  bLength;
+  UCHAR  bDescriptorType;
+  UCHAR  bEndpointAddress;
+  UCHAR  bmAttributes;
+  USHORT  wMaxPacketSize;
+  UCHAR  bInterval;
+} USB_ENDPOINT_DESCRIPTOR, *PUSB_ENDPOINT_DESCRIPTOR;
+
+typedef struct _USB_DESCRIPTOR_REQUEST {
+       ULONG  ConnectionIndex;
+       struct {
+               UCHAR  bmRequest;
+               UCHAR  bRequest;
+               USHORT  wValue;
+               USHORT  wIndex;
+               USHORT  wLength;
+       } SetupPacket;
+//     UCHAR  Data[0];
+} USB_DESCRIPTOR_REQUEST, *PUSB_DESCRIPTOR_REQUEST;
+
+typedef struct _USB_HUB_DESCRIPTOR {
+       UCHAR  bDescriptorLength;
+       UCHAR  bDescriptorType;
+       UCHAR  bNumberOfPorts;
+       USHORT  wHubCharacteristics;
+       UCHAR  bPowerOnToPowerGood;
+       UCHAR  bHubControlCurrent;
+       UCHAR  bRemoveAndPowerMask[64];
+} USB_HUB_DESCRIPTOR, *PUSB_HUB_DESCRIPTOR;
+
+typedef struct _USB_ROOT_HUB_NAME {
+       ULONG  ActualLength;
+       WCHAR  RootHubName[1];
+} USB_ROOT_HUB_NAME, *PUSB_ROOT_HUB_NAME;
+
+typedef struct _USB_ROOT_HUB_NAME_FIXED {
+       ULONG ActualLength;
+       WCHAR RootHubName[MAX_PATH_LENGTH];
+} USB_ROOT_HUB_NAME_FIXED;
+
+typedef struct _USB_NODE_CONNECTION_NAME {
+       ULONG  ConnectionIndex;
+       ULONG  ActualLength;
+       WCHAR  NodeName[1];
+} USB_NODE_CONNECTION_NAME, *PUSB_NODE_CONNECTION_NAME;
+
+typedef struct _USB_NODE_CONNECTION_NAME_FIXED {
+       ULONG ConnectionIndex;
+       ULONG ActualLength;
+       WCHAR NodeName[MAX_PATH_LENGTH];
+} USB_NODE_CONNECTION_NAME_FIXED;
+
+typedef struct _USB_HUB_NAME_FIXED {
+       union {
+               USB_ROOT_HUB_NAME_FIXED root;
+               USB_NODE_CONNECTION_NAME_FIXED node;
+       } u;
+} USB_HUB_NAME_FIXED;
+
+
+typedef struct _USB_HUB_INFORMATION {
+       USB_HUB_DESCRIPTOR  HubDescriptor;
+       BOOLEAN  HubIsBusPowered;
+} USB_HUB_INFORMATION, *PUSB_HUB_INFORMATION;
+
+typedef struct _USB_MI_PARENT_INFORMATION {
+  ULONG  NumberOfInterfaces;
+} USB_MI_PARENT_INFORMATION, *PUSB_MI_PARENT_INFORMATION;
+
+typedef struct _USB_NODE_INFORMATION {
+       USB_HUB_NODE  NodeType;
+       union {
+               USB_HUB_INFORMATION  HubInformation;
+               USB_MI_PARENT_INFORMATION  MiParentInformation;
+       } u;
+} USB_NODE_INFORMATION, *PUSB_NODE_INFORMATION;
+
+typedef struct _USB_PIPE_INFO {
+       USB_ENDPOINT_DESCRIPTOR  EndpointDescriptor;
+       ULONG  ScheduleOffset;
+} USB_PIPE_INFO, *PUSB_PIPE_INFO;
+
+typedef struct _USB_NODE_CONNECTION_INFORMATION {
+       ULONG  ConnectionIndex;
+       USB_DEVICE_DESCRIPTOR  DeviceDescriptor;
+       UCHAR  CurrentConfigurationValue;
+       BOOLEAN  LowSpeed;
+       BOOLEAN  DeviceIsHub;
+       USHORT  DeviceAddress;
+       ULONG  NumberOfOpenPipes;
+       USB_CONNECTION_STATUS  ConnectionStatus;
+//     USB_PIPE_INFO  PipeList[0];
+} USB_NODE_CONNECTION_INFORMATION, *PUSB_NODE_CONNECTION_INFORMATION;
+
+typedef struct _USB_HUB_CAP_FLAGS {
+       ULONG HubIsHighSpeedCapable:1;
+       ULONG HubIsHighSpeed:1;
+       ULONG HubIsMultiTtCapable:1;
+       ULONG HubIsMultiTt:1;
+       ULONG HubIsRoot:1;
+       ULONG HubIsArmedWakeOnConnect:1;
+       ULONG ReservedMBZ:26;
+} USB_HUB_CAP_FLAGS, *PUSB_HUB_CAP_FLAGS;
+
+typedef struct _USB_HUB_CAPABILITIES {
+  ULONG  HubIs2xCapable : 1;
+} USB_HUB_CAPABILITIES, *PUSB_HUB_CAPABILITIES;
+
+typedef struct _USB_HUB_CAPABILITIES_EX {
+       USB_HUB_CAP_FLAGS CapabilityFlags;
+} USB_HUB_CAPABILITIES_EX, *PUSB_HUB_CAPABILITIES_EX;
+
+#pragma pack(pop)
+
+/* winusb.dll interface */
+
+#define SHORT_PACKET_TERMINATE  0x01
+#define AUTO_CLEAR_STALL        0x02
+#define PIPE_TRANSFER_TIMEOUT   0x03
+#define IGNORE_SHORT_PACKETS    0x04
+#define ALLOW_PARTIAL_READS     0x05
+#define AUTO_FLUSH              0x06
+#define RAW_IO                  0x07
+#define MAXIMUM_TRANSFER_SIZE   0x08
+#define AUTO_SUSPEND            0x81
+#define SUSPEND_DELAY           0x83
+#define DEVICE_SPEED            0x01
+#define LowSpeed                0x01
+#define FullSpeed               0x02
+#define HighSpeed               0x03
+
+typedef enum _USBD_PIPE_TYPE {
+       UsbdPipeTypeControl,
+       UsbdPipeTypeIsochronous,
+       UsbdPipeTypeBulk,
+       UsbdPipeTypeInterrupt
+} USBD_PIPE_TYPE;
+
+typedef struct {
+  USBD_PIPE_TYPE PipeType;
+  UCHAR          PipeId;
+  USHORT         MaximumPacketSize;
+  UCHAR          Interval;
+} WINUSB_PIPE_INFORMATION, *PWINUSB_PIPE_INFORMATION;
+
+#pragma pack(1)
+typedef struct {
+  UCHAR  request_type;
+  UCHAR  request;
+  USHORT value;
+  USHORT index;
+  USHORT length;
+} WINUSB_SETUP_PACKET, *PWINUSB_SETUP_PACKET;
+#pragma pack()
+
+typedef void *WINUSB_INTERFACE_HANDLE, *PWINUSB_INTERFACE_HANDLE;
+
+DLL_DECLARE(WINAPI, BOOL, WinUsb_Initialize, (HANDLE, PWINUSB_INTERFACE_HANDLE));
+DLL_DECLARE(WINAPI, BOOL, WinUsb_Free, (WINUSB_INTERFACE_HANDLE));
+DLL_DECLARE(WINAPI, BOOL, WinUsb_GetAssociatedInterface, (WINUSB_INTERFACE_HANDLE, UCHAR, PWINUSB_INTERFACE_HANDLE));
+DLL_DECLARE(WINAPI, BOOL, WinUsb_GetDescriptor, (WINUSB_INTERFACE_HANDLE, UCHAR, UCHAR, USHORT, PUCHAR, ULONG, PULONG));
+DLL_DECLARE(WINAPI, BOOL, WinUsb_QueryInterfaceSettings, (WINUSB_INTERFACE_HANDLE, UCHAR, PUSB_INTERFACE_DESCRIPTOR));
+DLL_DECLARE(WINAPI, BOOL, WinUsb_QueryDeviceInformation, (WINUSB_INTERFACE_HANDLE, ULONG, PULONG, PVOID));
+DLL_DECLARE(WINAPI, BOOL, WinUsb_SetCurrentAlternateSetting, (WINUSB_INTERFACE_HANDLE, UCHAR));
+DLL_DECLARE(WINAPI, BOOL, WinUsb_GetCurrentAlternateSetting, (WINUSB_INTERFACE_HANDLE, PUCHAR));
+DLL_DECLARE(WINAPI, BOOL, WinUsb_QueryPipe, (WINUSB_INTERFACE_HANDLE, UCHAR, UCHAR, PWINUSB_PIPE_INFORMATION));
+DLL_DECLARE(WINAPI, BOOL, WinUsb_SetPipePolicy, (WINUSB_INTERFACE_HANDLE, UCHAR, ULONG, ULONG, PVOID));
+DLL_DECLARE(WINAPI, BOOL, WinUsb_GetPipePolicy, (WINUSB_INTERFACE_HANDLE, UCHAR, ULONG, PULONG, PVOID));
+DLL_DECLARE(WINAPI, BOOL, WinUsb_ReadPipe, (WINUSB_INTERFACE_HANDLE, UCHAR, PUCHAR, ULONG, PULONG, LPOVERLAPPED));
+DLL_DECLARE(WINAPI, BOOL, WinUsb_WritePipe, (WINUSB_INTERFACE_HANDLE, UCHAR, PUCHAR, ULONG, PULONG, LPOVERLAPPED));
+DLL_DECLARE(WINAPI, BOOL, WinUsb_ControlTransfer, (WINUSB_INTERFACE_HANDLE, WINUSB_SETUP_PACKET, PUCHAR, ULONG, PULONG, LPOVERLAPPED));
+DLL_DECLARE(WINAPI, BOOL, WinUsb_ResetPipe, (WINUSB_INTERFACE_HANDLE, UCHAR));
+DLL_DECLARE(WINAPI, BOOL, WinUsb_AbortPipe, (WINUSB_INTERFACE_HANDLE, UCHAR));
+DLL_DECLARE(WINAPI, BOOL, WinUsb_FlushPipe, (WINUSB_INTERFACE_HANDLE, UCHAR));
+
+/* hid.dll interface */
+
+#define HIDP_STATUS_SUCCESS  0x110000
+typedef void* PHIDP_PREPARSED_DATA;
+
+#pragma pack(1)
+typedef struct {
+       ULONG Size;
+       USHORT VendorID;
+       USHORT ProductID;
+       USHORT VersionNumber;
+} HIDD_ATTRIBUTES, *PHIDD_ATTRIBUTES;
+#pragma pack()
+
+typedef USHORT USAGE;
+typedef struct {
+  USAGE Usage;
+  USAGE UsagePage;
+  USHORT InputReportByteLength;
+  USHORT OutputReportByteLength;
+  USHORT FeatureReportByteLength;
+  USHORT Reserved[17];
+  USHORT NumberLinkCollectionNodes;
+  USHORT NumberInputButtonCaps;
+  USHORT NumberInputValueCaps;
+  USHORT NumberInputDataIndices;
+  USHORT NumberOutputButtonCaps;
+  USHORT NumberOutputValueCaps;
+  USHORT NumberOutputDataIndices;
+  USHORT NumberFeatureButtonCaps;
+  USHORT NumberFeatureValueCaps;
+  USHORT NumberFeatureDataIndices;
+} HIDP_CAPS, *PHIDP_CAPS;
+
+typedef enum _HIDP_REPORT_TYPE {
+  HidP_Input,
+  HidP_Output,
+  HidP_Feature
+} HIDP_REPORT_TYPE;
+
+typedef struct _HIDP_VALUE_CAPS {
+  USAGE  UsagePage;
+  UCHAR  ReportID;
+  BOOLEAN  IsAlias;
+  USHORT  BitField;
+  USHORT  LinkCollection;
+  USAGE  LinkUsage;
+  USAGE  LinkUsagePage;
+  BOOLEAN  IsRange;
+  BOOLEAN  IsStringRange;
+  BOOLEAN  IsDesignatorRange;
+  BOOLEAN  IsAbsolute;
+  BOOLEAN  HasNull;
+  UCHAR  Reserved;
+  USHORT  BitSize;
+  USHORT  ReportCount;
+  USHORT  Reserved2[5];
+  ULONG  UnitsExp;
+  ULONG  Units;
+  LONG  LogicalMin, LogicalMax;
+  LONG  PhysicalMin, PhysicalMax;
+       union {
+         struct {
+               USAGE  UsageMin, UsageMax;
+               USHORT  StringMin, StringMax;
+               USHORT  DesignatorMin, DesignatorMax;
+               USHORT  DataIndexMin, DataIndexMax;
+         } Range;
+         struct {
+               USAGE  Usage, Reserved1;
+               USHORT  StringIndex, Reserved2;
+               USHORT  DesignatorIndex, Reserved3;
+               USHORT  DataIndex, Reserved4;
+         } NotRange;
+       } u;
+} HIDP_VALUE_CAPS, *PHIDP_VALUE_CAPS;
+
+DLL_DECLARE(WINAPI, BOOL, HidD_GetAttributes, (HANDLE, PHIDD_ATTRIBUTES));
+DLL_DECLARE(WINAPI, VOID, HidD_GetHidGuid, (LPGUID));
+DLL_DECLARE(WINAPI, BOOL, HidD_GetPreparsedData, (HANDLE, PHIDP_PREPARSED_DATA *));
+DLL_DECLARE(WINAPI, BOOL, HidD_FreePreparsedData, (PHIDP_PREPARSED_DATA));
+DLL_DECLARE(WINAPI, BOOL, HidD_GetManufacturerString, (HANDLE, PVOID, ULONG));
+DLL_DECLARE(WINAPI, BOOL, HidD_GetProductString, (HANDLE, PVOID, ULONG));
+DLL_DECLARE(WINAPI, BOOL, HidD_GetSerialNumberString, (HANDLE, PVOID, ULONG));
+DLL_DECLARE(WINAPI, LONG, HidP_GetCaps, (PHIDP_PREPARSED_DATA, PHIDP_CAPS));
+DLL_DECLARE(WINAPI, BOOL, HidD_SetNumInputBuffers, (HANDLE, ULONG));
+DLL_DECLARE(WINAPI, BOOL, HidD_SetFeature, (HANDLE, PVOID, ULONG));
+DLL_DECLARE(WINAPI, BOOL, HidD_GetFeature, (HANDLE, PVOID, ULONG));
+DLL_DECLARE(WINAPI, BOOL, HidD_GetPhysicalDescriptor, (HANDLE, PVOID, ULONG));
+DLL_DECLARE(WINAPI, BOOL, HidD_GetInputReport, (HANDLE, PVOID, ULONG));
+DLL_DECLARE(WINAPI, BOOL, HidD_SetOutputReport, (HANDLE, PVOID, ULONG));
+DLL_DECLARE(WINAPI, BOOL, HidD_FlushQueue, (HANDLE));
+DLL_DECLARE(WINAPI, BOOL, HidP_GetValueCaps, (HIDP_REPORT_TYPE, PHIDP_VALUE_CAPS, PULONG, PHIDP_PREPARSED_DATA));