Quellcode durchsuchen

accounts/usbwallet, vendor: switch from HID to generic USB lib

Péter Szilágyi vor 6 Jahren
Ursprung
Commit
5d68400cad
92 geänderte Dateien mit 7479 neuen und 7799 gelöschten Zeilen
  1. 13 13
      accounts/usbwallet/hub.go
  2. 0 2
      accounts/usbwallet/trezor.go
  3. 3 3
      accounts/usbwallet/wallet.go
  4. 5 5
      node/config.go
  5. 7 7
      signer/core/api.go
  6. 0 8
      vendor/github.com/karalabe/hid/LICENSE.md
  7. 0 53
      vendor/github.com/karalabe/hid/README.md
  8. 0 54
      vendor/github.com/karalabe/hid/generic.go
  9. 0 52
      vendor/github.com/karalabe/hid/hid.go
  10. 0 76
      vendor/github.com/karalabe/hid/hid_disabled.go
  11. 0 459
      vendor/github.com/karalabe/hid/hid_enabled.go
  12. 0 728
      vendor/github.com/karalabe/hid/libusb/libusb/os/poll_windows.c
  13. 0 55
      vendor/github.com/karalabe/hid/libusb/libusb/os/threads_posix.h
  14. 0 259
      vendor/github.com/karalabe/hid/libusb/libusb/os/threads_windows.c
  15. 0 63
      vendor/github.com/karalabe/hid/libusb/libusb/os/windows_nt_common.h
  16. 0 4290
      vendor/github.com/karalabe/hid/libusb/libusb/os/windows_winusb.c
  17. 0 1
      vendor/github.com/karalabe/hid/libusb/libusb/version_nano.h
  18. 0 53
      vendor/github.com/karalabe/hid/usb.go
  19. 6 0
      vendor/github.com/karalabe/usb/AUTHORS
  20. 165 0
      vendor/github.com/karalabe/usb/LICENSE
  21. 47 0
      vendor/github.com/karalabe/usb/README.md
  22. 3 3
      vendor/github.com/karalabe/usb/appveyor.yml
  23. 76 0
      vendor/github.com/karalabe/usb/demo.go
  24. 3 0
      vendor/github.com/karalabe/usb/go.mod
  25. 42 0
      vendor/github.com/karalabe/usb/hid_disabled.go
  26. 187 0
      vendor/github.com/karalabe/usb/hid_enabled.go
  27. 0 0
      vendor/github.com/karalabe/usb/hidapi/AUTHORS.txt
  28. 0 0
      vendor/github.com/karalabe/usb/hidapi/LICENSE-bsd.txt
  29. 0 0
      vendor/github.com/karalabe/usb/hidapi/LICENSE-gpl3.txt
  30. 0 0
      vendor/github.com/karalabe/usb/hidapi/LICENSE-orig.txt
  31. 0 0
      vendor/github.com/karalabe/usb/hidapi/LICENSE.txt
  32. 0 0
      vendor/github.com/karalabe/usb/hidapi/README.txt
  33. 0 0
      vendor/github.com/karalabe/usb/hidapi/hidapi/hidapi.h
  34. 0 0
      vendor/github.com/karalabe/usb/hidapi/libusb/hid.c
  35. 0 0
      vendor/github.com/karalabe/usb/hidapi/mac/hid.c
  36. 0 0
      vendor/github.com/karalabe/usb/hidapi/windows/hid.c
  37. 74 0
      vendor/github.com/karalabe/usb/libs.go
  38. 32 2
      vendor/github.com/karalabe/usb/libusb/AUTHORS
  39. 0 0
      vendor/github.com/karalabe/usb/libusb/COPYING
  40. 0 0
      vendor/github.com/karalabe/usb/libusb/libusb/config.h
  41. 201 145
      vendor/github.com/karalabe/usb/libusb/libusb/core.c
  42. 11 10
      vendor/github.com/karalabe/usb/libusb/libusb/descriptor.c
  43. 73 50
      vendor/github.com/karalabe/usb/libusb/libusb/hotplug.c
  44. 34 25
      vendor/github.com/karalabe/usb/libusb/libusb/hotplug.h
  45. 38 35
      vendor/github.com/karalabe/usb/libusb/libusb/io.c
  46. 63 32
      vendor/github.com/karalabe/usb/libusb/libusb/libusb.h
  47. 89 73
      vendor/github.com/karalabe/usb/libusb/libusb/libusbi.h
  48. 75 27
      vendor/github.com/karalabe/usb/libusb/libusb/os/darwin_usb.c
  49. 48 13
      vendor/github.com/karalabe/usb/libusb/libusb/os/darwin_usb.h
  50. 0 0
      vendor/github.com/karalabe/usb/libusb/libusb/os/haiku_pollfs.cpp
  51. 0 0
      vendor/github.com/karalabe/usb/libusb/libusb/os/haiku_usb.h
  52. 0 0
      vendor/github.com/karalabe/usb/libusb/libusb/os/haiku_usb_backend.cpp
  53. 5 2
      vendor/github.com/karalabe/usb/libusb/libusb/os/haiku_usb_raw.cpp
  54. 0 0
      vendor/github.com/karalabe/usb/libusb/libusb/os/haiku_usb_raw.h
  55. 37 28
      vendor/github.com/karalabe/usb/libusb/libusb/os/linux_netlink.c
  56. 26 8
      vendor/github.com/karalabe/usb/libusb/libusb/os/linux_udev.c
  57. 162 100
      vendor/github.com/karalabe/usb/libusb/libusb/os/linux_usbfs.c
  58. 2 1
      vendor/github.com/karalabe/usb/libusb/libusb/os/linux_usbfs.h
  59. 3 3
      vendor/github.com/karalabe/usb/libusb/libusb/os/netbsd_usb.c
  60. 3 3
      vendor/github.com/karalabe/usb/libusb/libusb/os/openbsd_usb.c
  61. 36 5
      vendor/github.com/karalabe/usb/libusb/libusb/os/poll_posix.c
  62. 0 0
      vendor/github.com/karalabe/usb/libusb/libusb/os/poll_posix.h
  63. 364 0
      vendor/github.com/karalabe/usb/libusb/libusb/os/poll_windows.c
  64. 9 43
      vendor/github.com/karalabe/usb/libusb/libusb/os/poll_windows.h
  65. 475 92
      vendor/github.com/karalabe/usb/libusb/libusb/os/sunos_usb.c
  66. 6 0
      vendor/github.com/karalabe/usb/libusb/libusb/os/sunos_usb.h
  67. 6 5
      vendor/github.com/karalabe/usb/libusb/libusb/os/threads_posix.c
  68. 102 0
      vendor/github.com/karalabe/usb/libusb/libusb/os/threads_posix.h
  69. 126 0
      vendor/github.com/karalabe/usb/libusb/libusb/os/threads_windows.c
  70. 64 29
      vendor/github.com/karalabe/usb/libusb/libusb/os/threads_windows.h
  71. 28 39
      vendor/github.com/karalabe/usb/libusb/libusb/os/wince_usb.c
  72. 0 0
      vendor/github.com/karalabe/usb/libusb/libusb/os/wince_usb.h
  73. 20 16
      vendor/github.com/karalabe/usb/libusb/libusb/os/windows_common.h
  74. 523 106
      vendor/github.com/karalabe/usb/libusb/libusb/os/windows_nt_common.c
  75. 110 0
      vendor/github.com/karalabe/usb/libusb/libusb/os/windows_nt_common.h
  76. 138 0
      vendor/github.com/karalabe/usb/libusb/libusb/os/windows_nt_shared_types.h
  77. 171 246
      vendor/github.com/karalabe/usb/libusb/libusb/os/windows_usbdk.c
  78. 3 46
      vendor/github.com/karalabe/usb/libusb/libusb/os/windows_usbdk.h
  79. 3009 0
      vendor/github.com/karalabe/usb/libusb/libusb/os/windows_winusb.c
  80. 168 425
      vendor/github.com/karalabe/usb/libusb/libusb/os/windows_winusb.h
  81. 0 0
      vendor/github.com/karalabe/usb/libusb/libusb/strerror.c
  82. 0 0
      vendor/github.com/karalabe/usb/libusb/libusb/sync.c
  83. 1 1
      vendor/github.com/karalabe/usb/libusb/libusb/version.h
  84. 1 0
      vendor/github.com/karalabe/usb/libusb/libusb/version_nano.h
  85. 42 0
      vendor/github.com/karalabe/usb/raw_disabled.go
  86. 253 0
      vendor/github.com/karalabe/usb/raw_enabled.go
  87. 74 0
      vendor/github.com/karalabe/usb/raw_errors.go
  88. 68 0
      vendor/github.com/karalabe/usb/usb.go
  89. 46 0
      vendor/github.com/karalabe/usb/usb_disabled.go
  90. 98 0
      vendor/github.com/karalabe/usb/usb_enabled.go
  91. 1 1
      vendor/github.com/karalabe/usb/wchar.go
  92. 4 4
      vendor/vendor.json

+ 13 - 13
accounts/usbwallet/hub.go

@@ -25,7 +25,7 @@ import (
 	"github.com/ethereum/go-ethereum/accounts"
 	"github.com/ethereum/go-ethereum/event"
 	"github.com/ethereum/go-ethereum/log"
-	"github.com/karalabe/hid"
+	"github.com/karalabe/usb"
 )
 
 // LedgerScheme is the protocol scheme prefixing account and wallet URLs.
@@ -84,20 +84,20 @@ func NewLedgerHub() (*Hub, error) {
 	}, 0xffa0, 0, newLedgerDriver)
 }
 
-// NewTrezorHub creates a new hardware wallet manager for Trezor devices.
-func NewTrezorHub() (*Hub, error) {
-	return newHub(TrezorScheme, 0x534c, []uint16{0x0001 /* Trezor 1 */}, 0xff00, 0, newTrezorDriver)
+// NewTrezorHubWithHID creates a new hardware wallet manager for Trezor devices.
+func NewTrezorHubWithHID() (*Hub, error) {
+	return newHub(TrezorScheme, 0x534c, []uint16{0x0001 /* Trezor HID */}, 0xff00, 0, newTrezorDriver)
 }
 
-// NewWebUSBTrezorHub creates a new hardware wallet manager for Trezor devices with
+// NewTrezorHubWithWebUSB creates a new hardware wallet manager for Trezor devices with
 // firmware version > 1.8.0
-func NewWebUSBTrezorHub() (*Hub, error) {
-	return newHub(TrezorScheme, 0x1209, []uint16{0x53c1 /* Trezor 1 WebUSB */}, 0, 0, newTrezorDriver)
+func NewTrezorHubWithWebUSB() (*Hub, error) {
+	return newHub(TrezorScheme, 0x1209, []uint16{0x53c1 /* Trezor WebUSB */}, 0xffff /* No usage id on webusb, don't match unset (0) */, 0, newTrezorDriver)
 }
 
 // newHub creates a new hardware wallet manager for generic USB devices.
 func newHub(scheme string, vendorID uint16, productIDs []uint16, usageID uint16, endpointID int, makeDriver func(log.Logger) driver) (*Hub, error) {
-	if !hid.Supported() {
+	if !usb.Supported() {
 		return nil, errors.New("unsupported platform")
 	}
 	hub := &Hub{
@@ -139,7 +139,7 @@ func (hub *Hub) refreshWallets() {
 		return
 	}
 	// Retrieve the current list of USB wallet devices
-	var devices []hid.DeviceInfo
+	var devices []usb.DeviceInfo
 
 	if runtime.GOOS == "linux" {
 		// hidapi on Linux opens the device during enumeration to retrieve some infos,
@@ -154,7 +154,7 @@ func (hub *Hub) refreshWallets() {
 			return
 		}
 	}
-	infos, err := hid.Enumerate(hub.vendorID, 0)
+	infos, err := usb.Enumerate(hub.vendorID, 0)
 	if err != nil {
 		if runtime.GOOS == "linux" {
 			// See rationale before the enumeration why this is needed and only on Linux.
@@ -165,8 +165,8 @@ func (hub *Hub) refreshWallets() {
 	}
 	for _, info := range infos {
 		for _, id := range hub.productIDs {
-			_, pid, endpoint, _ /* FIXME usageID */ := info.IDs()
-			if pid == id && ( /* FIXME usageID == hub.usageID || */ endpoint == hub.endpointID) {
+			// Windows and Macos use UsageID matching, Linux uses Interface matching
+			if info.ProductID == id && (info.UsagePage == hub.usageID || info.Interface == hub.endpointID) {
 				devices = append(devices, info)
 				break
 			}
@@ -185,7 +185,7 @@ func (hub *Hub) refreshWallets() {
 	)
 
 	for _, device := range devices {
-		url := accounts.URL{Scheme: hub.scheme, Path: device.GetPath()}
+		url := accounts.URL{Scheme: hub.scheme, Path: device.Path}
 
 		// Drop wallets in front of the next device or those that failed for some reason
 		for len(hub.wallets) > 0 {

+ 0 - 2
accounts/usbwallet/trezor.go

@@ -36,8 +36,6 @@ import (
 	"github.com/golang/protobuf/proto"
 )
 
-var ErrInvalidDeviceType = errors.New("trezor: invalid device type")
-
 // ErrTrezorPINNeeded is returned if opening the trezor requires a PIN code. In
 // this case, the calling application should display a pinpad and send back the
 // encoded passphrase.

+ 3 - 3
accounts/usbwallet/wallet.go

@@ -31,7 +31,7 @@ import (
 	"github.com/ethereum/go-ethereum/core/types"
 	"github.com/ethereum/go-ethereum/crypto"
 	"github.com/ethereum/go-ethereum/log"
-	"github.com/karalabe/hid"
+	"github.com/karalabe/usb"
 )
 
 // Maximum time between wallet health checks to detect USB unplugs.
@@ -77,8 +77,8 @@ type wallet struct {
 	driver driver        // Hardware implementation of the low level device operations
 	url    *accounts.URL // Textual URL uniquely identifying this wallet
 
-	info   hid.DeviceInfo // Known USB device infos about the wallet
-	device hid.Device     // USB device advertising itself as a hardware wallet
+	info   usb.DeviceInfo // Known USB device infos about the wallet
+	device usb.Device     // USB device advertising itself as a hardware wallet
 
 	accounts []accounts.Account                         // List of derive accounts pinned on the hardware wallet
 	paths    map[common.Address]accounts.DerivationPath // Known derivation paths for signing operations

+ 5 - 5
node/config.go

@@ -501,15 +501,15 @@ func makeAccountManager(conf *Config) (*accounts.Manager, string, error) {
 			} else {
 				backends = append(backends, ledgerhub)
 			}
-			// Start a USB hub for Trezor hardware wallets
-			if trezorhub, err := usbwallet.NewTrezorHub(); err != nil {
-				log.Warn(fmt.Sprintf("Failed to start Trezor hub, disabling: %v", err))
+			// Start a USB hub for Trezor hardware wallets (HID version)
+			if trezorhub, err := usbwallet.NewTrezorHubWithHID(); err != nil {
+				log.Warn(fmt.Sprintf("Failed to start HID Trezor hub, disabling: %v", err))
 			} else {
 				backends = append(backends, trezorhub)
 			}
 			// Start a USB hub for Trezor hardware wallets (WebUSB version)
-			if trezorhub, err := usbwallet.NewWebUSBTrezorHub(); err != nil {
-				log.Warn(fmt.Sprintf("Failed to start Trezor hub, disabling: %v", err))
+			if trezorhub, err := usbwallet.NewTrezorHubWithWebUSB(); err != nil {
+				log.Warn(fmt.Sprintf("Failed to start WebUSB Trezor hub, disabling: %v", err))
 			} else {
 				backends = append(backends, trezorhub)
 			}

+ 7 - 7
signer/core/api.go

@@ -144,19 +144,19 @@ func StartClefAccountManager(ksLocation string, nousb, lightKDF bool) *accounts.
 			backends = append(backends, ledgerhub)
 			log.Debug("Ledger support enabled")
 		}
-		// Start a USB hub for Trezor hardware wallets
-		if trezorhub, err := usbwallet.NewTrezorHub(); err != nil {
-			log.Warn(fmt.Sprintf("Failed to start Trezor hub, disabling: %v", err))
+		// Start a USB hub for Trezor hardware wallets (HID version)
+		if trezorhub, err := usbwallet.NewTrezorHubWithHID(); err != nil {
+			log.Warn(fmt.Sprintf("Failed to start HID Trezor hub, disabling: %v", err))
 		} else {
 			backends = append(backends, trezorhub)
-			log.Debug("Trezor support enabled")
+			log.Debug("Trezor support enabled via HID")
 		}
 		// Start a USB hub for Trezor hardware wallets (WebUSB version)
-		if trezorhub, err := usbwallet.NewWebUSBTrezorHub(); err != nil {
-			log.Warn(fmt.Sprintf("Failed to start Trezor hub, disabling: %v", err))
+		if trezorhub, err := usbwallet.NewTrezorHubWithWebUSB(); err != nil {
+			log.Warn(fmt.Sprintf("Failed to start WebUSB Trezor hub, disabling: %v", err))
 		} else {
 			backends = append(backends, trezorhub)
-			log.Debug("Trezor support enabled")
+			log.Debug("Trezor support enabled via WebUSB")
 		}
 	}
 	// Clef doesn't allow insecure http account unlock.

+ 0 - 8
vendor/github.com/karalabe/hid/LICENSE.md

@@ -1,8 +0,0 @@
-The components of `hid` are licensed as such:
-
- * `hidapi` is released under the [3-clause BSD](https://github.com/signal11/hidapi/blob/master/LICENSE-bsd.txt) license.
- * `libusb` is released under the [GNU LGPL 2.1](https://github.com/libusb/libusb/blob/master/COPYING)license.
- * `go.hid` is released under the [2-clause BSD](https://github.com/GeertJohan/go.hid/blob/master/LICENSE) license.
- * `gowchar` is released under the [3-clause BSD](https://github.com/orofarne/gowchar/blob/master/LICENSE) license.
-
-Given the above, `hid` is licensed under GNU LGPL 2.1 or later on Linux and 3-clause BSD on other platforms.

+ 0 - 53
vendor/github.com/karalabe/hid/README.md

@@ -1,53 +0,0 @@
-[![Travis][travisimg]][travisurl]
-[![AppVeyor][appveyorimg]][appveyorurl]
-[![GoDoc][docimg]][docurl]
-
-[travisimg]:   https://travis-ci.org/karalabe/hid.svg?branch=master
-[travisurl]:   https://travis-ci.org/karalabe/hid
-[appveyorimg]: https://ci.appveyor.com/api/projects/status/plroy54odykb0ch3/branch/master?svg=true
-[appveyorurl]: https://ci.appveyor.com/project/karalabe/hid
-[docimg]:      https://godoc.org/github.com/karalabe/hid?status.svg
-[docurl]:      https://godoc.org/github.com/karalabe/hid
-
-# Gopher Interface Devices (USB HID)
-
-The `hid` package is a cross platform library for accessing and communicating with USB Human Interface
-Devices (HID). It is an alternative package to [`gousb`](https://github.com/karalabe/gousb) for use
-cases where devices support this ligher mode of operation (e.g. input devices, hardware crypto wallets).
-
-The package wraps [`hidapi`](https://github.com/signal11/hidapi) for accessing OS specific USB HID APIs
-directly instead of using low level USB constructs, which might have permission issues on some platforms.
-On Linux the package also wraps [`libusb`](https://github.com/libusb/libusb). Both of these dependencies
-are vendored directly into the repository and wrapped using CGO, making the `hid` package self-contained
-and go-gettable.
-
-Supported platforms at the moment are Linux, macOS and Windows (exclude constraints are also specified
-for Android and iOS to allow smoother vendoring into cross platform projects).
-
-## Cross-compiling
-
-Using `go get` the embedded C library is compiled into the binary format of your host OS. Cross compiling to a different platform or architecture entails disabling CGO by default in Go, causing device enumeration `hid.Enumerate()` to yield no results.
-
-To cross compile a functional version of this library, you'll need to enable CGO during cross compilation via `CGO_ENABLED=1` and you'll need to install and set a cross compilation enabled C toolkit via `CC=your-cross-gcc`.
-
-## Acknowledgements
-
-Although the `hid` package is an implementation from scratch, it was heavily inspired by the existing
-[`go.hid`](https://github.com/GeertJohan/go.hid) library, which seems abandoned since 2015; is incompatible
-with Go 1.6+; and has various external dependencies. Given its inspirational roots, I thought it important
-to give credit to the author of said package too.
-
-Wide character support in the `hid` package is done via the [`gowchar`](https://github.com/orofarne/gowchar)
-library, unmaintained since 2013; non buildable with a modern Go release and failing `go vet` checks. As
-such, `gowchar` was also vendored in inline (copyright headers and origins preserved).
-
-## License
-
-The components of `hid` are licensed as such:
-
- * `hidapi` is released under the [3-clause BSD](https://github.com/signal11/hidapi/blob/master/LICENSE-bsd.txt) license.
- * `libusb` is released under the [GNU LGPL 2.1](https://github.com/libusb/libusb/blob/master/COPYING)license.
- * `go.hid` is released under the [2-clause BSD](https://github.com/GeertJohan/go.hid/blob/master/LICENSE) license.
- * `gowchar` is released under the [3-clause BSD](https://github.com/orofarne/gowchar/blob/master/LICENSE) license.
-
-Given the above, `hid` is licensed under GNU LGPL 2.1 or later on Linux and 3-clause BSD on other platforms.

+ 0 - 54
vendor/github.com/karalabe/hid/generic.go

@@ -1,54 +0,0 @@
-// hid - Gopher Interface Devices (USB HID)
-// Copyright (c) 2019 Péter Szilágyi, Guillaume Ballet. All rights reserved.
-
-package hid
-
-import (
-	"C"
-)
-
-type GenericEndpointDirection uint8
-
-// List of endpoint direction types
-const (
-	GenericEndpointDirectionOut = 0x00
-	GenericEndpointDirectionIn  = 0x80
-)
-
-// List of endpoint attributes
-const (
-	GenericEndpointAttributeInterrupt = 3
-)
-
-// GenericEndpoint represents a USB endpoint
-type GenericEndpoint struct {
-	Address    uint8
-	Direction  GenericEndpointDirection
-	Attributes uint8
-}
-
-type GenericDeviceInfo struct {
-	Path      string // Platform-specific device path
-	VendorID  uint16 // Device Vendor ID
-	ProductID uint16 // Device Product ID
-
-	device *GenericDevice
-
-	Interface int
-
-	Endpoints []GenericEndpoint
-}
-
-func (gdi *GenericDeviceInfo) Type() DeviceType {
-	return DeviceTypeGeneric
-}
-
-// Platform-specific device path
-func (gdi *GenericDeviceInfo) GetPath() string {
-	return gdi.Path
-}
-
-// IDs returns the vendor and product IDs for the device
-func (gdi *GenericDeviceInfo) IDs() (uint16, uint16, int, uint16) {
-	return gdi.VendorID, gdi.ProductID, gdi.Interface, 0
-}

+ 0 - 52
vendor/github.com/karalabe/hid/hid.go

@@ -1,52 +0,0 @@
-// hid - Gopher Interface Devices (USB HID)
-// Copyright (c) 2017 Péter Szilágyi. All rights reserved.
-//
-// This file is released under the 3-clause BSD license. Note however that Linux
-// support depends on libusb, released under GNU LGPL 2.1 or later.
-
-// Package hid provides an interface for USB HID devices.
-package hid
-
-import "errors"
-
-// ErrDeviceClosed is returned for operations where the device closed before or
-// during the execution.
-var ErrDeviceClosed = errors.New("hid: device closed")
-
-// ErrUnsupportedPlatform is returned for all operations where the underlying
-// operating system is not supported by the library.
-var ErrUnsupportedPlatform = errors.New("hid: unsupported platform")
-
-// HidDeviceInfo is a hidapi info structure.
-type HidDeviceInfo struct {
-	Path         string // Platform-specific device path
-	VendorID     uint16 // Device Vendor ID
-	ProductID    uint16 // Device Product ID
-	Release      uint16 // Device Release Number in binary-coded decimal, also known as Device Version Number
-	Serial       string // Serial Number
-	Manufacturer string // Manufacturer String
-	Product      string // Product string
-	UsagePage    uint16 // Usage Page for this Device/Interface (Windows/Mac only)
-	Usage        uint16 // Usage for this Device/Interface (Windows/Mac only)
-
-	// The USB interface which this logical device
-	// represents. Valid on both Linux implementations
-	// in all cases, and valid on the Windows implementation
-	// only if the device contains more than one interface.
-	Interface int
-}
-
-// GetPath returns the system-dependent path to the device
-func (hdi *HidDeviceInfo) GetPath() string {
-	return hdi.Path
-}
-
-// IDs returns the vendor and product id of the device
-func (hdi *HidDeviceInfo) IDs() (uint16, uint16, int, uint16) {
-	return hdi.VendorID, hdi.ProductID, hdi.Interface, hdi.UsagePage
-}
-
-// Type returns the type of the device (HID or generic)
-func (hdi *HidDeviceInfo) Type() DeviceType {
-	return DeviceTypeHID
-}

+ 0 - 76
vendor/github.com/karalabe/hid/hid_disabled.go

@@ -1,76 +0,0 @@
-// hid - Gopher Interface Devices (USB HID)
-// Copyright (c) 2017 Péter Szilágyi. All rights reserved.
-//
-// This file is released under the 3-clause BSD license. Note however that Linux
-// support depends on libusb, released under GNU LGPL 2.1 or later.
-
-// +build !freebsd,!linux,!darwin,!windows ios !cgo
-
-package hid
-
-// Supported returns whether this platform is supported by the HID library or not.
-// The goal of this method is to allow programatically handling platforms that do
-// not support USB HID and not having to fall back to build constraints.
-func Supported() bool {
-	return false
-}
-
-// Enumerate returns a list of all the HID devices attached to the system which
-// match the vendor and product id. On platforms that this file implements the
-// function is a noop and returns an empty list always.
-func Enumerate(vendorID uint16, productID uint16) []DeviceInfo {
-	return nil
-}
-
-// HidDevice is a live HID USB connected device handle. On platforms that this file
-// implements the type lacks the actual HID device and all methods are noop.
-type HidDevice struct {
-	HidDeviceInfo // Embed the infos for easier access
-}
-
-// Open connects to an HID device by its path name. On platforms that this file
-// implements the method just returns an error.
-func (info HidDeviceInfo) Open() (*Device, error) {
-	return nil, ErrUnsupportedPlatform
-}
-
-// Close releases the HID USB device handle. On platforms that this file implements
-// the method is just a noop.
-func (dev *HidDevice) Close() error { return ErrUnsupportedPlatform }
-
-// Write sends an output report to a HID device. On platforms that this file
-// implements the method just returns an error.
-func (dev *HidDevice) Write(b []byte) (int, error) {
-	return 0, ErrUnsupportedPlatform
-}
-
-// Read retrieves an input report from a HID device. On platforms that this file
-// implements the method just returns an error.
-func (dev *HidDevice) Read(b []byte) (int, error) {
-	return 0, ErrUnsupportedPlatform
-}
-
-// Open tries to open the USB device represented by the current DeviceInfo
-func (gdi *GenericDeviceInfo) Open() (Device, error) {
-	return nil, ErrUnsupportedPlatform
-}
-
-// GenericDevice represents a generic USB device
-type GenericDevice struct {
-	*GenericDeviceInfo // Embed the infos for easier access
-}
-
-// Write implements io.ReaderWriter
-func (gd *GenericDevice) Write(b []byte) (int, error) {
-	return 0, ErrUnsupportedPlatform
-}
-
-// Read implements io.ReaderWriter
-func (gd *GenericDevice) Read(b []byte) (int, error) {
-	return 0, ErrUnsupportedPlatform
-}
-
-// Close a previously opened generic USB device
-func (gd *GenericDevice) Close() error {
-	return ErrUnsupportedPlatform
-}

+ 0 - 459
vendor/github.com/karalabe/hid/hid_enabled.go

@@ -1,459 +0,0 @@
-// hid - Gopher Interface Devices (USB HID)
-// Copyright (c) 2017 Péter Szilágyi. All rights reserved.
-//
-// This file is released under the 3-clause BSD license. Note however that Linux
-// support depends on libusb, released under LGNU GPL 2.1 or later.
-
-// +build freebsd,cgo linux,cgo darwin,!ios,cgo windows,cgo
-
-package hid
-
-/*
-#cgo CFLAGS: -I./hidapi/hidapi
-
-#cgo linux CFLAGS: -I./libusb/libusb -DDEFAULT_VISIBILITY="" -DOS_LINUX -D_GNU_SOURCE -DPOLL_NFDS_TYPE=int
-#cgo linux,!android LDFLAGS: -lrt
-#cgo darwin CFLAGS: -DOS_DARWIN -I./libusb/libusb
-#cgo darwin LDFLAGS: -framework CoreFoundation -framework IOKit -lusb-1.0.0
-#cgo windows CFLAGS: -DOS_WINDOWS
-#cgo windows LDFLAGS: -lsetupapi
-#cgo freebsd CFLAGS: -DOS_FREEBSD
-#cgo freebsd LDFLAGS: -lusb
-
-#ifdef OS_LINUX
-	#include <poll.h>
-	#include "os/threads_posix.c"
-	#include "os/poll_posix.c"
-
-	#include "os/linux_usbfs.c"
-	#include "os/linux_netlink.c"
-
-	#include "core.c"
-	#include "descriptor.c"
-	#include "hotplug.c"
-	#include "io.c"
-	#include "strerror.c"
-	#include "sync.c"
-
-	#include "hidapi/libusb/hid.c"
-#elif OS_DARWIN
-	#include <libusb.h>
-	#include "hidapi/mac/hid.c"
-#elif OS_WINDOWS
-	#include "hidapi/windows/hid.c"
-#elif OS_FREEBSD
-    #include <stdlib.h>
-	#include <libusb.h>
-	#include "hidapi/libusb/hid.c"
-#endif
-
-#if defined(OS_LINUX) || defined(OS_WINDOWS)
-	void copy_device_list_to_slice(struct libusb_device **data, struct libusb_device **list, int count)
-	{
-		int i;
-		struct libusb_device *current = *list;
-		for (i=0; i<count; i++)
-		{
-			 data[i] = current;
-			 current = list_entry(current->list.next, struct libusb_device, list);
-		}
-	}
-#elif defined(OS_DARWIN) || defined(OS_FREEBSD)
-	void copy_device_list_to_slice(struct libusb_device **data, struct libusb_device **list, int count)
-	{
-		int i;
-		// No memcopy because the struct size isn't available for a sizeof()
-		for (i=0; i<count; i++)
-		{
-			data[i] = list[i];
-		}
-	}
-#endif
-
-const char *usb_strerror(int err)
-{
-	return libusb_strerror(err);
-}
-*/
-import "C"
-
-import (
-	"errors"
-	"fmt"
-	"reflect"
-	"runtime"
-	"sync"
-	"unsafe"
-)
-
-// enumerateLock is a mutex serializing access to USB device enumeration needed
-// by the macOS USB HID system calls, which require 2 consecutive method calls
-// for enumeration, causing crashes if called concurrently.
-//
-// For more details, see:
-//   https://developer.apple.com/documentation/iokit/1438371-iohidmanagersetdevicematching
-//   > "subsequent calls will cause the hid manager to release previously enumerated devices"
-var enumerateLock sync.Mutex
-
-// Supported returns whether this platform is supported by the HID library or not.
-// The goal of this method is to allow programatically handling platforms that do
-// not support USB HID and not having to fall back to build constraints.
-func Supported() bool {
-	return true
-}
-
-// genericEnumerate performs generic USB device enumeration
-func genericEnumerate(vendorID uint16, productID uint16) ([]DeviceInfo, error) {
-	var infos []DeviceInfo
-	var ctx *C.struct_libusb_context
-	errCode := int(C.libusb_init((**C.struct_libusb_context)(&ctx)))
-	if errCode < 0 {
-		return nil, fmt.Errorf("Error while initializing libusb: %d", errCode)
-	}
-
-	var deviceListPtr **C.struct_libusb_device
-	count := C.libusb_get_device_list(ctx, (***C.struct_libusb_device)(&deviceListPtr))
-	if count < 0 {
-		return nil, fmt.Errorf("Error code listing devices: %d", count)
-	}
-	defer C.libusb_free_device_list(deviceListPtr, C.int(count))
-
-	deviceList := make([]*C.struct_libusb_device, count)
-	dlhdr := (*reflect.SliceHeader)(unsafe.Pointer(&deviceList))
-	C.copy_device_list_to_slice((**C.struct_libusb_device)(unsafe.Pointer(dlhdr.Data)), deviceListPtr, C.int(count))
-
-	for devnum, dev := range deviceList {
-		var desc C.struct_libusb_device_descriptor
-		errCode := int(C.libusb_get_device_descriptor(dev, &desc))
-		if errCode < 0 {
-			return nil, fmt.Errorf("Error getting device descriptor for generic device %d: %d", devnum, errCode)
-		}
-
-		// Start by checking the vendor id and the product id if necessary
-		if uint16(desc.idVendor) != vendorID || !(productID == 0 || uint16(desc.idProduct) == productID) {
-			continue
-		}
-
-		// Skip HID devices, they will be handled later
-		switch desc.bDeviceClass {
-		case 0:
-			/* Device class is specified at interface level */
-			for cfgnum := 0; cfgnum < int(desc.bNumConfigurations); cfgnum++ {
-				var cfgdesc *C.struct_libusb_config_descriptor
-				errCode = int(C.libusb_get_config_descriptor(dev, C.uint8_t(cfgnum), &cfgdesc))
-				if errCode != 0 {
-					return nil, fmt.Errorf("Error getting device configuration #%d for generic device %d: %d", cfgnum, devnum, errCode)
-				}
-
-				var ifs []C.struct_libusb_interface
-				ifshdr := (*reflect.SliceHeader)(unsafe.Pointer(&ifs))
-				ifshdr.Cap = int(cfgdesc.bNumInterfaces)
-				ifshdr.Len = int(cfgdesc.bNumInterfaces)
-				ifshdr.Data = uintptr(unsafe.Pointer(cfgdesc._interface))
-
-				for ifnum, ifc := range ifs {
-					var ifdescs []C.struct_libusb_interface_descriptor
-					ifdshdr := (*reflect.SliceHeader)(unsafe.Pointer(&ifdescs))
-					ifdshdr.Cap = int(ifc.num_altsetting)
-					ifdshdr.Len = int(ifc.num_altsetting)
-					ifdshdr.Data = uintptr(unsafe.Pointer(ifc.altsetting))
-
-					for _, alt := range ifdescs {
-						if alt.bInterfaceClass != 3 {
-							// Device isn't a HID interface, add them to the device list.
-
-							var endps []C.struct_libusb_endpoint_descriptor
-							endpshdr := (*reflect.SliceHeader)(unsafe.Pointer(&endps))
-							endpshdr.Cap = int(alt.bNumEndpoints)
-							endpshdr.Len = int(alt.bNumEndpoints)
-							endpshdr.Data = uintptr(unsafe.Pointer(alt.endpoint))
-
-							endpoints := make([]GenericEndpoint, alt.bNumEndpoints)
-
-							for ne, endpoint := range endps {
-								endpoints[ne] = GenericEndpoint{
-									Direction:  GenericEndpointDirection(endpoint.bEndpointAddress) & GenericEndpointDirectionIn,
-									Address:    uint8(endpoint.bEndpointAddress),
-									Attributes: uint8(endpoint.bmAttributes),
-								}
-							}
-
-							info := &GenericDeviceInfo{
-								Path:      fmt.Sprintf("%x:%x:%d", vendorID, uint16(desc.idProduct), uint8(C.libusb_get_port_number(dev))),
-								VendorID:  uint16(desc.idVendor),
-								ProductID: uint16(desc.idProduct),
-								device: &GenericDevice{
-									device: dev,
-								},
-								Endpoints: endpoints,
-								Interface: ifnum,
-							}
-							info.device.GenericDeviceInfo = info
-							infos = append(infos, info)
-						}
-					}
-				}
-			}
-		case 3:
-			// Device class is HID, skip it
-			continue
-		}
-	}
-
-	return infos, nil
-}
-
-// Enumerate returns a list of all the HID devices attached to the system which
-// match the vendor and product id:
-//  - If the vendor id is set to 0 then any vendor matches.
-//  - If the product id is set to 0 then any product matches.
-//  - If the vendor and product id are both 0, all HID devices are returned.
-func Enumerate(vendorID uint16, productID uint16) ([]DeviceInfo, error) {
-	enumerateLock.Lock()
-	defer enumerateLock.Unlock()
-
-	infos, err := genericEnumerate(vendorID, productID)
-
-	if err != nil {
-		return nil, err
-	}
-
-	// Gather all device infos and ensure they are freed before returning
-	head := C.hid_enumerate(C.ushort(vendorID), C.ushort(productID))
-	if head == nil {
-		return nil, nil
-	}
-	defer C.hid_free_enumeration(head)
-
-	// Iterate the list and retrieve the device details
-	for ; head != nil; head = head.next {
-		info := &HidDeviceInfo{
-			Path:      C.GoString(head.path),
-			VendorID:  uint16(head.vendor_id),
-			ProductID: uint16(head.product_id),
-			Release:   uint16(head.release_number),
-			UsagePage: uint16(head.usage_page),
-			Usage:     uint16(head.usage),
-			Interface: int(head.interface_number),
-		}
-		if head.serial_number != nil {
-			info.Serial, _ = wcharTToString(head.serial_number)
-		}
-		if head.product_string != nil {
-			info.Product, _ = wcharTToString(head.product_string)
-		}
-		if head.manufacturer_string != nil {
-			info.Manufacturer, _ = wcharTToString(head.manufacturer_string)
-		}
-		infos = append(infos, info)
-	}
-	return infos, nil
-}
-
-// Open connects to an HID device by its path name.
-func (info *HidDeviceInfo) Open() (Device, error) {
-	enumerateLock.Lock()
-	defer enumerateLock.Unlock()
-
-	path := C.CString(info.Path)
-	defer C.free(unsafe.Pointer(path))
-
-	device := C.hid_open_path(path)
-	if device == nil {
-		return nil, errors.New("hidapi: failed to open device")
-	}
-	return &HidDevice{
-		DeviceInfo: info,
-		device:     device,
-	}, nil
-}
-
-// HidDevice is a live HID USB connected device handle.
-type HidDevice struct {
-	DeviceInfo // Embed the infos for easier access
-
-	device *C.hid_device // Low level HID device to communicate through
-	lock   sync.Mutex
-}
-
-// Close releases the HID USB device handle.
-func (dev *HidDevice) Close() error {
-	dev.lock.Lock()
-	defer dev.lock.Unlock()
-
-	if dev.device != nil {
-		C.hid_close(dev.device)
-		dev.device = nil
-	}
-	return nil
-}
-
-// Write sends an output report to a HID device.
-//
-// Write will send the data on the first OUT endpoint, if one exists. If it does
-// not, it will send the data through the Control Endpoint (Endpoint 0).
-func (dev *HidDevice) Write(b []byte) (int, error) {
-	// Abort if nothing to write
-	if len(b) == 0 {
-		return 0, nil
-	}
-	// Abort if device closed in between
-	dev.lock.Lock()
-	device := dev.device
-	dev.lock.Unlock()
-
-	if device == nil {
-		return 0, ErrDeviceClosed
-	}
-	// Prepend a HID report ID on Windows, other OSes don't need it
-	var report []byte
-	if runtime.GOOS == "windows" {
-		report = append([]byte{0x00}, b...)
-	} else {
-		report = b
-	}
-	// Execute the write operation
-	written := int(C.hid_write(device, (*C.uchar)(&report[0]), C.size_t(len(report))))
-	if written == -1 {
-		// If the write failed, verify if closed or other error
-		dev.lock.Lock()
-		device = dev.device
-		dev.lock.Unlock()
-
-		if device == nil {
-			return 0, ErrDeviceClosed
-		}
-		// Device not closed, some other error occurred
-		message := C.hid_error(device)
-		if message == nil {
-			return 0, errors.New("hidapi: unknown failure")
-		}
-		failure, _ := wcharTToString(message)
-		return 0, errors.New("hidapi: " + failure)
-	}
-	return written, nil
-}
-
-// Read retrieves an input report from a HID device.
-func (dev *HidDevice) Read(b []byte) (int, error) {
-	// Aborth if nothing to read
-	if len(b) == 0 {
-		return 0, nil
-	}
-	// Abort if device closed in between
-	dev.lock.Lock()
-	device := dev.device
-	dev.lock.Unlock()
-
-	if device == nil {
-		return 0, ErrDeviceClosed
-	}
-	// Execute the read operation
-	read := int(C.hid_read(device, (*C.uchar)(&b[0]), C.size_t(len(b))))
-	if read == -1 {
-		// If the read failed, verify if closed or other error
-		dev.lock.Lock()
-		device = dev.device
-		dev.lock.Unlock()
-
-		if device == nil {
-			return 0, ErrDeviceClosed
-		}
-		// Device not closed, some other error occurred
-		message := C.hid_error(device)
-		if message == nil {
-			return 0, errors.New("hidapi: unknown failure")
-		}
-		failure, _ := wcharTToString(message)
-		return 0, errors.New("hidapi: " + failure)
-	}
-	return read, nil
-}
-
-// Type identify the device as a HID device
-func (dev *HidDevice) Type() DeviceType {
-	return dev.DeviceInfo.Type()
-}
-
-// Open tries to open the USB device represented by the current DeviceInfo
-func (gdi *GenericDeviceInfo) Open() (Device, error) {
-	var handle *C.struct_libusb_device_handle
-	errCode := int(C.libusb_open(gdi.device.device, (**C.struct_libusb_device_handle)(&handle)))
-	if errCode < 0 {
-		return nil, fmt.Errorf("Error opening generic USB device %v, code %d", gdi.device.handle, errCode)
-	}
-
-	gdi.device.handle = handle
-	// QUESTION: ai-je deja initialie le GDI ?
-	// 	GenericDeviceInfo: gdi,
-	// 	handle:            handle,
-	// }
-
-	for _, endpoint := range gdi.Endpoints {
-		switch {
-		case endpoint.Direction == GenericEndpointDirectionOut && endpoint.Attributes == GenericEndpointAttributeInterrupt:
-			gdi.device.WEndpoint = endpoint.Address
-		case endpoint.Direction == GenericEndpointDirectionIn && endpoint.Attributes == GenericEndpointAttributeInterrupt:
-			gdi.device.REndpoint = endpoint.Address
-		}
-	}
-
-	if gdi.device.REndpoint == 0 || gdi.device.WEndpoint == 0 {
-		return nil, fmt.Errorf("Missing endpoint in device %#x:%#x:%d", gdi.VendorID, gdi.ProductID, gdi.Interface)
-	}
-
-	return gdi.device, nil
-}
-
-// GenericDevice represents a generic USB device
-type GenericDevice struct {
-	*GenericDeviceInfo // Embed the infos for easier access
-
-	REndpoint uint8
-	WEndpoint uint8
-
-	device *C.struct_libusb_device
-	handle *C.struct_libusb_device_handle
-	lock   sync.Mutex
-}
-
-// Write implements io.ReaderWriter
-func (gd *GenericDevice) Write(b []byte) (int, error) {
-	gd.lock.Lock()
-	defer gd.lock.Unlock()
-
-	out, err := interruptTransfer(gd.handle, gd.WEndpoint, b)
-	return len(out), err
-}
-
-// Read implements io.ReaderWriter
-func (gd *GenericDevice) Read(b []byte) (int, error) {
-	gd.lock.Lock()
-	defer gd.lock.Unlock()
-
-	out, err := interruptTransfer(gd.handle, gd.REndpoint, b)
-	return len(out), err
-}
-
-// Close a previously opened generic USB device
-func (gd *GenericDevice) Close() error {
-	gd.lock.Lock()
-	defer gd.lock.Unlock()
-
-	if gd.handle != nil {
-		C.libusb_close(gd.handle)
-		gd.handle = nil
-	}
-
-	return nil
-}
-
-// interruptTransfer is a helpler function for libusb's interrupt transfer function
-func interruptTransfer(handle *C.struct_libusb_device_handle, endpoint uint8, data []byte) ([]byte, error) {
-	var transferred C.int
-	errCode := int(C.libusb_interrupt_transfer(handle, (C.uchar)(endpoint), (*C.uchar)(&data[0]), (C.int)(len(data)), &transferred, (C.uint)(0)))
-	if errCode != 0 {
-		return nil, fmt.Errorf("Interrupt transfer error: %s", C.GoString(C.usb_strerror(C.int(errCode))))
-	}
-	return data[:int(transferred)], nil
-}

+ 0 - 728
vendor/github.com/karalabe/hid/libusb/libusb/os/poll_windows.c

@@ -1,728 +0,0 @@
-/*
- * poll_windows: poll compatibility wrapper for Windows
- * Copyright © 2012-2013 RealVNC Ltd.
- * Copyright © 2009-2010 Pete Batard <pete@akeo.ie>
- * With contributions from Michael Plante, Orin Eman et al.
- * Parts of poll implementation from libusb-win32, by Stephan Meyer et al.
- *
- * 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 RW_READ and once in RW_WRITE mode to obtain 2 separate
- *   pollable fds
- * - leave the core functions call the poll routine and flag POLLIN/POLLOUT
- *
- * The pipe pollable synchronous I/O works using the overlapped event associated
- * with a fake pipe. The read/write functions are only meant to be used in that
- * context.
- */
-#include <config.h>
-
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "libusbi.h"
-
-// Uncomment to debug the polling layer
-//#define DEBUG_POLL_WINDOWS
-#if defined(DEBUG_POLL_WINDOWS)
-#define poll_dbg usbi_dbg
-#else
-// MSVC++ < 2005 cannot use a variadic argument and non MSVC
-// compilers produce warnings if parenthesis are omitted.
-#if defined(_MSC_VER) && (_MSC_VER < 1400)
-#define poll_dbg
-#else
-#define poll_dbg(...)
-#endif
-#endif
-
-#if defined(_PREFAST_)
-#pragma warning(disable:28719)
-#endif
-
-#define CHECK_INIT_POLLING do {if(!is_polling_set) init_polling();} while(0)
-
-// public fd data
-const struct winfd INVALID_WINFD = {-1, INVALID_HANDLE_VALUE, NULL, NULL, NULL, RW_NONE};
-struct winfd poll_fd[MAX_FDS];
-// internal fd data
-struct {
-	CRITICAL_SECTION mutex; // lock for fds
-	// Additional variables for XP CancelIoEx partial emulation
-	HANDLE original_handle;
-	DWORD thread_id;
-} _poll_fd[MAX_FDS];
-
-// globals
-BOOLEAN is_polling_set = FALSE;
-LONG pipe_number = 0;
-static volatile LONG compat_spinlock = 0;
-
-#if !defined(_WIN32_WCE)
-// CancelIoEx, available on Vista and later only, provides the ability to cancel
-// a single transfer (OVERLAPPED) when used. As it may not be part of any of the
-// platform headers, we hook into the Kernel32 system DLL directly to seek it.
-static BOOL (__stdcall *pCancelIoEx)(HANDLE, LPOVERLAPPED) = NULL;
-#define Use_Duplicate_Handles (pCancelIoEx == NULL)
-
-static inline void setup_cancel_io(void)
-{
-	HMODULE hKernel32 = GetModuleHandleA("KERNEL32");
-	if (hKernel32 != NULL) {
-		pCancelIoEx = (BOOL (__stdcall *)(HANDLE,LPOVERLAPPED))
-			GetProcAddress(hKernel32, "CancelIoEx");
-	}
-	usbi_dbg("Will use CancelIo%s for I/O cancellation",
-		Use_Duplicate_Handles?"":"Ex");
-}
-
-static inline BOOL cancel_io(int _index)
-{
-	if ((_index < 0) || (_index >= MAX_FDS)) {
-		return FALSE;
-	}
-
-	if ( (poll_fd[_index].fd < 0) || (poll_fd[_index].handle == INVALID_HANDLE_VALUE)
-	  || (poll_fd[_index].handle == 0) || (poll_fd[_index].overlapped == NULL) ) {
-		return TRUE;
-	}
-	if (poll_fd[_index].itransfer && poll_fd[_index].cancel_fn) {
-		// Cancel outstanding transfer via the specific callback
-		(*poll_fd[_index].cancel_fn)(poll_fd[_index].itransfer);
-		return TRUE;
-	}
-	if (pCancelIoEx != NULL) {
-		return (*pCancelIoEx)(poll_fd[_index].handle, poll_fd[_index].overlapped);
-	}
-	if (_poll_fd[_index].thread_id == GetCurrentThreadId()) {
-		return CancelIo(poll_fd[_index].handle);
-	}
-	usbi_warn(NULL, "Unable to cancel I/O that was started from another thread");
-	return FALSE;
-}
-#else
-#define Use_Duplicate_Handles FALSE
-
-static __inline void setup_cancel_io()
-{
-	// No setup needed on WinCE
-}
-
-static __inline BOOL cancel_io(int _index)
-{
-	if ((_index < 0) || (_index >= MAX_FDS)) {
-		return FALSE;
-	}
-	if ( (poll_fd[_index].fd < 0) || (poll_fd[_index].handle == INVALID_HANDLE_VALUE)
-	  || (poll_fd[_index].handle == 0) || (poll_fd[_index].overlapped == NULL) ) {
-		return TRUE;
-	}
-	if (poll_fd[_index].itransfer && poll_fd[_index].cancel_fn) {
-		// Cancel outstanding transfer via the specific callback
-		(*poll_fd[_index].cancel_fn)(poll_fd[_index].itransfer);
-	}
-	return TRUE;
-}
-#endif
-
-// Init
-void init_polling(void)
-{
-	int i;
-
-	while (InterlockedExchange((LONG *)&compat_spinlock, 1) == 1) {
-		SleepEx(0, TRUE);
-	}
-	if (!is_polling_set) {
-		setup_cancel_io();
-		for (i=0; i<MAX_FDS; i++) {
-			poll_fd[i] = INVALID_WINFD;
-			_poll_fd[i].original_handle = INVALID_HANDLE_VALUE;
-			_poll_fd[i].thread_id = 0;
-			InitializeCriticalSection(&_poll_fd[i].mutex);
-		}
-		is_polling_set = TRUE;
-	}
-	InterlockedExchange((LONG *)&compat_spinlock, 0);
-}
-
-// Internal function to retrieve the table index (and lock the fd mutex)
-static int _fd_to_index_and_lock(int fd)
-{
-	int i;
-
-	if (fd < 0)
-		return -1;
-
-	for (i=0; i<MAX_FDS; i++) {
-		if (poll_fd[i].fd == fd) {
-			EnterCriticalSection(&_poll_fd[i].mutex);
-			// fd might have changed before we got to critical
-			if (poll_fd[i].fd != fd) {
-				LeaveCriticalSection(&_poll_fd[i].mutex);
-				continue;
-			}
-			return i;
-		}
-	}
-	return -1;
-}
-
-static OVERLAPPED *create_overlapped(void)
-{
-	OVERLAPPED *overlapped = (OVERLAPPED*) calloc(1, sizeof(OVERLAPPED));
-	if (overlapped == NULL) {
-		return NULL;
-	}
-	overlapped->hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
-	if(overlapped->hEvent == NULL) {
-		free (overlapped);
-		return NULL;
-	}
-	return overlapped;
-}
-
-static void free_overlapped(OVERLAPPED *overlapped)
-{
-	if (overlapped == NULL)
-		return;
-
-	if ( (overlapped->hEvent != 0)
-	  && (overlapped->hEvent != INVALID_HANDLE_VALUE) ) {
-		CloseHandle(overlapped->hEvent);
-	}
-	free(overlapped);
-}
-
-void exit_polling(void)
-{
-	int i;
-
-	while (InterlockedExchange((LONG *)&compat_spinlock, 1) == 1) {
-		SleepEx(0, TRUE);
-	}
-	if (is_polling_set) {
-		is_polling_set = FALSE;
-
-		for (i=0; i<MAX_FDS; i++) {
-			// Cancel any async I/O (handle can be invalid)
-			cancel_io(i);
-			// If anything was pending on that I/O, it should be
-			// terminating, and we should be able to access the fd
-			// mutex lock before too long
-			EnterCriticalSection(&_poll_fd[i].mutex);
-			free_overlapped(poll_fd[i].overlapped);
-			if (Use_Duplicate_Handles) {
-				// Close duplicate handle
-				if (_poll_fd[i].original_handle != INVALID_HANDLE_VALUE) {
-					CloseHandle(poll_fd[i].handle);
-				}
-			}
-			poll_fd[i] = INVALID_WINFD;
-			LeaveCriticalSection(&_poll_fd[i].mutex);
-			DeleteCriticalSection(&_poll_fd[i].mutex);
-		}
-	}
-	InterlockedExchange((LONG *)&compat_spinlock, 0);
-}
-
-/*
- * Create a fake pipe.
- * As libusb only uses pipes for signaling, all we need from a pipe is an
- * event. To that extent, we create a single wfd and overlapped as a means
- * to access that event.
- */
-int usbi_pipe(int filedes[2])
-{
-	int i;
-	OVERLAPPED* overlapped;
-
-	CHECK_INIT_POLLING;
-
-	overlapped = create_overlapped();
-
-	if (overlapped == NULL) {
-		return -1;
-	}
-	// The overlapped must have status pending for signaling to work in poll
-	overlapped->Internal = STATUS_PENDING;
-	overlapped->InternalHigh = 0;
-
-	for (i=0; i<MAX_FDS; i++) {
-		if (poll_fd[i].fd < 0) {
-			EnterCriticalSection(&_poll_fd[i].mutex);
-			// fd might have been allocated before we got to critical
-			if (poll_fd[i].fd >= 0) {
-				LeaveCriticalSection(&_poll_fd[i].mutex);
-				continue;
-			}
-
-			// Use index as the unique fd number
-			poll_fd[i].fd = i;
-			// Read end of the "pipe"
-			filedes[0] = poll_fd[i].fd;
-			// We can use the same handle for both ends
-			filedes[1] = filedes[0];
-
-			poll_fd[i].handle = DUMMY_HANDLE;
-			poll_fd[i].overlapped = overlapped;
-			// There's no polling on the write end, so we just use READ for our needs
-			poll_fd[i].rw = RW_READ;
-			_poll_fd[i].original_handle = INVALID_HANDLE_VALUE;
-			LeaveCriticalSection(&_poll_fd[i].mutex);
-			return 0;
-		}
-	}
-	free_overlapped(overlapped);
-	return -1;
-}
-
-/*
- * Create both an fd and an OVERLAPPED from an open Windows handle, so that
- * it can be used with our polling function
- * The handle MUST support overlapped transfers (usually requires CreateFile
- * with FILE_FLAG_OVERLAPPED)
- * Return a pollable file descriptor struct, or INVALID_WINFD on error
- *
- * Note that the fd returned by this function is a per-transfer fd, rather
- * than a per-session fd and cannot be used for anything else but our
- * custom functions (the fd itself points to the NUL: device)
- * if you plan to do R/W on the same handle, you MUST create 2 fds: one for
- * read and one for write. Using a single R/W fd is unsupported and will
- * produce unexpected results
- */
-struct winfd usbi_create_fd(HANDLE handle, int access_mode, struct usbi_transfer *itransfer, cancel_transfer *cancel_fn)
-{
-	int i;
-	struct winfd wfd = INVALID_WINFD;
-	OVERLAPPED* overlapped = NULL;
-
-	CHECK_INIT_POLLING;
-
-	if ((handle == 0) || (handle == INVALID_HANDLE_VALUE)) {
-		return INVALID_WINFD;
-	}
-
-	wfd.itransfer = itransfer;
-	wfd.cancel_fn = cancel_fn;
-
-	if ((access_mode != RW_READ) && (access_mode != RW_WRITE)) {
-		usbi_warn(NULL, "only one of RW_READ or RW_WRITE are supported. "
-			"If you want to poll for R/W simultaneously, create multiple fds from the same handle.");
-		return INVALID_WINFD;
-	}
-	if (access_mode == RW_READ) {
-		wfd.rw = RW_READ;
-	} else {
-		wfd.rw = RW_WRITE;
-	}
-
-	overlapped = create_overlapped();
-	if(overlapped == NULL) {
-		return INVALID_WINFD;
-	}
-
-	for (i=0; i<MAX_FDS; i++) {
-		if (poll_fd[i].fd < 0) {
-			EnterCriticalSection(&_poll_fd[i].mutex);
-			// fd might have been removed before we got to critical
-			if (poll_fd[i].fd >= 0) {
-				LeaveCriticalSection(&_poll_fd[i].mutex);
-				continue;
-			}
-			// Use index as the unique fd number
-			wfd.fd = i;
-			// Attempt to emulate some of the CancelIoEx behaviour on platforms
-			// that don't have it
-			if (Use_Duplicate_Handles) {
-				_poll_fd[i].thread_id = GetCurrentThreadId();
-				if (!DuplicateHandle(GetCurrentProcess(), handle, GetCurrentProcess(),
-					&wfd.handle, 0, TRUE, DUPLICATE_SAME_ACCESS)) {
-					usbi_dbg("could not duplicate handle for CancelIo - using original one");
-					wfd.handle = handle;
-					// Make sure we won't close the original handle on fd deletion then
-					_poll_fd[i].original_handle = INVALID_HANDLE_VALUE;
-				} else {
-					_poll_fd[i].original_handle = handle;
-				}
-			} else {
-				wfd.handle = handle;
-			}
-			wfd.overlapped = overlapped;
-			memcpy(&poll_fd[i], &wfd, sizeof(struct winfd));
-			LeaveCriticalSection(&_poll_fd[i].mutex);
-			return wfd;
-		}
-	}
-	free_overlapped(overlapped);
-	return INVALID_WINFD;
-}
-
-static void _free_index(int _index)
-{
-	// Cancel any async IO (Don't care about the validity of our handles for this)
-	cancel_io(_index);
-	// close the duplicate handle (if we have an actual duplicate)
-	if (Use_Duplicate_Handles) {
-		if (_poll_fd[_index].original_handle != INVALID_HANDLE_VALUE) {
-			CloseHandle(poll_fd[_index].handle);
-		}
-		_poll_fd[_index].original_handle = INVALID_HANDLE_VALUE;
-		_poll_fd[_index].thread_id = 0;
-	}
-	free_overlapped(poll_fd[_index].overlapped);
-	poll_fd[_index] = INVALID_WINFD;
-}
-
-/*
- * Release a pollable file descriptor.
- *
- * Note that the associated Windows handle is not closed by this call
- */
-void usbi_free_fd(struct winfd *wfd)
-{
-	int _index;
-
-	CHECK_INIT_POLLING;
-
-	_index = _fd_to_index_and_lock(wfd->fd);
-	if (_index < 0) {
-		return;
-	}
-	_free_index(_index);
-	*wfd = INVALID_WINFD;
-	LeaveCriticalSection(&_poll_fd[_index].mutex);
-}
-
-/*
- * The functions below perform various conversions between fd, handle and OVERLAPPED
- */
-struct winfd fd_to_winfd(int fd)
-{
-	int i;
-	struct 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;
-
-	CHECK_INIT_POLLING;
-
-	triggered = 0;
-	handles_to_wait_on = (HANDLE*) calloc(nfds+1, sizeof(HANDLE));	// +1 for fd_update
-	handle_to_index = (int*) calloc(nfds, sizeof(int));
-	if ((handles_to_wait_on == NULL) || (handle_to_index == NULL)) {
-		errno = ENOMEM;
-		triggered = -1;
-		goto poll_exit;
-	}
-
-	for (i = 0; i < nfds; ++i) {
-		fds[i].revents = 0;
-
-		// Only one of POLLIN or POLLOUT can be selected with this version of poll (not both)
-		if ((fds[i].events & ~POLLIN) && (!(fds[i].events & POLLOUT))) {
-			fds[i].revents |= POLLERR;
-			errno = EACCES;
-			usbi_warn(NULL, "unsupported set of events");
-			triggered = -1;
-			goto poll_exit;
-		}
-
-		_index = _fd_to_index_and_lock(fds[i].fd);
-		poll_dbg("fd[%d]=%d: (overlapped=%p) got events %04X", i, poll_fd[_index].fd, poll_fd[_index].overlapped, fds[i].events);
-
-		if ( (_index < 0) || (poll_fd[_index].handle == INVALID_HANDLE_VALUE)
-		  || (poll_fd[_index].handle == 0) || (poll_fd[_index].overlapped == NULL)) {
-			fds[i].revents |= POLLNVAL | POLLERR;
-			errno = EBADF;
-			if (_index >= 0) {
-				LeaveCriticalSection(&_poll_fd[_index].mutex);
-			}
-			usbi_warn(NULL, "invalid fd");
-			triggered = -1;
-			goto poll_exit;
-		}
-
-		// IN or OUT must match our fd direction
-		if ((fds[i].events & POLLIN) && (poll_fd[_index].rw != RW_READ)) {
-			fds[i].revents |= POLLNVAL | POLLERR;
-			errno = EBADF;
-			usbi_warn(NULL, "attempted POLLIN on fd without READ access");
-			LeaveCriticalSection(&_poll_fd[_index].mutex);
-			triggered = -1;
-			goto poll_exit;
-		}
-
-		if ((fds[i].events & POLLOUT) && (poll_fd[_index].rw != RW_WRITE)) {
-			fds[i].revents |= POLLNVAL | POLLERR;
-			errno = EBADF;
-			usbi_warn(NULL, "attempted POLLOUT on fd without WRITE access");
-			LeaveCriticalSection(&_poll_fd[_index].mutex);
-			triggered = -1;
-			goto poll_exit;
-		}
-
-		// The following macro only works if overlapped I/O was reported pending
-		if ( (HasOverlappedIoCompleted(poll_fd[_index].overlapped))
-		  || (HasOverlappedIoCompletedSync(poll_fd[_index].overlapped)) ) {
-			poll_dbg("  completed");
-			// checks above should ensure this works:
-			fds[i].revents = fds[i].events;
-			triggered++;
-		} else {
-			handles_to_wait_on[nb_handles_to_wait_on] = poll_fd[_index].overlapped->hEvent;
-			handle_to_index[nb_handles_to_wait_on] = i;
-			nb_handles_to_wait_on++;
-		}
-		LeaveCriticalSection(&_poll_fd[_index].mutex);
-	}
-
-	// If nothing was triggered, wait on all fds that require it
-	if ((timeout != 0) && (triggered == 0) && (nb_handles_to_wait_on != 0)) {
-		if (timeout < 0) {
-			poll_dbg("starting infinite wait for %u handles...", (unsigned int)nb_handles_to_wait_on);
-		} else {
-			poll_dbg("starting %d ms wait for %u handles...", timeout, (unsigned int)nb_handles_to_wait_on);
-		}
-		ret = WaitForMultipleObjects(nb_handles_to_wait_on, handles_to_wait_on,
-			FALSE, (timeout<0)?INFINITE:(DWORD)timeout);
-		object_index = ret-WAIT_OBJECT_0;
-		if ((object_index >= 0) && ((DWORD)object_index < nb_handles_to_wait_on)) {
-			poll_dbg("  completed after wait");
-			i = handle_to_index[object_index];
-			_index = _fd_to_index_and_lock(fds[i].fd);
-			fds[i].revents = fds[i].events;
-			triggered++;
-			if (_index >= 0) {
-				LeaveCriticalSection(&_poll_fd[_index].mutex);
-			}
-		} else if (ret == WAIT_TIMEOUT) {
-			poll_dbg("  timed out");
-			triggered = 0;	// 0 = timeout
-		} else {
-			errno = EIO;
-			triggered = -1;	// error
-		}
-	}
-
-poll_exit:
-	if (handles_to_wait_on != NULL) {
-		free(handles_to_wait_on);
-	}
-	if (handle_to_index != NULL) {
-		free(handle_to_index);
-	}
-	return triggered;
-}
-
-/*
- * close a 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 {
-		free_overlapped(poll_fd[_index].overlapped);
-		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;
-	UNUSED(buf);
-
-	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, (unsigned int)GetCurrentThreadId());
-	SetEvent(poll_fd[_index].overlapped->hEvent);
-	poll_fd[_index].overlapped->Internal = STATUS_WAIT_0;
-	// If two threads write on the pipe at the same time, we need to
-	// process two separate reads => use the overlapped as a counter
-	poll_fd[_index].overlapped->InternalHigh++;
-
-	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;
-	UNUSED(buf);
-
-	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: %u", (unsigned int)GetLastError());
-		errno = EIO;
-		goto out;
-	}
-
-	poll_dbg("clr pipe event (fd = %d, thread = %08X)", _index, (unsigned int)GetCurrentThreadId());
-	poll_fd[_index].overlapped->InternalHigh--;
-	// Don't reset unless we don't have any more events to process
-	if (poll_fd[_index].overlapped->InternalHigh <= 0) {
-		ResetEvent(poll_fd[_index].overlapped->hEvent);
-		poll_fd[_index].overlapped->Internal = STATUS_PENDING;
-	}
-
-	r = sizeof(unsigned char);
-
-out:
-	LeaveCriticalSection(&_poll_fd[_index].mutex);
-	return r;
-}

+ 0 - 55
vendor/github.com/karalabe/hid/libusb/libusb/os/threads_posix.h

@@ -1,55 +0,0 @@
-/*
- * libusb synchronization using POSIX Threads
- *
- * Copyright © 2010 Peter Stuge <peter@stuge.se>
- *
- * 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_POSIX_H
-#define LIBUSB_THREADS_POSIX_H
-
-#include <pthread.h>
-#ifdef HAVE_SYS_TIME_H
-#include <sys/time.h>
-#endif
-
-#define usbi_mutex_static_t		pthread_mutex_t
-#define USBI_MUTEX_INITIALIZER		PTHREAD_MUTEX_INITIALIZER
-#define usbi_mutex_static_lock		pthread_mutex_lock
-#define usbi_mutex_static_unlock	pthread_mutex_unlock
-
-#define usbi_mutex_t			pthread_mutex_t
-#define usbi_mutex_init(mutex)		pthread_mutex_init((mutex), NULL)
-#define usbi_mutex_lock			pthread_mutex_lock
-#define usbi_mutex_unlock		pthread_mutex_unlock
-#define usbi_mutex_trylock		pthread_mutex_trylock
-#define usbi_mutex_destroy		pthread_mutex_destroy
-
-#define usbi_cond_t			pthread_cond_t
-#define usbi_cond_init(cond)		pthread_cond_init((cond), NULL)
-#define usbi_cond_wait			pthread_cond_wait
-#define usbi_cond_broadcast		pthread_cond_broadcast
-#define usbi_cond_destroy		pthread_cond_destroy
-
-#define usbi_tls_key_t			pthread_key_t
-#define usbi_tls_key_create(key)	pthread_key_create((key), NULL)
-#define usbi_tls_key_get		pthread_getspecific
-#define usbi_tls_key_set		pthread_setspecific
-#define usbi_tls_key_delete		pthread_key_delete
-
-int usbi_get_tid(void);
-
-#endif /* LIBUSB_THREADS_POSIX_H */

+ 0 - 259
vendor/github.com/karalabe/hid/libusb/libusb/os/threads_windows.c

@@ -1,259 +0,0 @@
-/*
- * libusb synchronization on Microsoft Windows
- *
- * Copyright © 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 "libusbi.h"
-
-struct usbi_cond_perthread {
-	struct list_head list;
-	DWORD tid;
-	HANDLE event;
-};
-
-int usbi_mutex_static_lock(usbi_mutex_static_t *mutex)
-{
-	if (!mutex)
-		return EINVAL;
-	while (InterlockedExchange(mutex, 1) == 1)
-		SleepEx(0, TRUE);
-	return 0;
-}
-
-int usbi_mutex_static_unlock(usbi_mutex_static_t *mutex)
-{
-	if (!mutex)
-		return EINVAL;
-	InterlockedExchange(mutex, 0);
-	return 0;
-}
-
-int usbi_mutex_init(usbi_mutex_t *mutex)
-{
-	if (!mutex)
-		return EINVAL;
-	*mutex = CreateMutex(NULL, FALSE, NULL);
-	if (!*mutex)
-		return ENOMEM;
-	return 0;
-}
-
-int usbi_mutex_lock(usbi_mutex_t *mutex)
-{
-	DWORD result;
-
-	if (!mutex)
-		return EINVAL;
-	result = WaitForSingleObject(*mutex, INFINITE);
-	if (result == WAIT_OBJECT_0 || result == WAIT_ABANDONED)
-		return 0; // acquired (ToDo: check that abandoned is ok)
-	else
-		return 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 EINVAL;
-	if (ReleaseMutex(*mutex))
-		return 0;
-	else
-		return EPERM;
-}
-
-int usbi_mutex_trylock(usbi_mutex_t *mutex)
-{
-	DWORD result;
-
-	if (!mutex)
-		return EINVAL;
-	result = WaitForSingleObject(*mutex, 0);
-	if (result == WAIT_OBJECT_0 || result == WAIT_ABANDONED)
-		return 0; // acquired (ToDo: check that abandoned is ok)
-	else if (result == WAIT_TIMEOUT)
-		return EBUSY;
-	else
-		return EINVAL; // don't know how this would happen
-			       //   so don't know proper error
-}
-
-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 EINVAL;
-	*mutex = NULL;
-	return 0;
-}
-
-int usbi_cond_init(usbi_cond_t *cond)
-{
-	if (!cond)
-		return 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, *next_pos;
-
-	if(!cond)
-		return EINVAL;
-	if (!list_empty(&cond->waiters))
-		return EBUSY; // (!see above!)
-	list_for_each_entry_safe(pos, next_pos, &cond->not_waiting, list, struct usbi_cond_perthread) {
-		CloseHandle(pos->event);
-		list_del(&pos->list);
-		free(pos);
-	}
-	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 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 ? EINVAL : 0;
-}
-
-__inline static int usbi_cond_intwait(usbi_cond_t *cond,
-	usbi_mutex_t *mutex, DWORD timeout_ms)
-{
-	struct usbi_cond_perthread *pos;
-	int r, found = 0;
-	DWORD r2, tid = GetCurrentThreadId();
-
-	if (!cond || !mutex)
-		return EINVAL;
-	list_for_each_entry(pos, &cond->not_waiting, list, struct usbi_cond_perthread) {
-		if(tid == pos->tid) {
-			found = 1;
-			break;
-		}
-	}
-
-	if (!found) {
-		pos = calloc(1, sizeof(struct usbi_cond_perthread));
-		if (!pos)
-			return 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 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_OBJECT_0)
-		return 0;
-	else if (r2 == WAIT_TIMEOUT)
-		return ETIMEDOUT;
-	else
-		return EINVAL;
-}
-// 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 timeval *tv)
-{
-	DWORD millis;
-
-	millis = (DWORD)(tv->tv_sec * 1000) + (tv->tv_usec / 1000);
-	/* round up to next millisecond */
-	if (tv->tv_usec % 1000)
-		millis++;
-	return usbi_cond_intwait(cond, mutex, millis);
-}
-
-int usbi_tls_key_create(usbi_tls_key_t *key)
-{
-	if (!key)
-		return EINVAL;
-	*key = TlsAlloc();
-	if (*key == TLS_OUT_OF_INDEXES)
-		return ENOMEM;
-	else
-		return 0;
-}
-
-void *usbi_tls_key_get(usbi_tls_key_t key)
-{
-	return TlsGetValue(key);
-}
-
-int usbi_tls_key_set(usbi_tls_key_t key, void *value)
-{
-	if (TlsSetValue(key, value))
-		return 0;
-	else
-		return EINVAL;
-}
-
-int usbi_tls_key_delete(usbi_tls_key_t key)
-{
-	if (TlsFree(key))
-		return 0;
-	else
-		return EINVAL;
-}
-
-int usbi_get_tid(void)
-{
-	return (int)GetCurrentThreadId();
-}

+ 0 - 63
vendor/github.com/karalabe/hid/libusb/libusb/os/windows_nt_common.h

@@ -1,63 +0,0 @@
-/*
- * Windows backend common header for libusb 1.0
- *
- * This file brings together header code common between
- * the desktop Windows backends.
- * Copyright © 2012-2013 RealVNC Ltd.
- * Copyright © 2009-2012 Pete Batard <pete@akeo.ie>
- * 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
-
-// Missing from MinGW
-#if !defined(FACILITY_SETUPAPI)
-#define FACILITY_SETUPAPI	15
-#endif
-
-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 libusb_device_descriptor USB_DEVICE_DESCRIPTOR, *PUSB_DEVICE_DESCRIPTOR;
-
-int windows_common_init(struct libusb_context *ctx);
-void windows_common_exit(void);
-
-unsigned long htab_hash(const char *str);
-int windows_clock_gettime(int clk_id, struct timespec *tp);
-
-void windows_clear_transfer_priv(struct usbi_transfer *itransfer);
-int windows_copy_transfer_data(struct usbi_transfer *itransfer, uint32_t io_size);
-struct winfd *windows_get_fd(struct usbi_transfer *transfer);
-void windows_get_overlapped_result(struct usbi_transfer *transfer, struct winfd *pollable_fd, DWORD *io_result, DWORD *io_size);
-
-void windows_handle_callback(struct usbi_transfer *itransfer, uint32_t io_result, uint32_t io_size);
-int windows_handle_events(struct libusb_context *ctx, struct pollfd *fds, POLL_NFDS_TYPE nfds, int num_ready);
-
-#if defined(ENABLE_LOGGING)
-const char *windows_error_str(DWORD error_code);
-#endif

+ 0 - 4290
vendor/github.com/karalabe/hid/libusb/libusb/os/windows_winusb.c

@@ -1,4290 +0,0 @@
-/*
- * windows backend for libusb 1.0
- * Copyright © 2009-2012 Pete Batard <pete@akeo.ie>
- * With contributions from Michael Plante, Orin Eman et al.
- * Parts of this code adapted from libusb-win32-v1 by Stephan Meyer
- * HID Reports IOCTLs inspired from HIDAPI by Alan Ott, Signal 11 Software
- * Hash table functions adapted from glibc, by Ulrich Drepper et al.
- * 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
- */
-
-#include <config.h>
-
-#if !defined(USE_USBDK)
-
-#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>
-#include <winioctl.h>
-
-#include "libusbi.h"
-#include "poll_windows.h"
-#include "windows_winusb.h"
-
-#define HANDLE_VALID(h) (((h) != 0) && ((h) != INVALID_HANDLE_VALUE))
-
-// 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;			\
-	}
-
-// WinUSB-like API prototypes
-static int winusbx_init(int sub_api, struct libusb_context *ctx);
-static int winusbx_exit(int sub_api);
-static int winusbx_open(int sub_api, struct libusb_device_handle *dev_handle);
-static void winusbx_close(int sub_api, struct libusb_device_handle *dev_handle);
-static int winusbx_configure_endpoints(int sub_api, struct libusb_device_handle *dev_handle, int iface);
-static int winusbx_claim_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface);
-static int winusbx_release_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface);
-static int winusbx_submit_control_transfer(int sub_api, struct usbi_transfer *itransfer);
-static int winusbx_set_interface_altsetting(int sub_api, struct libusb_device_handle *dev_handle, int iface, int altsetting);
-static int winusbx_submit_bulk_transfer(int sub_api, struct usbi_transfer *itransfer);
-static int winusbx_clear_halt(int sub_api, struct libusb_device_handle *dev_handle, unsigned char endpoint);
-static int winusbx_abort_transfers(int sub_api, struct usbi_transfer *itransfer);
-static int winusbx_abort_control(int sub_api, struct usbi_transfer *itransfer);
-static int winusbx_reset_device(int sub_api, struct libusb_device_handle *dev_handle);
-static int winusbx_copy_transfer_data(int sub_api, struct usbi_transfer *itransfer, uint32_t io_size);
-// HID API prototypes
-static int hid_init(int sub_api, struct libusb_context *ctx);
-static int hid_exit(int sub_api);
-static int hid_open(int sub_api, struct libusb_device_handle *dev_handle);
-static void hid_close(int sub_api, struct libusb_device_handle *dev_handle);
-static int hid_claim_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface);
-static int hid_release_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface);
-static int hid_set_interface_altsetting(int sub_api, struct libusb_device_handle *dev_handle, int iface, int altsetting);
-static int hid_submit_control_transfer(int sub_api, struct usbi_transfer *itransfer);
-static int hid_submit_bulk_transfer(int sub_api, struct usbi_transfer *itransfer);
-static int hid_clear_halt(int sub_api, struct libusb_device_handle *dev_handle, unsigned char endpoint);
-static int hid_abort_transfers(int sub_api, struct usbi_transfer *itransfer);
-static int hid_reset_device(int sub_api, struct libusb_device_handle *dev_handle);
-static int hid_copy_transfer_data(int sub_api, struct usbi_transfer *itransfer, uint32_t io_size);
-// Composite API prototypes
-static int composite_init(int sub_api, struct libusb_context *ctx);
-static int composite_exit(int sub_api);
-static int composite_open(int sub_api, struct libusb_device_handle *dev_handle);
-static void composite_close(int sub_api, struct libusb_device_handle *dev_handle);
-static int composite_claim_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface);
-static int composite_set_interface_altsetting(int sub_api, struct libusb_device_handle *dev_handle, int iface, int altsetting);
-static int composite_release_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface);
-static int composite_submit_control_transfer(int sub_api, struct usbi_transfer *itransfer);
-static int composite_submit_bulk_transfer(int sub_api, struct usbi_transfer *itransfer);
-static int composite_submit_iso_transfer(int sub_api, struct usbi_transfer *itransfer);
-static int composite_clear_halt(int sub_api, struct libusb_device_handle *dev_handle, unsigned char endpoint);
-static int composite_abort_transfers(int sub_api, struct usbi_transfer *itransfer);
-static int composite_abort_control(int sub_api, struct usbi_transfer *itransfer);
-static int composite_reset_device(int sub_api, struct libusb_device_handle *dev_handle);
-static int composite_copy_transfer_data(int sub_api, struct usbi_transfer *itransfer, uint32_t io_size);
-
-
-// Global variables
-int windows_version = WINDOWS_UNDEFINED;
-static char windows_version_str[128] = "Undefined";
-// Concurrency
-static int concurrent_usage = -1;
-static usbi_mutex_t autoclaim_lock;
-// API globals
-#define CHECK_WINUSBX_AVAILABLE(sub_api)		\
-	do {						\
-		if (sub_api == SUB_API_NOTSET)		\
-			sub_api = priv->sub_api;	\
-		if (!WinUSBX[sub_api].initialized) 	\
-			return LIBUSB_ERROR_ACCESS;	\
-	} while(0)
-
-static HMODULE WinUSBX_handle = NULL;
-static struct winusb_interface WinUSBX[SUB_API_MAX];
-static const char *sub_api_name[SUB_API_MAX] = WINUSBX_DRV_NAMES;
-
-static bool api_hid_available = false;
-#define CHECK_HID_AVAILABLE				\
-	do {						\
-		if (!api_hid_available)			\
-			return LIBUSB_ERROR_ACCESS;	\
-	} while (0)
-
-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 defined(ENABLE_LOGGING)
-static char *guid_to_string(const GUID *guid)
-{
-	static char guid_string[MAX_GUID_STRING_LENGTH];
-
-	if (guid == NULL)
-		return NULL;
-
-	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
-
-/*
- * 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;
-	char *ret_path;
-	size_t add_root = 0;
-
-	if (path == NULL)
-		return NULL;
-
-	size = strlen(path) + 1;
-
-	// Microsoft indiscriminately uses '\\?\', '\\.\', '##?#" or "##.#" for root prefixes.
-	if (!((size > 3) && (((path[0] == '\\') && (path[1] == '\\') && (path[3] == '\\'))
-			|| ((path[0] == '#') && (path[1] == '#') && (path[3] == '#'))))) {
-		add_root = sizeof(root_prefix);
-		size += add_root;
-	}
-
-	ret_path = malloc(size);
-	if (ret_path == NULL)
-		return NULL;
-
-	strcpy(&ret_path[add_root], path);
-
-	// Ensure consistency with root prefix
-	memcpy(ret_path, root_prefix, sizeof(root_prefix));
-
-	// Same goes for '\' and '#' after the root prefix. Ensure '#' is used
-	for (j = sizeof(root_prefix); 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, OLE32 and SetupAPI DLL functions
- */
-static int init_dlls(void)
-{
-	DLL_GET_HANDLE(Cfgmgr32);
-	DLL_LOAD_FUNC(Cfgmgr32, CM_Get_Parent, TRUE);
-	DLL_LOAD_FUNC(Cfgmgr32, CM_Get_Child, TRUE);
-	DLL_LOAD_FUNC(Cfgmgr32, CM_Get_Sibling, TRUE);
-	DLL_LOAD_FUNC(Cfgmgr32, CM_Get_Device_IDA, TRUE);
-
-	// Prefixed to avoid conflict with header files
-	DLL_GET_HANDLE(AdvAPI32);
-	DLL_LOAD_FUNC_PREFIXED(AdvAPI32, p, RegQueryValueExW, TRUE);
-	DLL_LOAD_FUNC_PREFIXED(AdvAPI32, p, RegCloseKey, TRUE);
-
-	DLL_GET_HANDLE(Kernel32);
-	DLL_LOAD_FUNC_PREFIXED(Kernel32, p, IsWow64Process, FALSE);
-
-	DLL_GET_HANDLE(OLE32);
-	DLL_LOAD_FUNC_PREFIXED(OLE32, p, CLSIDFromString, TRUE);
-
-	DLL_GET_HANDLE(SetupAPI);
-	DLL_LOAD_FUNC_PREFIXED(SetupAPI, p, SetupDiGetClassDevsA, TRUE);
-	DLL_LOAD_FUNC_PREFIXED(SetupAPI, p, SetupDiEnumDeviceInfo, TRUE);
-	DLL_LOAD_FUNC_PREFIXED(SetupAPI, p, SetupDiEnumDeviceInterfaces, TRUE);
-	DLL_LOAD_FUNC_PREFIXED(SetupAPI, p, SetupDiGetDeviceInterfaceDetailA, TRUE);
-	DLL_LOAD_FUNC_PREFIXED(SetupAPI, p, SetupDiDestroyDeviceInfoList, TRUE);
-	DLL_LOAD_FUNC_PREFIXED(SetupAPI, p, SetupDiOpenDevRegKey, TRUE);
-	DLL_LOAD_FUNC_PREFIXED(SetupAPI, p, SetupDiGetDeviceRegistryPropertyA, TRUE);
-	DLL_LOAD_FUNC_PREFIXED(SetupAPI, p, SetupDiOpenDeviceInterfaceRegKey, TRUE);
-
-	return LIBUSB_SUCCESS;
-}
-
-static void exit_dlls(void)
-{
-	DLL_FREE_HANDLE(Cfgmgr32);
-	DLL_FREE_HANDLE(AdvAPI32);
-	DLL_FREE_HANDLE(Kernel32);
-	DLL_FREE_HANDLE(OLE32);
-	DLL_FREE_HANDLE(SetupAPI);
-}
-
-/*
- * enumerate interfaces for the whole USB class
- *
- * 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)
- * usb_class: the generic USB class 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.
- */
-static bool get_devinfo_data(struct libusb_context *ctx,
-	HDEVINFO *dev_info, SP_DEVINFO_DATA *dev_info_data, const char *usb_class, unsigned _index)
-{
-	if (_index <= 0) {
-		*dev_info = pSetupDiGetClassDevsA(NULL, usb_class, NULL, DIGCF_PRESENT|DIGCF_ALLCLASSES);
-		if (*dev_info == INVALID_HANDLE_VALUE)
-			return false;
-	}
-
-	dev_info_data->cbSize = sizeof(SP_DEVINFO_DATA);
-	if (!pSetupDiEnumDeviceInfo(*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));
-
-		pSetupDiDestroyDeviceInfoList(*dev_info);
-		*dev_info = INVALID_HANDLE_VALUE;
-		return false;
-	}
-	return true;
-}
-
-/*
- * 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.
- */
-static SP_DEVICE_INTERFACE_DETAIL_DATA_A *get_interface_details(struct libusb_context *ctx,
-	HDEVINFO *dev_info, SP_DEVINFO_DATA *dev_info_data, const GUID *guid, unsigned _index)
-{
-	SP_DEVICE_INTERFACE_DATA dev_interface_data;
-	SP_DEVICE_INTERFACE_DETAIL_DATA_A *dev_interface_details;
-	DWORD size;
-
-	if (_index <= 0)
-		*dev_info = pSetupDiGetClassDevsA(guid, NULL, NULL, DIGCF_PRESENT|DIGCF_DEVICEINTERFACE);
-
-	if (dev_info_data != NULL) {
-		dev_info_data->cbSize = sizeof(SP_DEVINFO_DATA);
-		if (!pSetupDiEnumDeviceInfo(*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));
-
-			pSetupDiDestroyDeviceInfoList(*dev_info);
-			*dev_info = INVALID_HANDLE_VALUE;
-			return NULL;
-		}
-	}
-
-	dev_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
-	if (!pSetupDiEnumDeviceInterfaces(*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));
-
-		pSetupDiDestroyDeviceInfoList(*dev_info);
-		*dev_info = INVALID_HANDLE_VALUE;
-		return NULL;
-	}
-
-	// Read interface data (dummy + actual) to access the device path
-	if (!pSetupDiGetDeviceInterfaceDetailA(*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;
-	}
-
-	dev_interface_details = calloc(1, size);
-	if (dev_interface_details == 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_A);
-	if (!pSetupDiGetDeviceInterfaceDetailA(*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:
-	pSetupDiDestroyDeviceInfoList(*dev_info);
-	*dev_info = INVALID_HANDLE_VALUE;
-	return NULL;
-}
-
-/* For libusb0 filter */
-static SP_DEVICE_INTERFACE_DETAIL_DATA_A *get_interface_details_filter(struct libusb_context *ctx,
-	HDEVINFO *dev_info, SP_DEVINFO_DATA *dev_info_data, const GUID *guid, unsigned _index, char *filter_path)
-{
-	SP_DEVICE_INTERFACE_DATA dev_interface_data;
-	SP_DEVICE_INTERFACE_DETAIL_DATA_A *dev_interface_details;
-	DWORD size;
-
-	if (_index <= 0)
-		*dev_info = pSetupDiGetClassDevsA(guid, NULL, NULL, DIGCF_PRESENT|DIGCF_DEVICEINTERFACE);
-
-	if (dev_info_data != NULL) {
-		dev_info_data->cbSize = sizeof(SP_DEVINFO_DATA);
-		if (!pSetupDiEnumDeviceInfo(*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));
-
-			pSetupDiDestroyDeviceInfoList(*dev_info);
-			*dev_info = INVALID_HANDLE_VALUE;
-			return NULL;
-		}
-	}
-
-	dev_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
-	if (!pSetupDiEnumDeviceInterfaces(*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));
-
-		pSetupDiDestroyDeviceInfoList(*dev_info);
-		*dev_info = INVALID_HANDLE_VALUE;
-		return NULL;
-	}
-
-	// Read interface data (dummy + actual) to access the device path
-	if (!pSetupDiGetDeviceInterfaceDetailA(*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;
-	}
-
-	dev_interface_details = calloc(1, size);
-	if (dev_interface_details == 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_A);
-	if (!pSetupDiGetDeviceInterfaceDetailA(*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));
-
-	// [trobinso] lookup the libusb0 symbolic index.
-	if (dev_interface_details) {
-		HKEY hkey_device_interface = pSetupDiOpenDeviceInterfaceRegKey(*dev_info, &dev_interface_data, 0, KEY_READ);
-		if (hkey_device_interface != INVALID_HANDLE_VALUE) {
-			DWORD libusb0_symboliclink_index = 0;
-			DWORD value_length = sizeof(DWORD);
-			DWORD value_type = 0;
-			LONG status;
-
-			status = pRegQueryValueExW(hkey_device_interface, L"LUsb0", NULL, &value_type,
-				(LPBYTE)&libusb0_symboliclink_index, &value_length);
-			if (status == ERROR_SUCCESS) {
-				if (libusb0_symboliclink_index < 256) {
-					// libusb0.sys is connected to this device instance.
-					// If the the device interface guid is {F9F3FF14-AE21-48A0-8A25-8011A7A931D9} then it's a filter.
-					sprintf(filter_path, "\\\\.\\libusb0-%04u", (unsigned int)libusb0_symboliclink_index);
-					usbi_dbg("assigned libusb0 symbolic link %s", filter_path);
-				} else {
-					// libusb0.sys was connected to this device instance at one time; but not anymore.
-				}
-			}
-			pRegCloseKey(hkey_device_interface);
-		}
-	}
-
-	return dev_interface_details;
-
-err_exit:
-	pSetupDiDestroyDeviceInfoList(*dev_info);
-	*dev_info = INVALID_HANDLE_VALUE;
-	return NULL;
-}
-
-/*
- * Returns the session ID of a device's nth level ancestor
- * If there's no device at the nth level, return 0
- */
-static unsigned long get_ancestor_session_id(DWORD devinst, unsigned level)
-{
-	DWORD parent_devinst;
-	unsigned long session_id;
-	char *sanitized_path;
-	char path[MAX_PATH_LENGTH];
-	unsigned i;
-
-	if (level < 1)
-		return 0;
-
-	for (i = 0; i < level; i++) {
-		if (CM_Get_Parent(&parent_devinst, devinst, 0) != CR_SUCCESS)
-			return 0;
-		devinst = parent_devinst;
-	}
-
-	if (CM_Get_Device_IDA(devinst, path, MAX_PATH_LENGTH, 0) != CR_SUCCESS)
-		return 0;
-
-	// TODO: (post hotplug): try without sanitizing
-	sanitized_path = sanitize_path(path);
-	if (sanitized_path == NULL)
-		return 0;
-
-	session_id = htab_hash(sanitized_path);
-	free(sanitized_path);
-	return session_id;
-}
-
-/*
- * Determine which interface the given endpoint address belongs to
- */
-static int get_interface_by_endpoint(struct libusb_config_descriptor *conf_desc, uint8_t ep)
-{
-	const struct libusb_interface *intf;
-	const struct libusb_interface_descriptor *intf_desc;
-	int i, j, k;
-
-	for (i = 0; i < conf_desc->bNumInterfaces; i++) {
-		intf = &conf_desc->interface[i];
-		for (j = 0; j < intf->num_altsetting; j++) {
-			intf_desc = &intf->altsetting[j];
-			for (k = 0; k < intf_desc->bNumEndpoints; k++) {
-				if (intf_desc->endpoint[k].bEndpointAddress == ep) {
-					usbi_dbg("found endpoint %02X on interface %d", intf_desc->bInterfaceNumber, i);
-					return intf_desc->bInterfaceNumber;
-				}
-			}
-		}
-	}
-
-	usbi_dbg("endpoint %02X not found on any interface", ep);
-	return LIBUSB_ERROR_NOT_FOUND;
-}
-
-/*
- * Populate the endpoints addresses of the device_priv interface helper structs
- */
-static int windows_assign_endpoints(struct libusb_device_handle *dev_handle, int iface, int altsetting)
-{
-	int i, r;
-	struct windows_device_priv *priv = _device_priv(dev_handle->dev);
-	struct libusb_config_descriptor *conf_desc;
-	const struct libusb_interface_descriptor *if_desc;
-	struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev);
-
-	r = libusb_get_active_config_descriptor(dev_handle->dev, &conf_desc);
-	if (r != LIBUSB_SUCCESS) {
-		usbi_warn(ctx, "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);
-		libusb_free_config_descriptor(conf_desc);
-		return LIBUSB_SUCCESS;
-	}
-
-	priv->usb_interface[iface].endpoint = malloc(if_desc->bNumEndpoints);
-	if (priv->usb_interface[iface].endpoint == NULL) {
-		libusb_free_config_descriptor(conf_desc);
-		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);
-
-	// Extra init may be required to configure endpoints
-	return priv->apib->configure_endpoints(SUB_API_NOTSET, dev_handle, iface);
-}
-
-// Lookup for a match in the list of API driver names
-// return -1 if not found, driver match number otherwise
-static int get_sub_api(char *driver, int api)
-{
-	int i;
-	const char sep_str[2] = {LIST_SEPARATOR, 0};
-	char *tok, *tmp_str;
-	size_t len = strlen(driver);
-
-	if (len == 0)
-		return SUB_API_NOTSET;
-
-	tmp_str = _strdup(driver);
-	if (tmp_str == NULL)
-		return SUB_API_NOTSET;
-
-	tok = strtok(tmp_str, sep_str);
-	while (tok != NULL) {
-		for (i = 0; i < usb_api_backend[api].nb_driver_names; i++) {
-			if (_stricmp(tok, usb_api_backend[api].driver_name_list[i]) == 0) {
-				free(tmp_str);
-				return i;
-			}
-		}
-		tok = strtok(NULL, sep_str);
-	}
-
-	free(tmp_str);
-	return SUB_API_NOTSET;
-}
-
-/*
- * auto-claiming and auto-release helper functions
- */
-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_WINUSBX:
-	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->id == 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_error_name((enum libusb_error)r));
-		}
-	}
-	usbi_mutex_unlock(&autoclaim_lock);
-}
-
-/* Windows version dtection */
-static BOOL is_x64(void)
-{
-	BOOL ret = FALSE;
-
-	// Detect if we're running a 32 or 64 bit system
-	if (sizeof(uintptr_t) < 8) {
-		if (pIsWow64Process != NULL)
-			pIsWow64Process(GetCurrentProcess(), &ret);
-	} else {
-		ret = TRUE;
-	}
-
-	return ret;
-}
-
-static void get_windows_version(void)
-{
-	OSVERSIONINFOEXA vi, vi2;
-	const char *arch, *w = NULL;
-	unsigned major, minor;
-	ULONGLONG major_equal, minor_equal;
-	BOOL ws;
-
-	memset(&vi, 0, sizeof(vi));
-	vi.dwOSVersionInfoSize = sizeof(vi);
-	if (!GetVersionExA((OSVERSIONINFOA *)&vi)) {
-		memset(&vi, 0, sizeof(vi));
-		vi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
-		if (!GetVersionExA((OSVERSIONINFOA *)&vi))
-			return;
-	}
-
-	if (vi.dwPlatformId == VER_PLATFORM_WIN32_NT) {
-		if (vi.dwMajorVersion > 6 || (vi.dwMajorVersion == 6 && vi.dwMinorVersion >= 2)) {
-			// Starting with Windows 8.1 Preview, GetVersionEx() does no longer report the actual OS version
-			// See: http://msdn.microsoft.com/en-us/library/windows/desktop/dn302074.aspx
-
-			major_equal = VerSetConditionMask(0, VER_MAJORVERSION, VER_EQUAL);
-			for (major = vi.dwMajorVersion; major <= 9; major++) {
-				memset(&vi2, 0, sizeof(vi2));
-				vi2.dwOSVersionInfoSize = sizeof(vi2);
-				vi2.dwMajorVersion = major;
-				if (!VerifyVersionInfoA(&vi2, VER_MAJORVERSION, major_equal))
-					continue;
-
-				if (vi.dwMajorVersion < major) {
-					vi.dwMajorVersion = major;
-					vi.dwMinorVersion = 0;
-				}
-
-				minor_equal = VerSetConditionMask(0, VER_MINORVERSION, VER_EQUAL);
-				for (minor = vi.dwMinorVersion; minor <= 9; minor++) {
-					memset(&vi2, 0, sizeof(vi2));
-					vi2.dwOSVersionInfoSize = sizeof(vi2);
-					vi2.dwMinorVersion = minor;
-					if (!VerifyVersionInfoA(&vi2, VER_MINORVERSION, minor_equal))
-						continue;
-
-					vi.dwMinorVersion = minor;
-					break;
-				}
-
-				break;
-			}
-		}
-
-		if (vi.dwMajorVersion <= 0xf && vi.dwMinorVersion <= 0xf) {
-			ws = (vi.wProductType <= VER_NT_WORKSTATION);
-			windows_version = vi.dwMajorVersion << 4 | vi.dwMinorVersion;
-			switch (windows_version) {
-			case 0x50: w = "2000"; break;
-			case 0x51: w = "XP"; break;
-			case 0x52: w = "2003"; break;
-			case 0x60: w = (ws ? "Vista" : "2008"); break;
-			case 0x61: w = (ws ? "7" : "2008_R2"); break;
-			case 0x62: w = (ws ? "8" : "2012"); break;
-			case 0x63: w = (ws ? "8.1" : "2012_R2"); break;
-			case 0x64: w = (ws ? "10" : "2015"); break;
-			default:
-				if (windows_version < 0x50)
-					windows_version = WINDOWS_UNSUPPORTED;
-				else
-					w = "11 or later";
-				break;
-			}
-		}
-	}
-
-	arch = is_x64() ? "64-bit" : "32-bit";
-
-	if (w == NULL)
-		snprintf(windows_version_str, sizeof(windows_version_str), "%s %u.%u %s",
-			(vi.dwPlatformId == VER_PLATFORM_WIN32_NT ? "NT" : "??"),
-			(unsigned int)vi.dwMajorVersion, (unsigned int)vi.dwMinorVersion, arch);
-	else if (vi.wServicePackMinor)
-		snprintf(windows_version_str, sizeof(windows_version_str), "%s SP%u.%u %s",
-			w, vi.wServicePackMajor, vi.wServicePackMinor, arch);
-	else if (vi.wServicePackMajor)
-		snprintf(windows_version_str, sizeof(windows_version_str), "%s SP%u %s",
-			w, vi.wServicePackMajor, arch);
-	else
-		snprintf(windows_version_str, sizeof(windows_version_str), "%s %s",
-			w, arch);
-}
-
-/*
- * 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)
-{
-	int i, r = LIBUSB_ERROR_OTHER;
-	HANDLE semaphore;
-	char sem_name[11 + 8 + 1]; // strlen("libusb_init") + (32-bit hex PID) + '\0'
-
-	sprintf(sem_name, "libusb_init%08X", (unsigned int)(GetCurrentProcessId() & 0xFFFFFFFF));
-	semaphore = CreateSemaphoreA(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?
-		get_windows_version();
-		usbi_dbg("Windows %s", windows_version_str);
-
-		if (windows_version == WINDOWS_UNSUPPORTED) {
-			usbi_err(ctx, "This version of Windows is NOT supported");
-			r = LIBUSB_ERROR_NOT_SUPPORTED;
-			goto init_exit;
-		}
-
-		// We need a lock for proper auto-release
-		usbi_mutex_init(&autoclaim_lock);
-
-		// Initialize pollable file descriptors
-		init_polling();
-
-		// Load DLL imports
-		if (init_dlls() != LIBUSB_SUCCESS) {
-			usbi_err(ctx, "could not resolve DLL functions");
-			goto init_exit;
-		}
-
-		// 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(SUB_API_NOTSET, ctx);
-
-		r = windows_common_init(ctx);
-		if (r)
-			goto init_exit;
-	}
-	// At this stage, either we went through full init successfully, or didn't need to
-	r = LIBUSB_SUCCESS;
-
-init_exit: // Holds semaphore here.
-	if (!concurrent_usage && r != LIBUSB_SUCCESS) { // First init failed?
-		for (i = 0; i < USB_API_MAX; i++)
-			usb_api_backend[i].exit(SUB_API_NOTSET);
-		exit_dlls();
-		exit_polling();
-		windows_common_exit();
-		usbi_mutex_destroy(&autoclaim_lock);
-	}
-
-	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;
-}
-
-/*
- * HCD (root) hubs need to have their device descriptor manually populated
- *
- * Note that, like Microsoft does in the device manager, we populate the
- * Vendor and Device ID for HCD hubs with the ones from the PCI HCD device.
- */
-static int force_hcd_device_descriptor(struct libusb_device *dev)
-{
-	struct windows_device_priv *parent_priv, *priv = _device_priv(dev);
-	struct libusb_context *ctx = DEVICE_CTX(dev);
-	int vid, pid;
-
-	dev->num_configurations = 1;
-	priv->dev_descriptor.bLength = sizeof(USB_DEVICE_DESCRIPTOR);
-	priv->dev_descriptor.bDescriptorType = USB_DEVICE_DESCRIPTOR_TYPE;
-	priv->dev_descriptor.bNumConfigurations = 1;
-	priv->active_config = 1;
-
-	if (dev->parent_dev == NULL) {
-		usbi_err(ctx, "program assertion failed - HCD hub has no parent");
-		return LIBUSB_ERROR_NO_DEVICE;
-	}
-
-	parent_priv = _device_priv(dev->parent_dev);
-	if (sscanf(parent_priv->path, "\\\\.\\PCI#VEN_%04x&DEV_%04x%*s", &vid, &pid) == 2) {
-		priv->dev_descriptor.idVendor = (uint16_t)vid;
-		priv->dev_descriptor.idProduct = (uint16_t)pid;
-	} else {
-		usbi_warn(ctx, "could not infer VID/PID of HCD hub from '%s'", parent_priv->path);
-		priv->dev_descriptor.idVendor = 0x1d6b; // Linux Foundation root hub
-		priv->dev_descriptor.idProduct = 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, char *device_id)
-{
-	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 = calloc(dev->num_configurations, sizeof(unsigned char *));
-	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 = (ULONG)priv->port;
-		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 = 0;
-		cd_buf_short.req.SetupPacket.wLength = (USHORT)(size - sizeof(USB_DESCRIPTOR_REQUEST));
-
-		// Dummy call to get the required data size. Initial failures are reported as info rather
-		// than error as they can occur for non-penalizing situations, such as with some hubs.
-		// coverity[tainted_data_argument]
-		if (!DeviceIoControl(hub_handle, IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, &cd_buf_short, size,
-			&cd_buf_short, size, &ret_size, NULL)) {
-			usbi_info(ctx, "could not access configuration descriptor (dummy) for '%s': %s", device_id, windows_error_str(0));
-			LOOP_BREAK(LIBUSB_ERROR_IO);
-		}
-
-		if ((ret_size != size) || (cd_buf_short.data.wTotalLength < sizeof(USB_CONFIGURATION_DESCRIPTOR))) {
-			usbi_info(ctx, "unexpected configuration descriptor size (dummy) for '%s'.", device_id);
-			LOOP_BREAK(LIBUSB_ERROR_IO);
-		}
-
-		size = sizeof(USB_DESCRIPTOR_REQUEST) + cd_buf_short.data.wTotalLength;
-		cd_buf_actual = calloc(1, size);
-		if (cd_buf_actual == NULL) {
-			usbi_err(ctx, "could not allocate configuration descriptor buffer for '%s'.", device_id);
-			LOOP_BREAK(LIBUSB_ERROR_NO_MEM);
-		}
-
-		// Actual call
-		cd_buf_actual->ConnectionIndex = (ULONG)priv->port;
-		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 = 0;
-		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) for '%s': %s", device_id, 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) for '%s'.", device_id);
-			LOOP_BREAK(LIBUSB_ERROR_IO);
-		}
-
-		if (cd_data->bDescriptorType != USB_CONFIGURATION_DESCRIPTOR_TYPE) {
-			usbi_err(ctx, "not a configuration descriptor for '%s'", device_id);
-			LOOP_BREAK(LIBUSB_ERROR_IO);
-		}
-
-		usbi_dbg("cached config descriptor %d (bConfigurationValue=%u, %u bytes)",
-			i, cd_data->bConfigurationValue, cd_data->wTotalLength);
-
-		// Cache the descriptor
-		priv->config_descriptor[i] = malloc(cd_data->wTotalLength);
-		if (priv->config_descriptor[i] == NULL)
-			LOOP_BREAK(LIBUSB_ERROR_NO_MEM);
-		memcpy(priv->config_descriptor[i], cd_data, cd_data->wTotalLength);
-	}
-	return LIBUSB_SUCCESS;
-}
-
-/*
- * Populate a libusb device structure
- */
-static int init_device(struct libusb_device *dev, struct libusb_device *parent_dev,
-	uint8_t port_number, char *device_id, DWORD devinst)
-{
-	HANDLE handle;
-	DWORD size;
-	USB_NODE_CONNECTION_INFORMATION_EX conn_info;
-	USB_NODE_CONNECTION_INFORMATION_EX_V2 conn_info_v2;
-	struct windows_device_priv *priv, *parent_priv;
-	struct libusb_context *ctx;
-	struct libusb_device *tmp_dev;
-	unsigned long tmp_id;
-	unsigned i;
-
-	if ((dev == NULL) || (parent_dev == NULL))
-		return LIBUSB_ERROR_NOT_FOUND;
-
-	ctx = DEVICE_CTX(dev);
-	priv = _device_priv(dev);
-	parent_priv = _device_priv(parent_dev);
-	if (parent_priv->apib->id != USB_API_HUB) {
-		usbi_warn(ctx, "parent for device '%s' is not a hub", device_id);
-		return LIBUSB_ERROR_NOT_FOUND;
-	}
-
-	// It is possible for the parent hub not to have been initialized yet
-	// If that's the case, lookup the ancestors to set the bus number
-	if (parent_dev->bus_number == 0) {
-		for (i = 2; ; i++) {
-			tmp_id = get_ancestor_session_id(devinst, i);
-			if (tmp_id == 0)
-				break;
-
-			tmp_dev = usbi_get_device_by_session_id(ctx, tmp_id);
-			if (tmp_dev == NULL)
-				continue;
-
-			if (tmp_dev->bus_number != 0) {
-				usbi_dbg("got bus number from ancestor #%u", i);
-				parent_dev->bus_number = tmp_dev->bus_number;
-				libusb_unref_device(tmp_dev);
-				break;
-			}
-
-			libusb_unref_device(tmp_dev);
-		}
-	}
-
-	if (parent_dev->bus_number == 0) {
-		usbi_err(ctx, "program assertion failed: unable to find ancestor bus number for '%s'", device_id);
-		return LIBUSB_ERROR_NOT_FOUND;
-	}
-
-	dev->bus_number = parent_dev->bus_number;
-	priv->port = port_number;
-	dev->port_number = port_number;
-	priv->depth = parent_priv->depth + 1;
-	dev->parent_dev = parent_dev;
-
-	// If the device address is already set, we can stop here
-	if (dev->device_address != 0)
-		return LIBUSB_SUCCESS;
-
-	memset(&conn_info, 0, sizeof(conn_info));
-	if (priv->depth != 0) { // Not a HCD hub
-		handle = CreateFileA(parent_priv->path, 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", parent_priv->path, windows_error_str(0));
-			return LIBUSB_ERROR_ACCESS;
-		}
-
-		size = sizeof(conn_info);
-		conn_info.ConnectionIndex = (ULONG)port_number;
-		// coverity[tainted_data_argument]
-		if (!DeviceIoControl(handle, IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX, &conn_info, size,
-			&conn_info, size, &size, NULL)) {
-			usbi_warn(ctx, "could not get node connection information for device '%s': %s",
-				device_id, windows_error_str(0));
-			CloseHandle(handle);
-			return LIBUSB_ERROR_NO_DEVICE;
-		}
-
-		if (conn_info.ConnectionStatus == NoDeviceConnected) {
-			usbi_err(ctx, "device '%s' is no longer connected!", device_id);
-			CloseHandle(handle);
-			return LIBUSB_ERROR_NO_DEVICE;
-		}
-
-		memcpy(&priv->dev_descriptor, &(conn_info.DeviceDescriptor), sizeof(USB_DEVICE_DESCRIPTOR));
-		dev->num_configurations = priv->dev_descriptor.bNumConfigurations;
-		priv->active_config = conn_info.CurrentConfigurationValue;
-		usbi_dbg("found %u configurations (active conf: %u)", dev->num_configurations, priv->active_config);
-
-		// If we can't read the config descriptors, just set the number of confs to zero
-		if (cache_config_descriptors(dev, handle, device_id) != LIBUSB_SUCCESS) {
-			dev->num_configurations = 0;
-			priv->dev_descriptor.bNumConfigurations = 0;
-		}
-
-		// In their great wisdom, Microsoft decided to BREAK the USB speed report between Windows 7 and Windows 8
-		if (windows_version >= WINDOWS_8) {
-			memset(&conn_info_v2, 0, sizeof(conn_info_v2));
-			size = sizeof(conn_info_v2);
-			conn_info_v2.ConnectionIndex = (ULONG)port_number;
-			conn_info_v2.Length = size;
-			conn_info_v2.SupportedUsbProtocols.Usb300 = 1;
-			if (!DeviceIoControl(handle, IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX_V2,
-				&conn_info_v2, size, &conn_info_v2, size, &size, NULL)) {
-				usbi_warn(ctx, "could not get node connection information (V2) for device '%s': %s",
-					device_id,  windows_error_str(0));
-			} else if (conn_info_v2.Flags.DeviceIsOperatingAtSuperSpeedOrHigher) {
-				conn_info.Speed = 3;
-			}
-		}
-
-		CloseHandle(handle);
-
-		if (conn_info.DeviceAddress > UINT8_MAX)
-			usbi_err(ctx, "program assertion failed: device address overflow");
-
-		dev->device_address = (uint8_t)conn_info.DeviceAddress + 1;
-		if (dev->device_address == 1)
-			usbi_err(ctx, "program assertion failed: device address collision with root hub");
-
-		switch (conn_info.Speed) {
-		case 0: dev->speed = LIBUSB_SPEED_LOW; break;
-		case 1: dev->speed = LIBUSB_SPEED_FULL; break;
-		case 2: dev->speed = LIBUSB_SPEED_HIGH; break;
-		case 3: dev->speed = LIBUSB_SPEED_SUPER; break;
-		default:
-			usbi_warn(ctx, "Got unknown device speed %u", conn_info.Speed);
-			break;
-		}
-	} else {
-		dev->device_address = 1; // root hubs are set to use device number 1
-		force_hcd_device_descriptor(dev);
-	}
-
-	usbi_sanitize_device(dev);
-
-	usbi_dbg("(bus: %u, addr: %u, depth: %u, port: %u): '%s'",
-		dev->bus_number, dev->device_address, priv->depth, priv->port, device_id);
-
-	return LIBUSB_SUCCESS;
-}
-
-// Returns the api type, or 0 if not found/unsupported
-static void get_api_type(struct libusb_context *ctx, HDEVINFO *dev_info,
-	SP_DEVINFO_DATA *dev_info_data, int *api, int *sub_api)
-{
-	// Precedence for filter drivers vs driver is in the order of this array
-	struct driver_lookup lookup[3] = {
-		{"\0\0", SPDRP_SERVICE, "driver"},
-		{"\0\0", SPDRP_UPPERFILTERS, "upper filter driver"},
-		{"\0\0", SPDRP_LOWERFILTERS, "lower filter driver"}
-	};
-	DWORD size, reg_type;
-	unsigned k, l;
-	int i, j;
-
-	*api = USB_API_UNSUPPORTED;
-	*sub_api = SUB_API_NOTSET;
-
-	// Check the service & filter names to know the API we should use
-	for (k = 0; k < 3; k++) {
-		if (pSetupDiGetDeviceRegistryPropertyA(*dev_info, dev_info_data, lookup[k].reg_prop,
-			&reg_type, (BYTE *)lookup[k].list, MAX_KEY_LENGTH, &size)) {
-			// Turn the REG_SZ SPDRP_SERVICE into REG_MULTI_SZ
-			if (lookup[k].reg_prop == SPDRP_SERVICE)
-				// our buffers are MAX_KEY_LENGTH + 1 so we can overflow if needed
-				lookup[k].list[strlen(lookup[k].list) + 1] = 0;
-
-			// MULTI_SZ is a pain to work with. Turn it into something much more manageable
-			// NB: none of the driver names we check against contain LIST_SEPARATOR,
-			// (currently ';'), so even if an unsuported one does, it's not an issue
-			for (l = 0; (lookup[k].list[l] != 0) || (lookup[k].list[l + 1] != 0); l++) {
-				if (lookup[k].list[l] == 0)
-					lookup[k].list[l] = LIST_SEPARATOR;
-			}
-			usbi_dbg("%s(s): %s", lookup[k].designation, lookup[k].list);
-		} else {
-			if (GetLastError() != ERROR_INVALID_DATA)
-				usbi_dbg("could not access %s: %s", lookup[k].designation, windows_error_str(0));
-			lookup[k].list[0] = 0;
-		}
-	}
-
-	for (i = 1; i < USB_API_MAX; i++) {
-		for (k = 0; k < 3; k++) {
-			j = get_sub_api(lookup[k].list, i);
-			if (j >= 0) {
-				usbi_dbg("matched %s name against %s", lookup[k].designation,
-					(i != USB_API_WINUSBX) ? usb_api_backend[i].designation : sub_api_name[j]);
-				*api = i;
-				*sub_api = j;
-				return;
-			}
-		}
-	}
-}
-
-static int set_composite_interface(struct libusb_context *ctx, struct libusb_device *dev,
-	char *dev_interface_path, char *device_id, int api, int sub_api)
-{
-	unsigned i;
-	struct windows_device_priv *priv = _device_priv(dev);
-	int interface_number;
-
-	if (priv->apib->id != USB_API_COMPOSITE) {
-		usbi_err(ctx, "program assertion failed: '%s' is not composite", device_id);
-		return LIBUSB_ERROR_NO_DEVICE;
-	}
-
-	// Because MI_## are not necessarily in sequential order (some composite
-	// devices will have only MI_00 & MI_03 for instance), we retrieve the actual
-	// interface number from the path's MI value
-	interface_number = 0;
-	for (i = 0; device_id[i] != 0; ) {
-		if ((device_id[i++] == 'M') && (device_id[i++] == 'I')
-				&& (device_id[i++] == '_')) {
-			interface_number = (device_id[i++] - '0') * 10;
-			interface_number += device_id[i] - '0';
-			break;
-		}
-	}
-
-	if (device_id[i] == 0)
-		usbi_warn(ctx, "failure to read interface number for %s. Using default value %d",
-			device_id, interface_number);
-
-	if (priv->usb_interface[interface_number].path != NULL) {
-		if (api == USB_API_HID) {
-			// HID devices can have multiple collections (COL##) for each MI_## interface
-			usbi_dbg("interface[%d] already set - ignoring HID collection: %s",
-				interface_number, device_id);
-			return LIBUSB_ERROR_ACCESS;
-		}
-		// In other cases, just use the latest data
-		safe_free(priv->usb_interface[interface_number].path);
-	}
-
-	usbi_dbg("interface[%d] = %s", interface_number, dev_interface_path);
-	priv->usb_interface[interface_number].path = dev_interface_path;
-	priv->usb_interface[interface_number].apib = &usb_api_backend[api];
-	priv->usb_interface[interface_number].sub_api = sub_api;
-	if ((api == USB_API_HID) && (priv->hid == NULL)) {
-		priv->hid = calloc(1, sizeof(struct hid_device_priv));
-		if (priv->hid == NULL)
-			return LIBUSB_ERROR_NO_MEM;
-	}
-
-	return LIBUSB_SUCCESS;
-}
-
-static int set_hid_interface(struct libusb_context *ctx, struct libusb_device *dev,
-	char *dev_interface_path)
-{
-	int i;
-	struct windows_device_priv *priv = _device_priv(dev);
-
-	if (priv->hid == NULL) {
-		usbi_err(ctx, "program assertion failed: parent is not HID");
-		return LIBUSB_ERROR_NO_DEVICE;
-	} else if (priv->hid->nb_interfaces == USB_MAXINTERFACES) {
-		usbi_err(ctx, "program assertion failed: max USB interfaces reached for HID device");
-		return LIBUSB_ERROR_NO_DEVICE;
-	}
-
-	for (i = 0; i < priv->hid->nb_interfaces; i++) {
-		if ((priv->usb_interface[i].path != NULL) && strcmp(priv->usb_interface[i].path, dev_interface_path) == 0) {
-			usbi_dbg("interface[%d] already set to %s", i, dev_interface_path);
-			return LIBUSB_ERROR_ACCESS;
-		}
-	}
-
-	priv->usb_interface[priv->hid->nb_interfaces].path = dev_interface_path;
-	priv->usb_interface[priv->hid->nb_interfaces].apib = &usb_api_backend[USB_API_HID];
-	usbi_dbg("interface[%u] = %s", priv->hid->nb_interfaces, dev_interface_path);
-	priv->hid->nb_interfaces++;
-	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 discovered_devs *discdevs;
-	HDEVINFO dev_info = { 0 };
-	const char *usb_class[] = {"USB", "NUSB3", "IUSB3", "IARUSB3"};
-	SP_DEVINFO_DATA dev_info_data = { 0 };
-	SP_DEVICE_INTERFACE_DETAIL_DATA_A *dev_interface_details = NULL;
-	GUID hid_guid;
-#define MAX_ENUM_GUIDS 64
-	const GUID *guid[MAX_ENUM_GUIDS];
-#define HCD_PASS 0
-#define HUB_PASS 1
-#define GEN_PASS 2
-#define DEV_PASS 3
-#define HID_PASS 4
-	int r = LIBUSB_SUCCESS;
-	int api, sub_api;
-	size_t class_index = 0;
-	unsigned int nb_guids, pass, i, j, ancestor;
-	char path[MAX_PATH_LENGTH];
-	char strbuf[MAX_PATH_LENGTH];
-	struct libusb_device *dev, *parent_dev;
-	struct windows_device_priv *priv, *parent_priv;
-	char *dev_interface_path = NULL;
-	char *dev_id_path = NULL;
-	unsigned long session_id;
-	DWORD size, reg_type, port_nr, install_state;
-	HKEY key;
-	WCHAR guid_string_w[MAX_GUID_STRING_LENGTH];
-	GUID *if_guid;
-	LONG s;
-	// Keep a list of newly allocated devs to unref
-	libusb_device **unref_list, **new_unref_list;
-	unsigned int unref_size = 64;
-	unsigned int unref_cur = 0;
-
-	// PASS 1 : (re)enumerate HCDs (allows for HCD hotplug)
-	// PASS 2 : (re)enumerate HUBS
-	// PASS 3 : (re)enumerate generic USB devices (including driverless)
-	//           and list additional USB device interface GUIDs to explore
-	// PASS 4 : (re)enumerate master USB devices that have a device interface
-	// PASS 5+: (re)enumerate device interfaced GUIDs (including HID) and
-	//           set the device interfaces.
-
-	// Init the GUID table
-	guid[HCD_PASS] = &GUID_DEVINTERFACE_USB_HOST_CONTROLLER;
-	guid[HUB_PASS] = &GUID_DEVINTERFACE_USB_HUB;
-	guid[GEN_PASS] = NULL;
-	guid[DEV_PASS] = &GUID_DEVINTERFACE_USB_DEVICE;
-	HidD_GetHidGuid(&hid_guid);
-	guid[HID_PASS] = &hid_guid;
-	nb_guids = HID_PASS + 1;
-
-	unref_list = calloc(unref_size, sizeof(libusb_device *));
-	if (unref_list == NULL)
-		return LIBUSB_ERROR_NO_MEM;
-
-	for (pass = 0; ((pass < nb_guids) && (r == LIBUSB_SUCCESS)); pass++) {
-//#define ENUM_DEBUG
-#if defined(ENABLE_LOGGING) && defined(ENUM_DEBUG)
-		const char *passname[] = { "HCD", "HUB", "GEN", "DEV", "HID", "EXT" };
-		usbi_dbg("#### PROCESSING %ss %s", passname[(pass <= HID_PASS) ? pass : (HID_PASS + 1)],
-			(pass != GEN_PASS) ? guid_to_string(guid[pass]) : "");
-#endif
-		for (i = 0; ; i++) {
-			// safe loop: free up any (unprotected) dynamic resource
-			// NB: this is always executed before breaking the loop
-			safe_free(dev_interface_details);
-			safe_free(dev_interface_path);
-			safe_free(dev_id_path);
-			priv = parent_priv = NULL;
-			dev = parent_dev = NULL;
-
-			// Safe loop: end of loop conditions
-			if (r != LIBUSB_SUCCESS)
-				break;
-
-			if ((pass == HCD_PASS) && (i == UINT8_MAX)) {
-				usbi_warn(ctx, "program assertion failed - found more than %d buses, skipping the rest.", UINT8_MAX);
-				break;
-			}
-
-			if (pass != GEN_PASS) {
-				// Except for GEN, all passes deal with device interfaces
-				dev_interface_details = get_interface_details(ctx, &dev_info, &dev_info_data, guid[pass], i);
-				if (dev_interface_details == NULL)
-					break;
-
-				dev_interface_path = sanitize_path(dev_interface_details->DevicePath);
-				if (dev_interface_path == NULL) {
-					usbi_warn(ctx, "could not sanitize device interface path for '%s'", dev_interface_details->DevicePath);
-					continue;
-				}
-			} else {
-				// Workaround for a Nec/Renesas USB 3.0 driver bug where root hubs are
-				// being listed under the "NUSB3" PnP Symbolic Name rather than "USB".
-				// The Intel USB 3.0 driver behaves similar, but uses "IUSB3"
-				// The Intel Alpine Ridge USB 3.1 driver uses "IARUSB3"
-				for (; class_index < ARRAYSIZE(usb_class); class_index++) {
-					if (get_devinfo_data(ctx, &dev_info, &dev_info_data, usb_class[class_index], i))
-						break;
-					i = 0;
-				}
-				if (class_index >= ARRAYSIZE(usb_class))
-					break;
-			}
-
-			// Read the Device ID path. This is what we'll use as UID
-			// Note that if the device is plugged in a different port or hub, the Device ID changes
-			if (CM_Get_Device_IDA(dev_info_data.DevInst, path, sizeof(path), 0) != CR_SUCCESS) {
-				usbi_warn(ctx, "could not read the device id path for devinst %X, skipping",
-					(unsigned int)dev_info_data.DevInst);
-				continue;
-			}
-
-			dev_id_path = sanitize_path(path);
-			if (dev_id_path == NULL) {
-				usbi_warn(ctx, "could not sanitize device id path for devinst %X, skipping",
-					(unsigned int)dev_info_data.DevInst);
-				continue;
-			}
-#ifdef ENUM_DEBUG
-			usbi_dbg("PRO: %s", dev_id_path);
-#endif
-
-			// The SPDRP_ADDRESS for USB devices is the device port number on the hub
-			port_nr = 0;
-			if ((pass >= HUB_PASS) && (pass <= GEN_PASS)) {
-				if ((!pSetupDiGetDeviceRegistryPropertyA(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_id_path, windows_error_str(0));
-					continue;
-				}
-			}
-
-			// Set API to use or get additional data from generic pass
-			api = USB_API_UNSUPPORTED;
-			sub_api = SUB_API_NOTSET;
-			switch (pass) {
-			case HCD_PASS:
-				break;
-			case GEN_PASS:
-				// We use the GEN pass to detect driverless devices...
-				size = sizeof(strbuf);
-				if (!pSetupDiGetDeviceRegistryPropertyA(dev_info, &dev_info_data, SPDRP_DRIVER,
-					&reg_type, (BYTE *)strbuf, size, &size)) {
-						usbi_info(ctx, "The following device has no driver: '%s'", dev_id_path);
-						usbi_info(ctx, "libusb will not be able to access it.");
-				}
-				// ...and to add the additional device interface GUIDs
-				key = pSetupDiOpenDevRegKey(dev_info, &dev_info_data, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ);
-				if (key != INVALID_HANDLE_VALUE) {
-					size = sizeof(guid_string_w);
-					s = pRegQueryValueExW(key, L"DeviceInterfaceGUIDs", NULL, &reg_type,
-						(BYTE *)guid_string_w, &size);
-					pRegCloseKey(key);
-					if (s == ERROR_SUCCESS) {
-						if (nb_guids >= MAX_ENUM_GUIDS) {
-							// If this assert is ever reported, grow a GUID table dynamically
-							usbi_err(ctx, "program assertion failed: too many GUIDs");
-							LOOP_BREAK(LIBUSB_ERROR_OVERFLOW);
-						}
-						if_guid = calloc(1, sizeof(GUID));
-						if (if_guid == NULL) {
-							usbi_err(ctx, "could not calloc for if_guid: not enough memory");
-							LOOP_BREAK(LIBUSB_ERROR_NO_MEM);
-						}
-						pCLSIDFromString(guid_string_w, if_guid);
-						guid[nb_guids++] = if_guid;
-						usbi_dbg("extra GUID: %s", guid_to_string(if_guid));
-					}
-				}
-				break;
-			case HID_PASS:
-				api = USB_API_HID;
-				break;
-			default:
-				// Get the API type (after checking that the driver installation is OK)
-				if ((!pSetupDiGetDeviceRegistryPropertyA(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_id_path, windows_error_str(0));
-				} else if (install_state != 0) {
-					usbi_warn(ctx, "driver for device '%s' is reporting an issue (code: %u) - skipping",
-						dev_id_path, (unsigned int)install_state);
-					continue;
-				}
-				get_api_type(ctx, &dev_info, &dev_info_data, &api, &sub_api);
-				break;
-			}
-
-			// Find parent device (for the passes that need it)
-			switch (pass) {
-			case HCD_PASS:
-			case DEV_PASS:
-			case HUB_PASS:
-				break;
-			default:
-				// Go through the ancestors until we see a face we recognize
-				parent_dev = NULL;
-				for (ancestor = 1; parent_dev == NULL; ancestor++) {
-					session_id = get_ancestor_session_id(dev_info_data.DevInst, ancestor);
-					if (session_id == 0)
-						break;
-
-					parent_dev = usbi_get_device_by_session_id(ctx, session_id);
-				}
-
-				if (parent_dev == NULL) {
-					usbi_dbg("unlisted ancestor for '%s' (non USB HID, newly connected, etc.) - ignoring", dev_id_path);
-					continue;
-				}
-
-				parent_priv = _device_priv(parent_dev);
-				// virtual USB devices are also listed during GEN - don't process these yet
-				if ((pass == GEN_PASS) && (parent_priv->apib->id != USB_API_HUB)) {
-					libusb_unref_device(parent_dev);
-					continue;
-				}
-
-				break;
-			}
-
-			// Create new or match existing device, using the (hashed) device_id as session id
-			if (pass <= DEV_PASS) {	// For subsequent passes, we'll lookup the parent
-				// These are the passes that create "new" devices
-				session_id = htab_hash(dev_id_path);
-				dev = usbi_get_device_by_session_id(ctx, session_id);
-				if (dev == NULL) {
-					if (pass == DEV_PASS) {
-						// This can occur if the OS only reports a newly plugged device after we started enum
-						usbi_warn(ctx, "'%s' was only detected in late pass (newly connected device?)"
-							" - ignoring", dev_id_path);
-						continue;
-					}
-
-					usbi_dbg("allocating new device for session [%lX]", session_id);
-					dev = usbi_alloc_device(ctx, session_id);
-					if (dev == NULL)
-						LOOP_BREAK(LIBUSB_ERROR_NO_MEM);
-
-					priv = windows_device_priv_init(dev);
-				} else {
-					usbi_dbg("found existing device for session [%lX] (%u.%u)",
-						session_id, dev->bus_number, dev->device_address);
-
-					priv = _device_priv(dev);
-					if ((parent_dev != NULL) && (dev->parent_dev != NULL)) {
-						if (dev->parent_dev != parent_dev) {
-							// It is possible for the actual parent device to not have existed at the
-							// time of enumeration, so the currently assigned parent may in fact be a
-							// grandparent.  If the devices differ, we assume the "new" parent device
-							// is in fact closer to the device.
-                                                        usbi_dbg("updating parent device [session %lX -> %lX]",
-                                                                dev->parent_dev->session_data, parent_dev->session_data);
-							libusb_unref_device(dev->parent_dev);
-							dev->parent_dev = parent_dev;
-						} else {
-							// We hold a reference to parent_dev instance, but this device already
-							// has a parent_dev reference (only one per child)
-							libusb_unref_device(parent_dev);
-						}
-					}
-				}
-
-				// Keep track of devices that need unref
-				unref_list[unref_cur++] = dev;
-				if (unref_cur >= unref_size) {
-					unref_size += 64;
-					new_unref_list = usbi_reallocf(unref_list, unref_size * sizeof(libusb_device *));
-					if (new_unref_list == NULL) {
-						usbi_err(ctx, "could not realloc list for unref - aborting.");
-						LOOP_BREAK(LIBUSB_ERROR_NO_MEM);
-					} else {
-						unref_list = new_unref_list;
-					}
-				}
-			}
-
-			// Setup device
-			switch (pass) {
-			case HCD_PASS:
-				// If the hcd has already been setup, don't do it again
-				if (priv->path != NULL)
-					break;
-				dev->bus_number = (uint8_t)(i + 1); // bus 0 is reserved for disconnected
-				dev->device_address = 0;
-				dev->num_configurations = 0;
-				priv->apib = &usb_api_backend[USB_API_HUB];
-				priv->sub_api = SUB_API_NOTSET;
-				priv->depth = UINT8_MAX; // Overflow to 0 for HCD Hubs
-				priv->path = dev_interface_path;
-				dev_interface_path = NULL;
-				break;
-			case HUB_PASS:
-			case DEV_PASS:
-				// If the device has already been setup, don't do it again
-				if (priv->path != NULL)
-					break;
-				// Take care of API initialization
-				priv->path = dev_interface_path;
-				dev_interface_path = NULL;
-				priv->apib = &usb_api_backend[api];
-				priv->sub_api = sub_api;
-				switch(api) {
-				case USB_API_COMPOSITE:
-				case USB_API_HUB:
-					break;
-				case USB_API_HID:
-					priv->hid = calloc(1, sizeof(struct hid_device_priv));
-					if (priv->hid == NULL)
-						LOOP_BREAK(LIBUSB_ERROR_NO_MEM);
-
-					priv->hid->nb_interfaces = 0;
-					break;
-				default:
-					// For other devices, the first interface is the same as the device
-					priv->usb_interface[0].path = _strdup(priv->path);
-					if (priv->usb_interface[0].path == NULL)
-						usbi_warn(ctx, "could not duplicate interface path '%s'", priv->path);
-					// The following is needed if we want API calls to work for both simple
-					// and composite devices.
-					for (j = 0; j < USB_MAXINTERFACES; j++)
-						priv->usb_interface[j].apib = &usb_api_backend[api];
-
-					break;
-				}
-				break;
-			case GEN_PASS:
-				r = init_device(dev, parent_dev, (uint8_t)port_nr, dev_id_path, dev_info_data.DevInst);
-				if (r == LIBUSB_SUCCESS) {
-					// Append device to the list of discovered devices
-					discdevs = discovered_devs_append(*_discdevs, dev);
-					if (!discdevs)
-						LOOP_BREAK(LIBUSB_ERROR_NO_MEM);
-
-					*_discdevs = discdevs;
-				} else if (r == LIBUSB_ERROR_NO_DEVICE) {
-					// This can occur if the device was disconnected but Windows hasn't
-					// refreshed its enumeration yet - in that case, we ignore the device
-					r = LIBUSB_SUCCESS;
-				}
-				break;
-			default: // HID_PASS and later
-				if (parent_priv->apib->id == USB_API_HID || parent_priv->apib->id == USB_API_COMPOSITE) {
-					if (parent_priv->apib->id == USB_API_HID) {
-						usbi_dbg("setting HID interface for [%lX]:", parent_dev->session_data);
-						r = set_hid_interface(ctx, parent_dev, dev_interface_path);
-					} else {
-						usbi_dbg("setting composite interface for [%lX]:", parent_dev->session_data);
-						r = set_composite_interface(ctx, parent_dev, dev_interface_path, dev_id_path, api, sub_api);
-					}
-					switch (r) {
-					case LIBUSB_SUCCESS:
-						dev_interface_path = NULL;
-						break;
-					case LIBUSB_ERROR_ACCESS:
-						// interface has already been set => make sure dev_interface_path is freed then
-						r = LIBUSB_SUCCESS;
-						break;
-					default:
-						LOOP_BREAK(r);
-						break;
-					}
-				}
-				libusb_unref_device(parent_dev);
-				break;
-			}
-		}
-	}
-
-	// Free any additional GUIDs
-	for (pass = HID_PASS + 1; pass < nb_guids; pass++)
-		free((void *)guid[pass]);
-
-	// Unref newly allocated devs
-	for (i = 0; i < unref_cur; i++)
-		libusb_unref_device(unref_list[i]);
-	free(unref_list);
-
-	return r;
-}
-
-/*
- * exit: libusb backend deinitialization function
- */
-static void windows_exit(void)
-{
-	int i;
-	HANDLE semaphore;
-	char sem_name[11 + 8 + 1]; // strlen("libusb_init") + (32-bit hex PID) + '\0'
-
-	sprintf(sem_name, "libusb_init%08X", (unsigned int)(GetCurrentProcessId() & 0xFFFFFFFF));
-	semaphore = CreateSemaphoreA(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
-		for (i = 0; i < USB_API_MAX; i++)
-			usb_api_backend[i].exit(SUB_API_NOTSET);
-		exit_dlls();
-		exit_polling();
-		windows_common_exit();
-		usbi_mutex_destroy(&autoclaim_lock);
-	}
-
-	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);
-	*host_endian = 0;
-
-	return (int)size;
-}
-
-static int windows_get_config_descriptor_by_value(struct libusb_device *dev, uint8_t bConfigurationValue,
-	unsigned char **buffer, int *host_endian)
-{
-	struct windows_device_priv *priv = _device_priv(dev);
-	PUSB_CONFIGURATION_DESCRIPTOR config_header;
-	uint8_t index;
-
-	*buffer = NULL;
-	*host_endian = 0;
-
-	if (priv->config_descriptor == NULL)
-		return LIBUSB_ERROR_NOT_FOUND;
-
-	for (index = 0; index < dev->num_configurations; index++) {
-		config_header = (PUSB_CONFIGURATION_DESCRIPTOR)priv->config_descriptor[index];
-		if (config_header->bConfigurationValue == bConfigurationValue) {
-			*buffer = priv->config_descriptor[index];
-			return (int)config_header->wTotalLength;
-		}
-	}
-
-	return LIBUSB_ERROR_NOT_FOUND;
-}
-
-/*
- * 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);
-	unsigned char *config_desc;
-	int r;
-
-	if (priv->active_config == 0)
-		return LIBUSB_ERROR_NOT_FOUND;
-
-	r = windows_get_config_descriptor_by_value(dev, priv->active_config, &config_desc, host_endian);
-	if (r < 0)
-		return r;
-
-	len = MIN((size_t)r, len);
-	memcpy(buffer, config_desc, len);
-	return (int)len;
-}
-
-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(SUB_API_NOTSET, 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(SUB_API_NOTSET, 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);
-
-	safe_free(priv->usb_interface[iface].endpoint);
-	priv->usb_interface[iface].nb_endpoints = 0;
-
-	r = priv->apib->claim_interface(SUB_API_NOTSET, dev_handle, iface);
-
-	if (r == LIBUSB_SUCCESS)
-		r = windows_assign_endpoints(dev_handle, 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(SUB_API_NOTSET, dev_handle, iface, altsetting);
-
-	if (r == LIBUSB_SUCCESS)
-		r = windows_assign_endpoints(dev_handle, 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);
-
-	return priv->apib->release_interface(SUB_API_NOTSET, 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(SUB_API_NOTSET, 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(SUB_API_NOTSET, 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)
-{
-	windows_device_priv_release(dev);
-}
-
-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);
-	safe_free(transfer_priv->hid_buffer);
-	// When auto claim is in use, attempt to release the auto-claimed interface
-	auto_release(itransfer);
-}
-
-static int submit_bulk_transfer(struct usbi_transfer *itransfer)
-{
-	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(SUB_API_NOTSET, itransfer);
-	if (r != LIBUSB_SUCCESS)
-		return r;
-
-	usbi_add_pollfd(ctx, transfer_priv->pollable_fd.fd,
-		(short)(IS_XFERIN(transfer) ? POLLIN : POLLOUT));
-
-	return LIBUSB_SUCCESS;
-}
-
-static int submit_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(SUB_API_NOTSET, itransfer);
-	if (r != LIBUSB_SUCCESS)
-		return r;
-
-	usbi_add_pollfd(ctx, transfer_priv->pollable_fd.fd,
-		(short)(IS_XFERIN(transfer) ? POLLIN : POLLOUT));
-
-	return LIBUSB_SUCCESS;
-}
-
-static int submit_control_transfer(struct usbi_transfer *itransfer)
-{
-	struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
-	struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev);
-	struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
-	struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev);
-	int r;
-
-	r = priv->apib->submit_control_transfer(SUB_API_NOTSET, itransfer);
-	if (r != LIBUSB_SUCCESS)
-		return r;
-
-	usbi_add_pollfd(ctx, transfer_priv->pollable_fd.fd, POLLIN);
-
-	return LIBUSB_SUCCESS;
-}
-
-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:
-		if (IS_XFEROUT(transfer) && (transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET))
-			return LIBUSB_ERROR_NOT_SUPPORTED;
-		return submit_bulk_transfer(itransfer);
-	case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
-		return submit_iso_transfer(itransfer);
-	case LIBUSB_TRANSFER_TYPE_BULK_STREAM:
-		return LIBUSB_ERROR_NOT_SUPPORTED;
-	default:
-		usbi_err(TRANSFER_CTX(transfer), "unknown endpoint type %d", transfer->type);
-		return LIBUSB_ERROR_INVALID_PARAM;
-	}
-}
-
-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(SUB_API_NOTSET, 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(SUB_API_NOTSET, itransfer);
-}
-
-static int windows_cancel_transfer(struct usbi_transfer *itransfer)
-{
-	struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
-
-	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);
-	case LIBUSB_TRANSFER_TYPE_BULK_STREAM:
-		return LIBUSB_ERROR_NOT_SUPPORTED;
-	default:
-		usbi_err(ITRANSFER_CTX(itransfer), "unknown endpoint type %d", transfer->type);
-		return LIBUSB_ERROR_INVALID_PARAM;
-	}
-}
-
-int windows_copy_transfer_data(struct usbi_transfer *itransfer, 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);
-	return priv->apib->copy_transfer_data(SUB_API_NOTSET, itransfer, io_size);
-}
-
-struct winfd *windows_get_fd(struct usbi_transfer *transfer)
-{
-	struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(transfer);
-	return &transfer_priv->pollable_fd;
-}
-
-void windows_get_overlapped_result(struct usbi_transfer *transfer, struct winfd *pollable_fd, DWORD *io_result, DWORD *io_size)
-{
-	if (HasOverlappedIoCompletedSync(pollable_fd->overlapped)) {
-		*io_result = NO_ERROR;
-		*io_size = (DWORD)pollable_fd->overlapped->InternalHigh;
-	} else if (GetOverlappedResult(pollable_fd->handle, pollable_fd->overlapped, io_size, false)) {
-		// Regular async overlapped
-		*io_result = NO_ERROR;
-	} else {
-		*io_result = GetLastError();
-	}
-}
-
-// NB: MSVC6 does not support named initializers.
-const struct usbi_os_backend windows_backend = {
-	"Windows",
-	USBI_CAP_HAS_HID_ACCESS,
-	windows_init,
-	windows_exit,
-
-	windows_get_device_list,
-	NULL,				/* hotplug_poll */
-	windows_open,
-	windows_close,
-
-	windows_get_device_descriptor,
-	windows_get_active_config_descriptor,
-	windows_get_config_descriptor,
-	windows_get_config_descriptor_by_value,
-
-	windows_get_configuration,
-	windows_set_configuration,
-	windows_claim_interface,
-	windows_release_interface,
-
-	windows_set_interface_altsetting,
-	windows_clear_halt,
-	windows_reset_device,
-
-	NULL,				/* alloc_streams */
-	NULL,				/* free_streams */
-
-	NULL,				/* dev_mem_alloc */
-	NULL,				/* dev_mem_free */
-
-	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,
-	NULL,
-
-	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),
-};
-
-
-/*
- * USB API backends
- */
-static int unsupported_init(int sub_api, struct libusb_context *ctx)
-{
-	return LIBUSB_SUCCESS;
-}
-
-static int unsupported_exit(int sub_api)
-{
-	return LIBUSB_SUCCESS;
-}
-
-static int unsupported_open(int sub_api, struct libusb_device_handle *dev_handle)
-{
-	PRINT_UNSUPPORTED_API(open);
-}
-
-static void unsupported_close(int sub_api, struct libusb_device_handle *dev_handle)
-{
-	usbi_dbg("unsupported API call for 'close'");
-}
-
-static int unsupported_configure_endpoints(int sub_api, struct libusb_device_handle *dev_handle, int iface)
-{
-	PRINT_UNSUPPORTED_API(configure_endpoints);
-}
-
-static int unsupported_claim_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface)
-{
-	PRINT_UNSUPPORTED_API(claim_interface);
-}
-
-static int unsupported_set_interface_altsetting(int sub_api, struct libusb_device_handle *dev_handle, int iface, int altsetting)
-{
-	PRINT_UNSUPPORTED_API(set_interface_altsetting);
-}
-
-static int unsupported_release_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface)
-{
-	PRINT_UNSUPPORTED_API(release_interface);
-}
-
-static int unsupported_clear_halt(int sub_api, struct libusb_device_handle *dev_handle, unsigned char endpoint)
-{
-	PRINT_UNSUPPORTED_API(clear_halt);
-}
-
-static int unsupported_reset_device(int sub_api, struct libusb_device_handle *dev_handle)
-{
-	PRINT_UNSUPPORTED_API(reset_device);
-}
-
-static int unsupported_submit_bulk_transfer(int sub_api, struct usbi_transfer *itransfer)
-{
-	PRINT_UNSUPPORTED_API(submit_bulk_transfer);
-}
-
-static int unsupported_submit_iso_transfer(int sub_api, struct usbi_transfer *itransfer)
-{
-	PRINT_UNSUPPORTED_API(submit_iso_transfer);
-}
-
-static int unsupported_submit_control_transfer(int sub_api, struct usbi_transfer *itransfer)
-{
-	PRINT_UNSUPPORTED_API(submit_control_transfer);
-}
-
-static int unsupported_abort_control(int sub_api, struct usbi_transfer *itransfer)
-{
-	PRINT_UNSUPPORTED_API(abort_control);
-}
-
-static int unsupported_abort_transfers(int sub_api, struct usbi_transfer *itransfer)
-{
-	PRINT_UNSUPPORTED_API(abort_transfers);
-}
-
-static int unsupported_copy_transfer_data(int sub_api, struct usbi_transfer *itransfer, uint32_t io_size)
-{
-	PRINT_UNSUPPORTED_API(copy_transfer_data);
-}
-
-static int common_configure_endpoints(int sub_api, struct libusb_device_handle *dev_handle, int iface)
-{
-	return LIBUSB_SUCCESS;
-}
-
-// These names must be uppercase
-static const char *hub_driver_names[] = {"USBHUB", "USBHUB3", "USB3HUB", "NUSB3HUB", "RUSB3HUB", "FLXHCIH", "TIHUB3", "ETRONHUB3", "VIAHUB3", "ASMTHUB3", "IUSB3HUB", "VUSB3HUB", "AMDHUB30", "VHHUB", "AUSB3HUB"};
-static const char *composite_driver_names[] = {"USBCCGP"};
-static const char *winusbx_driver_names[] = WINUSBX_DRV_NAMES;
-static const char *hid_driver_names[] = {"HIDUSB", "MOUHID", "KBDHID"};
-const struct windows_usb_api_backend usb_api_backend[USB_API_MAX] = {
-	{
-		USB_API_UNSUPPORTED,
-		"Unsupported API",
-		NULL,
-		0,
-		unsupported_init,
-		unsupported_exit,
-		unsupported_open,
-		unsupported_close,
-		unsupported_configure_endpoints,
-		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_HUB,
-		"HUB API",
-		hub_driver_names,
-		ARRAYSIZE(hub_driver_names),
-		unsupported_init,
-		unsupported_exit,
-		unsupported_open,
-		unsupported_close,
-		unsupported_configure_endpoints,
-		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,
-		"Composite API",
-		composite_driver_names,
-		ARRAYSIZE(composite_driver_names),
-		composite_init,
-		composite_exit,
-		composite_open,
-		composite_close,
-		common_configure_endpoints,
-		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_WINUSBX,
-		"WinUSB-like APIs",
-		winusbx_driver_names,
-		ARRAYSIZE(winusbx_driver_names),
-		winusbx_init,
-		winusbx_exit,
-		winusbx_open,
-		winusbx_close,
-		winusbx_configure_endpoints,
-		winusbx_claim_interface,
-		winusbx_set_interface_altsetting,
-		winusbx_release_interface,
-		winusbx_clear_halt,
-		winusbx_reset_device,
-		winusbx_submit_bulk_transfer,
-		unsupported_submit_iso_transfer,
-		winusbx_submit_control_transfer,
-		winusbx_abort_control,
-		winusbx_abort_transfers,
-		winusbx_copy_transfer_data,
-	},
-	{
-		USB_API_HID,
-		"HID API",
-		hid_driver_names,
-		ARRAYSIZE(hid_driver_names),
-		hid_init,
-		hid_exit,
-		hid_open,
-		hid_close,
-		common_configure_endpoints,
-		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-like (WinUSB, libusb0/libusbK through libusbk DLL) API functions
- */
-#define WinUSBX_Set(fn)										\
-	do {											\
-		if (native_winusb)								\
-			WinUSBX[i].fn = (WinUsb_##fn##_t)GetProcAddress(h, "WinUsb_" #fn);	\
-		else										\
-			pLibK_GetProcAddress((PVOID *)&WinUSBX[i].fn, i, KUSB_FNID_##fn);	\
-	} while (0)
-
-static int winusbx_init(int sub_api, struct libusb_context *ctx)
-{
-	HMODULE h;
-	bool native_winusb;
-	int i;
-	KLIB_VERSION LibK_Version;
-	LibK_GetProcAddress_t pLibK_GetProcAddress = NULL;
-	LibK_GetVersion_t pLibK_GetVersion;
-
-	h = LoadLibraryA("libusbK");
-
-	if (h == NULL) {
-		usbi_info(ctx, "libusbK DLL is not available, will use native WinUSB");
-		h = LoadLibraryA("WinUSB");
-
-		if (h == NULL) {
-			usbi_warn(ctx, "WinUSB DLL is not available either, "
-				"you will not be able to access devices outside of enumeration");
-			return LIBUSB_ERROR_NOT_FOUND;
-		}
-	} else {
-		usbi_dbg("using libusbK DLL for universal access");
-		pLibK_GetVersion = (LibK_GetVersion_t)GetProcAddress(h, "LibK_GetVersion");
-		if (pLibK_GetVersion != NULL) {
-			pLibK_GetVersion(&LibK_Version);
-			usbi_dbg("libusbK version: %d.%d.%d.%d", LibK_Version.Major, LibK_Version.Minor,
-				LibK_Version.Micro, LibK_Version.Nano);
-		}
-		pLibK_GetProcAddress = (LibK_GetProcAddress_t)GetProcAddress(h, "LibK_GetProcAddress");
-		if (pLibK_GetProcAddress == NULL) {
-			usbi_err(ctx, "LibK_GetProcAddress() not found in libusbK DLL");
-			FreeLibrary(h);
-			return LIBUSB_ERROR_NOT_FOUND;
-		}
-	}
-
-	native_winusb = (pLibK_GetProcAddress == NULL);
-	for (i = SUB_API_LIBUSBK; i < SUB_API_MAX; i++) {
-		WinUSBX_Set(AbortPipe);
-		WinUSBX_Set(ControlTransfer);
-		WinUSBX_Set(FlushPipe);
-		WinUSBX_Set(Free);
-		WinUSBX_Set(GetAssociatedInterface);
-		WinUSBX_Set(GetCurrentAlternateSetting);
-		WinUSBX_Set(GetDescriptor);
-		WinUSBX_Set(GetOverlappedResult);
-		WinUSBX_Set(GetPipePolicy);
-		WinUSBX_Set(GetPowerPolicy);
-		WinUSBX_Set(Initialize);
-		WinUSBX_Set(QueryDeviceInformation);
-		WinUSBX_Set(QueryInterfaceSettings);
-		WinUSBX_Set(QueryPipe);
-		WinUSBX_Set(ReadPipe);
-		WinUSBX_Set(ResetPipe);
-		WinUSBX_Set(SetCurrentAlternateSetting);
-		WinUSBX_Set(SetPipePolicy);
-		WinUSBX_Set(SetPowerPolicy);
-		WinUSBX_Set(WritePipe);
-		if (!native_winusb)
-			WinUSBX_Set(ResetDevice);
-
-		if (WinUSBX[i].Initialize != NULL) {
-			WinUSBX[i].initialized = true;
-			usbi_dbg("initalized sub API %s", sub_api_name[i]);
-		} else {
-			usbi_warn(ctx, "Failed to initalize sub API %s", sub_api_name[i]);
-			WinUSBX[i].initialized = false;
-		}
-	}
-
-	WinUSBX_handle = h;
-	return LIBUSB_SUCCESS;
-}
-
-static int winusbx_exit(int sub_api)
-{
-	if (WinUSBX_handle != NULL) {
-		FreeLibrary(WinUSBX_handle);
-		WinUSBX_handle = NULL;
-
-		/* Reset the WinUSBX API structures */
-		memset(&WinUSBX, 0, sizeof(WinUSBX));
-	}
-
-	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 winusbx_open(int sub_api, 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_WINUSBX_AVAILABLE(sub_api);
-
-	// WinUSB requires a separate 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_WINUSBX)) {
-			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->usb_interface[i].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 winusbx_close(int sub_api, 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 handle;
-	int i;
-
-	if (sub_api == SUB_API_NOTSET)
-		sub_api = priv->sub_api;
-
-	if (!WinUSBX[sub_api].initialized)
-		return;
-
-	if (priv->apib->id == USB_API_COMPOSITE) {
-		// If this is a composite device, just free and close all WinUSB-like
-		// interfaces directly (each is independent and not associated with another)
-		for (i = 0; i < USB_MAXINTERFACES; i++) {
-			if (priv->usb_interface[i].apib->id == USB_API_WINUSBX) {
-				handle = handle_priv->interface_handle[i].api_handle;
-				if (HANDLE_VALID(handle))
-					WinUSBX[sub_api].Free(handle);
-
-				handle = handle_priv->interface_handle[i].dev_handle;
-				if (HANDLE_VALID(handle))
-					CloseHandle(handle);
-			}
-		}
-	} else {
-		// If this is a WinUSB device, free all interfaces above interface 0,
-		// then free and close interface 0 last
-		for (i = 1; i < USB_MAXINTERFACES; i++) {
-			handle = handle_priv->interface_handle[i].api_handle;
-			if (HANDLE_VALID(handle))
-				WinUSBX[sub_api].Free(handle);
-		}
-		handle = handle_priv->interface_handle[0].api_handle;
-		if (HANDLE_VALID(handle))
-			WinUSBX[sub_api].Free(handle);
-
-		handle = handle_priv->interface_handle[0].dev_handle;
-		if (HANDLE_VALID(handle))
-			CloseHandle(handle);
-	}
-}
-
-static int winusbx_configure_endpoints(int sub_api, 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);
-	HANDLE winusb_handle = handle_priv->interface_handle[iface].api_handle;
-	UCHAR policy;
-	ULONG timeout = 0;
-	uint8_t endpoint_address;
-	int i;
-
-	CHECK_WINUSBX_AVAILABLE(sub_api);
-
-	// With handle and enpoints set (in parent), we can setup the default pipe properties
-	// see http://download.microsoft.com/download/D/1/D/D1DD7745-426B-4CC3-A269-ABBBE427C0EF/DVC-T705_DDC08.pptx
-	for (i = -1; i < priv->usb_interface[iface].nb_endpoints; i++) {
-		endpoint_address = (i == -1) ? 0 : priv->usb_interface[iface].endpoint[i];
-		if (!WinUSBX[sub_api].SetPipePolicy(winusb_handle, endpoint_address,
-			PIPE_TRANSFER_TIMEOUT, sizeof(ULONG), &timeout))
-			usbi_dbg("failed to set PIPE_TRANSFER_TIMEOUT for control endpoint %02X", endpoint_address);
-
-		if ((i == -1) || (sub_api == SUB_API_LIBUSB0))
-			continue; // Other policies don't apply to control endpoint or libusb0
-
-		policy = false;
-		if (!WinUSBX[sub_api].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 (!WinUSBX[sub_api].SetPipePolicy(winusb_handle, endpoint_address,
-			IGNORE_SHORT_PACKETS, sizeof(UCHAR), &policy))
-			usbi_dbg("failed to disable IGNORE_SHORT_PACKETS for endpoint %02X", endpoint_address);
-
-		policy = true;
-		/* ALLOW_PARTIAL_READS must be enabled due to likely libusbK bug. See:
-		   https://sourceforge.net/mailarchive/message.php?msg_id=29736015 */
-		if (!WinUSBX[sub_api].SetPipePolicy(winusb_handle, endpoint_address,
-			ALLOW_PARTIAL_READS, sizeof(UCHAR), &policy))
-			usbi_dbg("failed to enable ALLOW_PARTIAL_READS for endpoint %02X", endpoint_address);
-
-		if (!WinUSBX[sub_api].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 winusbx_claim_interface(int sub_api, 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_using_usbccgp = (priv->apib->id == USB_API_COMPOSITE);
-	SP_DEVICE_INTERFACE_DETAIL_DATA_A *dev_interface_details = NULL;
-	HDEVINFO dev_info = INVALID_HANDLE_VALUE;
-	SP_DEVINFO_DATA dev_info_data;
-	char *dev_path_no_guid = NULL;
-	char filter_path[] = "\\\\.\\libusb0-0000";
-	bool found_filter = false;
-	HANDLE file_handle, winusb_handle;
-	DWORD err;
-	int i;
-
-	CHECK_WINUSBX_AVAILABLE(sub_api);
-
-	// If the device is composite, but using the default Windows composite parent driver (usbccgp)
-	// or if it's the first WinUSB-like interface, we get a handle through Initialize().
-	if ((is_using_usbccgp) || (iface == 0)) {
-		// composite device (independent interfaces) or interface 0
-		file_handle = handle_priv->interface_handle[iface].dev_handle;
-		if (!HANDLE_VALID(file_handle))
-			return LIBUSB_ERROR_NOT_FOUND;
-
-		if (!WinUSBX[sub_api].Initialize(file_handle, &winusb_handle)) {
-			handle_priv->interface_handle[iface].api_handle = INVALID_HANDLE_VALUE;
-			err = GetLastError();
-			switch(err) {
-			case ERROR_BAD_COMMAND:
-				// The device was disconnected
-				usbi_err(ctx, "could not access interface %d: %s", iface, windows_error_str(0));
-				return LIBUSB_ERROR_NO_DEVICE;
-			default:
-				// it may be that we're using the libusb0 filter driver.
-				// TODO: can we move this whole business into the K/0 DLL?
-				for (i = 0; ; i++) {
-					safe_free(dev_interface_details);
-					safe_free(dev_path_no_guid);
-
-					dev_interface_details = get_interface_details_filter(ctx, &dev_info, &dev_info_data, &GUID_DEVINTERFACE_LIBUSB0_FILTER, i, filter_path);
-					if ((found_filter) || (dev_interface_details == NULL))
-						break;
-
-					// ignore GUID part
-					dev_path_no_guid = sanitize_path(strtok(dev_interface_details->DevicePath, "{"));
-					if (dev_path_no_guid == NULL)
-						continue;
-
-					if (strncmp(dev_path_no_guid, priv->usb_interface[iface].path, strlen(dev_path_no_guid)) == 0) {
-						file_handle = CreateFileA(filter_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) {
-							if (WinUSBX[sub_api].Initialize(file_handle, &winusb_handle)) {
-								// Replace the existing file handle with the working one
-								CloseHandle(handle_priv->interface_handle[iface].dev_handle);
-								handle_priv->interface_handle[iface].dev_handle = file_handle;
-								found_filter = true;
-							} else {
-								usbi_err(ctx, "could not initialize filter driver for %s", filter_path);
-								CloseHandle(file_handle);
-							}
-						} else {
-							usbi_err(ctx, "could not open device %s: %s", filter_path, windows_error_str(0));
-						}
-					}
-				}
-				free(dev_interface_details);
-				if (!found_filter) {
-					usbi_err(ctx, "could not access interface %d: %s", iface, windows_error_str(err));
-					return LIBUSB_ERROR_ACCESS;
-				}
-			}
-		}
-		handle_priv->interface_handle[iface].api_handle = winusb_handle;
-	} else {
-		// For all other interfaces, use GetAssociatedInterface()
-		winusb_handle = handle_priv->interface_handle[0].api_handle;
-		// It is a requirement for multiple interface devices on Windows that, to you
-		// must first claim the first interface before you claim the others
-		if (!HANDLE_VALID(winusb_handle)) {
-			file_handle = handle_priv->interface_handle[0].dev_handle;
-			if (WinUSBX[sub_api].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): %s", iface, windows_error_str(0));
-				return LIBUSB_ERROR_ACCESS;
-			}
-		}
-		if (!WinUSBX[sub_api].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;
-			}
-		}
-	}
-	usbi_dbg("claimed interface %d", iface);
-	handle_priv->active_interface = iface;
-
-	return LIBUSB_SUCCESS;
-}
-
-static int winusbx_release_interface(int sub_api, 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);
-	HANDLE winusb_handle;
-
-	CHECK_WINUSBX_AVAILABLE(sub_api);
-
-	winusb_handle = handle_priv->interface_handle[iface].api_handle;
-	if (!HANDLE_VALID(winusb_handle))
-		return LIBUSB_ERROR_NOT_FOUND;
-
-	WinUSBX[sub_api].Free(winusb_handle);
-	handle_priv->interface_handle[iface].api_handle = INVALID_HANDLE_VALUE;
-
-	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_WINUSBX) || (api_id > USB_API_HID)) {
-		usbi_dbg("unsupported API ID");
-		return -1;
-	}
-
-	for (i = 0; i < USB_MAXINTERFACES; i++) {
-		if (HANDLE_VALID(handle_priv->interface_handle[i].dev_handle)
-				&& HANDLE_VALID(handle_priv->interface_handle[i].api_handle)
-				&& (priv->usb_interface[i].apib->id == 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_VALID(handle_priv->interface_handle[i].api_handle))
-			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 winusbx_submit_control_transfer(int sub_api, 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);
-	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_WINUSBX_AVAILABLE(sub_api);
-
-	transfer_priv->pollable_fd = INVALID_WINFD;
-	size = transfer->length - LIBUSB_CONTROL_SETUP_SIZE;
-
-	// Windows places upper limits on the control transfer size
-	// See: https://msdn.microsoft.com/en-us/library/windows/hardware/ff538112.aspx
-	if (size > MAX_CTRL_BUFFER_LENGTH)
-		return LIBUSB_ERROR_INVALID_PARAM;
-
-	current_interface = get_valid_interface(transfer->dev_handle, USB_API_WINUSBX);
-	if (current_interface < 0) {
-		if (auto_claim(transfer, &current_interface, USB_API_WINUSBX) != LIBUSB_SUCCESS)
-			return LIBUSB_ERROR_NOT_FOUND;
-	}
-
-	usbi_dbg("will use interface %d", current_interface);
-	winusb_handle = handle_priv->interface_handle[current_interface].api_handle;
-
-	wfd = usbi_create_fd(winusb_handle, RW_READ, NULL, NULL);
-	// Always use the handle returned from usbi_create_fd (wfd.handle)
-	if (wfd.fd < 0)
-		return LIBUSB_ERROR_NO_MEM;
-
-	// Sending of set configuration control requests from WinUSB creates issues
-	if (((setup->request_type & (0x03 << 5)) == LIBUSB_REQUEST_TYPE_STANDARD)
-			&& (setup->request == LIBUSB_REQUEST_SET_CONFIGURATION)) {
-		if (setup->value != priv->active_config) {
-			usbi_warn(ctx, "cannot set configuration other than the default one");
-			usbi_free_fd(&wfd);
-			return LIBUSB_ERROR_INVALID_PARAM;
-		}
-		wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY;
-		wfd.overlapped->InternalHigh = 0;
-	} else {
-		if (!WinUSBX[sub_api].ControlTransfer(wfd.handle, *setup, transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE, size, NULL, wfd.overlapped)) {
-			if (GetLastError() != ERROR_IO_PENDING) {
-				usbi_warn(ctx, "ControlTransfer failed: %s", windows_error_str(0));
-				usbi_free_fd(&wfd);
-				return LIBUSB_ERROR_IO;
-			}
-		} else {
-			wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY;
-			wfd.overlapped->InternalHigh = (DWORD)size;
-		}
-	}
-
-	// 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 winusbx_set_interface_altsetting(int sub_api, 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);
-	struct windows_device_priv *priv = _device_priv(dev_handle->dev);
-	HANDLE winusb_handle;
-
-	CHECK_WINUSBX_AVAILABLE(sub_api);
-
-	if (altsetting > 255)
-		return LIBUSB_ERROR_INVALID_PARAM;
-
-	winusb_handle = handle_priv->interface_handle[iface].api_handle;
-	if (!HANDLE_VALID(winusb_handle)) {
-		usbi_err(ctx, "interface must be claimed first");
-		return LIBUSB_ERROR_NOT_FOUND;
-	}
-
-	if (!WinUSBX[sub_api].SetCurrentAlternateSetting(winusb_handle, (UCHAR)altsetting)) {
-		usbi_err(ctx, "SetCurrentAlternateSetting failed: %s", windows_error_str(0));
-		return LIBUSB_ERROR_IO;
-	}
-
-	return LIBUSB_SUCCESS;
-}
-
-static int winusbx_submit_bulk_transfer(int sub_api, 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 ret;
-	int current_interface;
-	struct winfd wfd;
-
-	CHECK_WINUSBX_AVAILABLE(sub_api);
-
-	transfer_priv->pollable_fd = INVALID_WINFD;
-
-	current_interface = interface_by_endpoint(priv, handle_priv, transfer->endpoint);
-	if (current_interface < 0) {
-		usbi_err(ctx, "unable to match endpoint to an open interface - cancelling transfer");
-		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;
-
-	wfd = usbi_create_fd(winusb_handle, IS_XFERIN(transfer) ? RW_READ : RW_WRITE, NULL, NULL);
-	// Always use the handle returned from usbi_create_fd (wfd.handle)
-	if (wfd.fd < 0)
-		return LIBUSB_ERROR_NO_MEM;
-
-	if (IS_XFERIN(transfer)) {
-		usbi_dbg("reading %d bytes", transfer->length);
-		ret = WinUSBX[sub_api].ReadPipe(wfd.handle, transfer->endpoint, transfer->buffer, transfer->length, NULL, wfd.overlapped);
-	} else {
-		usbi_dbg("writing %d bytes", transfer->length);
-		ret = WinUSBX[sub_api].WritePipe(wfd.handle, transfer->endpoint, transfer->buffer, transfer->length, NULL, wfd.overlapped);
-	}
-
-	if (!ret) {
-		if (GetLastError() != ERROR_IO_PENDING) {
-			usbi_err(ctx, "ReadPipe/WritePipe failed: %s", windows_error_str(0));
-			usbi_free_fd(&wfd);
-			return LIBUSB_ERROR_IO;
-		}
-	} else {
-		wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY;
-		wfd.overlapped->InternalHigh = (DWORD)transfer->length;
-	}
-
-	transfer_priv->pollable_fd = wfd;
-	transfer_priv->interface_number = (uint8_t)current_interface;
-
-	return LIBUSB_SUCCESS;
-}
-
-static int winusbx_clear_halt(int sub_api, 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_WINUSBX_AVAILABLE(sub_api);
-
-	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 (!WinUSBX[sub_api].ResetPipe(winusb_handle, endpoint)) {
-		usbi_err(ctx, "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 winusbx_abort_control(int sub_api, struct usbi_transfer *itransfer)
-{
-	// Cancelling of the I/O is done in the parent
-	return LIBUSB_SUCCESS;
-}
-
-static int winusbx_abort_transfers(int sub_api, 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);
-	struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev);
-	HANDLE winusb_handle;
-	int current_interface;
-
-	CHECK_WINUSBX_AVAILABLE(sub_api);
-
-	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 (!WinUSBX[sub_api].AbortPipe(winusb_handle, transfer->endpoint)) {
-		usbi_err(ctx, "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: (post hotplug): see if we can force eject the device and redetect it (reuse hotplug?)
-static int winusbx_reset_device(int sub_api, 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_WINUSBX_AVAILABLE(sub_api);
-
-	// Reset any available pipe (except control)
-	for (i = 0; i < USB_MAXINTERFACES; i++) {
-		winusb_handle = handle_priv->interface_handle[i].api_handle;
-		for (wfd = handle_to_winfd(winusb_handle); wfd.fd > 0; ) {
-			// Cancel any pollable I/O
-			usbi_remove_pollfd(ctx, wfd.fd);
-			usbi_free_fd(&wfd);
-			wfd = handle_to_winfd(winusb_handle);
-		}
-
-		if (HANDLE_VALID(winusb_handle)) {
-			for (j = 0; j < priv->usb_interface[i].nb_endpoints; j++) {
-				usbi_dbg("resetting ep %02X", priv->usb_interface[i].endpoint[j]);
-				if (!WinUSBX[sub_api].AbortPipe(winusb_handle, priv->usb_interface[i].endpoint[j]))
-					usbi_err(ctx, "AbortPipe (pipe address %02X) failed: %s",
-						priv->usb_interface[i].endpoint[j], windows_error_str(0));
-
-				// FlushPipe seems to fail on OUT pipes
-				if (IS_EPIN(priv->usb_interface[i].endpoint[j])
-						&& (!WinUSBX[sub_api].FlushPipe(winusb_handle, priv->usb_interface[i].endpoint[j])))
-					usbi_err(ctx, "FlushPipe (pipe address %02X) failed: %s",
-						priv->usb_interface[i].endpoint[j], windows_error_str(0));
-
-				if (!WinUSBX[sub_api].ResetPipe(winusb_handle, priv->usb_interface[i].endpoint[j]))
-					usbi_err(ctx, "ResetPipe (pipe address %02X) failed: %s",
-						priv->usb_interface[i].endpoint[j], windows_error_str(0));
-			}
-		}
-	}
-
-	// libusbK & libusb0 have the ability to issue an actual device reset
-	if (WinUSBX[sub_api].ResetDevice != NULL) {
-		winusb_handle = handle_priv->interface_handle[0].api_handle;
-		if (HANDLE_VALID(winusb_handle))
-			WinUSBX[sub_api].ResetDevice(winusb_handle);
-	}
-
-	return LIBUSB_SUCCESS;
-}
-
-static int winusbx_copy_transfer_data(int sub_api, 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 = (struct libusb_endpoint_descriptor *)((char *)ed + LIBUSB_DT_ENDPOINT_SIZE);
-	}
-
-	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_NOT_SUPPORTED;
-}
-
-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, int report_type)
-{
-	uint8_t *buf;
-	DWORD ioctl_code, 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 (%u)", *size);
-		return LIBUSB_ERROR_INVALID_PARAM;
-	}
-
-	switch (report_type) {
-	case HID_REPORT_TYPE_INPUT:
-		ioctl_code = IOCTL_HID_GET_INPUT_REPORT;
-		break;
-	case HID_REPORT_TYPE_FEATURE:
-		ioctl_code = IOCTL_HID_GET_FEATURE;
-		break;
-	default:
-		usbi_dbg("unknown HID report type %d", report_type);
-		return LIBUSB_ERROR_INVALID_PARAM;
-	}
-
-	// Add a trailing byte to detect overflows
-	buf = calloc(1, expected_size + 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]);
-
-	tp->hid_expected_size = expected_size;
-	read_size = expected_size;
-
-	// NB: The size returned by DeviceIoControl doesn't include report IDs when not in use (0)
-	if (!DeviceIoControl(hid_handle, ioctl_code, buf, expected_size + 1,
-		buf, expected_size + 1, &read_size, overlapped)) {
-		if (GetLastError() != ERROR_IO_PENDING) {
-			usbi_dbg("Failed to Read HID Report: %s", windows_error_str(0));
-			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;
-	}
-
-	// Transfer completed synchronously => copy and discard extra buffer
-	if (read_size == 0) {
-		usbi_warn(NULL, "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;
-		}
-
-		*size = MIN((size_t)read_size, *size);
-		if (id == 0)
-			memcpy(data, buf + 1, *size); // Discard report ID
-		else
-			memcpy(data, buf, *size);
-	}
-
-	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, int report_type)
-{
-	uint8_t *buf = NULL;
-	DWORD ioctl_code, 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 (%u)", *size);
-		return LIBUSB_ERROR_INVALID_PARAM;
-	}
-
-	switch (report_type) {
-	case HID_REPORT_TYPE_OUTPUT:
-		ioctl_code = IOCTL_HID_SET_OUTPUT_REPORT;
-		break;
-	case HID_REPORT_TYPE_FEATURE:
-		ioctl_code = IOCTL_HID_SET_FEATURE;
-		break;
-	default:
-		usbi_dbg("unknown HID report type %d", report_type);
-		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);
-	}
-
-	// NB: The size returned by DeviceIoControl doesn't include report IDs when not in use (0)
-	if (!DeviceIoControl(hid_handle, ioctl_code, buf, write_size,
-		buf, write_size, &write_size, overlapped)) {
-		if (GetLastError() != ERROR_IO_PENDING) {
-			usbi_dbg("Failed to Write HID Output Report: %s", windows_error_str(0));
-			free(buf);
-			return LIBUSB_ERROR_IO;
-		}
-		tp->hid_buffer = buf;
-		tp->hid_dest = NULL;
-		return LIBUSB_SUCCESS;
-	}
-
-	// Transfer completed synchronously
-	*size = write_size;
-	if (write_size == 0)
-		usbi_dbg("program assertion failed - write completed synchronously, but no data was written");
-
-	free(buf);
-	return LIBUSB_COMPLETED;
-}
-
-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)
-		return _hid_set_report(dev, hid_handle, report_id, data, tp, size, overlapped, report_type);
-
-	if (LIBUSB_REQ_IN(request_type) && request == HID_REQ_GET_REPORT)
-		return _hid_get_report(dev, hid_handle, report_id, data, tp, size, overlapped, report_type);
-
-	return LIBUSB_ERROR_INVALID_PARAM;
-}
-
-
-/*
- * HID API functions
- */
-static int hid_init(int sub_api, struct libusb_context *ctx)
-{
-	DLL_GET_HANDLE(hid);
-	DLL_LOAD_FUNC(hid, HidD_GetAttributes, TRUE);
-	DLL_LOAD_FUNC(hid, HidD_GetHidGuid, TRUE);
-	DLL_LOAD_FUNC(hid, HidD_GetPreparsedData, TRUE);
-	DLL_LOAD_FUNC(hid, HidD_FreePreparsedData, TRUE);
-	DLL_LOAD_FUNC(hid, HidD_GetManufacturerString, TRUE);
-	DLL_LOAD_FUNC(hid, HidD_GetProductString, TRUE);
-	DLL_LOAD_FUNC(hid, HidD_GetSerialNumberString, TRUE);
-	DLL_LOAD_FUNC(hid, HidP_GetCaps, TRUE);
-	DLL_LOAD_FUNC(hid, HidD_SetNumInputBuffers, TRUE);
-	DLL_LOAD_FUNC(hid, HidD_SetFeature, TRUE);
-	DLL_LOAD_FUNC(hid, HidD_GetFeature, TRUE);
-	DLL_LOAD_FUNC(hid, HidD_GetPhysicalDescriptor, TRUE);
-	DLL_LOAD_FUNC(hid, HidD_GetInputReport, FALSE);
-	DLL_LOAD_FUNC(hid, HidD_SetOutputReport, FALSE);
-	DLL_LOAD_FUNC(hid, HidD_FlushQueue, TRUE);
-	DLL_LOAD_FUNC(hid, HidP_GetValueCaps, TRUE);
-
-	api_hid_available = true;
-	return LIBUSB_SUCCESS;
-}
-
-static int hid_exit(int sub_api)
-{
-	DLL_FREE_HANDLE(hid);
-
-	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(int sub_api, 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];
-	int nb_ids[2]; // zero and nonzero report IDs
-#if defined(ENABLE_LOGGING)
-	const char *type[3] = {"input", "output", "feature"};
-#endif
-
-	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 = HidP_Input; j <= HidP_Feature; j++) {
-			usbi_dbg("%u HID %s report value(s) found", (unsigned int)size[j], type[j]);
-			priv->hid->uses_report_ids[j] = false;
-			if (size[j] > 0) {
-				value_caps = calloc(size[j], sizeof(HIDP_VALUE_CAPS));
-				if ((value_caps != NULL)
-						&& (HidP_GetValueCaps((HIDP_REPORT_TYPE)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]);
-				}
-				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(int sub_api, 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 (HANDLE_VALID(file_handle))
-				CloseHandle(file_handle);
-		}
-	}
-}
-
-static int hid_claim_interface(int sub_api, 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(int sub_api, 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(int sub_api, 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(int sub_api, struct usbi_transfer *itransfer)
-{
-	struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
-	struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
-	struct windows_device_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 (auto_claim(transfer, &current_interface, USB_API_HID) != LIBUSB_SUCCESS)
-			return LIBUSB_ERROR_NOT_FOUND;
-	}
-
-	usbi_dbg("will use interface %d", current_interface);
-	hid_handle = handle_priv->interface_handle[current_interface].api_handle;
-	// Always use the handle returned from usbi_create_fd (wfd.handle)
-	wfd = usbi_create_fd(hid_handle, RW_READ, NULL, NULL);
-	if (wfd.fd < 0)
-		return LIBUSB_ERROR_NOT_FOUND;
-
-	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_NOT_SUPPORTED;
-			}
-			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(0, 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_NOT_SUPPORTED;
-			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_NOT_SUPPORTED;
-		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);
-	}
-
-	return r;
-}
-
-static int hid_submit_bulk_transfer(int sub_api, struct usbi_transfer *itransfer)
-{
-	struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
-	struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
-	struct 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?RW_READ:RW_WRITE, NULL, NULL);
-	// Always use the handle returned from usbi_create_fd (wfd.handle)
-	if (wfd.fd < 0)
-		return LIBUSB_ERROR_NO_MEM;
-
-	// If 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 = calloc(1, length + 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: 0x00)", length);
-		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);
-			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(int sub_api, struct usbi_transfer *itransfer)
-{
-	struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
-	struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
-	struct windows_device_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(int sub_api, 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 (HANDLE_VALID(hid_handle))
-			HidD_FlushQueue(hid_handle);
-	}
-
-	return LIBUSB_SUCCESS;
-}
-
-static int hid_clear_halt(int sub_api, 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(int sub_api, 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
-			if (corrected_size > 0) {
-				// 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(int sub_api, struct libusb_context *ctx)
-{
-	return LIBUSB_SUCCESS;
-}
-
-static int composite_exit(int sub_api)
-{
-	return LIBUSB_SUCCESS;
-}
-
-static int composite_open(int sub_api, struct libusb_device_handle *dev_handle)
-{
-	struct windows_device_priv *priv = _device_priv(dev_handle->dev);
-	int r = LIBUSB_ERROR_NOT_FOUND;
-	uint8_t i;
-	// SUB_API_MAX + 1 as the SUB_API_MAX pos is used to indicate availability of HID
-	bool available[SUB_API_MAX + 1] = { 0 };
-
-	for (i = 0; i < USB_MAXINTERFACES; i++) {
-		switch (priv->usb_interface[i].apib->id) {
-		case USB_API_WINUSBX:
-			if (priv->usb_interface[i].sub_api != SUB_API_NOTSET)
-				available[priv->usb_interface[i].sub_api] = true;
-			break;
-		case USB_API_HID:
-			available[SUB_API_MAX] = true;
-			break;
-		default:
-			break;
-		}
-	}
-
-	for (i = 0; i < SUB_API_MAX; i++) { // WinUSB-like drivers
-		if (available[i]) {
-			r = usb_api_backend[USB_API_WINUSBX].open(i, dev_handle);
-			if (r != LIBUSB_SUCCESS)
-				return r;
-		}
-	}
-
-	if (available[SUB_API_MAX]) // HID driver
-		r = hid_open(SUB_API_NOTSET, dev_handle);
-
-	return r;
-}
-
-static void composite_close(int sub_api, struct libusb_device_handle *dev_handle)
-{
-	struct windows_device_priv *priv = _device_priv(dev_handle->dev);
-	uint8_t i;
-	// SUB_API_MAX + 1 as the SUB_API_MAX pos is used to indicate availability of HID
-	bool available[SUB_API_MAX + 1] = { 0 };
-
-	for (i = 0; i < USB_MAXINTERFACES; i++) {
-		switch (priv->usb_interface[i].apib->id) {
-		case USB_API_WINUSBX:
-			if (priv->usb_interface[i].sub_api != SUB_API_NOTSET)
-				available[priv->usb_interface[i].sub_api] = true;
-			break;
-		case USB_API_HID:
-			available[SUB_API_MAX] = true;
-			break;
-		default:
-			break;
-		}
-	}
-
-	for (i = 0; i < SUB_API_MAX; i++) { // WinUSB-like drivers
-		if (available[i])
-			usb_api_backend[USB_API_WINUSBX].close(i, dev_handle);
-	}
-
-	if (available[SUB_API_MAX]) // HID driver
-		hid_close(SUB_API_NOTSET, dev_handle);
-}
-
-static int composite_claim_interface(int sub_api, 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(priv->usb_interface[iface].sub_api, dev_handle, iface);
-}
-
-static int composite_set_interface_altsetting(int sub_api, 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(priv->usb_interface[iface].sub_api, dev_handle, iface, altsetting);
-}
-
-static int composite_release_interface(int sub_api, 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(priv->usb_interface[iface].sub_api, dev_handle, iface);
-}
-
-static int composite_submit_control_transfer(int sub_api, 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);
-	struct libusb_config_descriptor *conf_desc;
-	WINUSB_SETUP_PACKET *setup = (WINUSB_SETUP_PACKET *)transfer->buffer;
-	int iface, pass, r;
-
-	// Interface shouldn't matter for control, but it does in practice, with Windows'
-	// restrictions with regards to accessing HID keyboards and mice. Try to target
-	// a specific interface first, if possible.
-	switch (LIBUSB_REQ_RECIPIENT(setup->request_type)) {
-	case LIBUSB_RECIPIENT_INTERFACE:
-		iface = setup->index & 0xFF;
-		break;
-	case LIBUSB_RECIPIENT_ENDPOINT:
-		r = libusb_get_active_config_descriptor(transfer->dev_handle->dev, &conf_desc);
-		if (r == LIBUSB_SUCCESS) {
-			iface = get_interface_by_endpoint(conf_desc, (setup->index & 0xFF));
-			libusb_free_config_descriptor(conf_desc);
-			break;
-		}
-		// Fall through if not able to determine interface
-	default:
-		iface = -1;
-		break;
-	}
-
-	// Try and target a specific interface if the control setup indicates such
-	if ((iface >= 0) && (iface < USB_MAXINTERFACES)) {
-		usbi_dbg("attempting control transfer targeted to interface %d", iface);
-		if (priv->usb_interface[iface].path != NULL) {
-			r = priv->usb_interface[iface].apib->submit_control_transfer(priv->usb_interface[iface].sub_api, itransfer);
-			if (r == LIBUSB_SUCCESS)
-				return r;
-		}
-	}
-
-	// Either not targeted to a specific interface or no luck in doing so.
-	// Try a 2 pass approach with all interfaces.
-	for (pass = 0; pass < 2; pass++) {
-		for (iface = 0; iface < USB_MAXINTERFACES; iface++) {
-			if (priv->usb_interface[iface].path != NULL) {
-				if ((pass == 0) && (priv->usb_interface[iface].restricted_functionality)) {
-					usbi_dbg("trying to skip restricted interface #%d (HID keyboard or mouse?)", iface);
-					continue;
-				}
-				usbi_dbg("using interface %d", iface);
-				r = priv->usb_interface[iface].apib->submit_control_transfer(priv->usb_interface[iface].sub_api, itransfer);
-				// If not supported on this API, it may be supported on another, so don't give up yet!!
-				if (r == LIBUSB_ERROR_NOT_SUPPORTED)
-					continue;
-				return r;
-			}
-		}
-	}
-
-	usbi_err(ctx, "no libusb supported interfaces to complete request");
-	return LIBUSB_ERROR_NOT_FOUND;
-}
-
-static int composite_submit_bulk_transfer(int sub_api, 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(priv->usb_interface[current_interface].sub_api, itransfer);
-}
-
-static int composite_submit_iso_transfer(int sub_api, 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(priv->usb_interface[current_interface].sub_api, itransfer);
-}
-
-static int composite_clear_halt(int sub_api, 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(priv->usb_interface[current_interface].sub_api, dev_handle, endpoint);
-}
-
-static int composite_abort_control(int sub_api, struct usbi_transfer *itransfer)
-{
-	struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
-	struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
-	struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev);
-
-	return priv->usb_interface[transfer_priv->interface_number].apib->
-		abort_control(priv->usb_interface[transfer_priv->interface_number].sub_api, itransfer);
-}
-
-static int composite_abort_transfers(int sub_api, struct usbi_transfer *itransfer)
-{
-	struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
-	struct windows_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
-	struct windows_device_priv *priv = _device_priv(transfer->dev_handle->dev);
-
-	return priv->usb_interface[transfer_priv->interface_number].apib->
-		abort_transfers(priv->usb_interface[transfer_priv->interface_number].sub_api, itransfer);
-}
-
-static int composite_reset_device(int sub_api, struct libusb_device_handle *dev_handle)
-{
-	struct windows_device_priv *priv = _device_priv(dev_handle->dev);
-	int r;
-	uint8_t i;
-	bool available[SUB_API_MAX];
-
-	for (i = 0; i < SUB_API_MAX; i++)
-		available[i] = false;
-
-	for (i = 0; i < USB_MAXINTERFACES; i++) {
-		if ((priv->usb_interface[i].apib->id == USB_API_WINUSBX)
-				&& (priv->usb_interface[i].sub_api != SUB_API_NOTSET))
-			available[priv->usb_interface[i].sub_api] = true;
-	}
-
-	for (i = 0; i < SUB_API_MAX; i++) {
-		if (available[i]) {
-			r = usb_api_backend[USB_API_WINUSBX].reset_device(i, dev_handle);
-			if (r != LIBUSB_SUCCESS)
-				return r;
-		}
-	}
-
-	return LIBUSB_SUCCESS;
-}
-
-static int composite_copy_transfer_data(int sub_api, 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(priv->usb_interface[transfer_priv->interface_number].sub_api, itransfer, io_size);
-}
-
-#endif /* !USE_USBDK */

+ 0 - 1
vendor/github.com/karalabe/hid/libusb/libusb/version_nano.h

@@ -1 +0,0 @@
-#define LIBUSB_NANO 11182

+ 0 - 53
vendor/github.com/karalabe/hid/usb.go

@@ -1,53 +0,0 @@
-// hid - Gopher Interface Devices (USB HID)
-// Copyright (c) 2019 Péter Szilágyi, Guillaume Ballet. All rights reserved.
-//
-// This file is released under the 3-clause BSD license. Note however that Linux
-// support depends on libusb, released under GNU LGPL 2.1 or later.
-
-// Package usb provide interfaces for generic USB devices.
-package hid
-
-// DeviceType represents the type of a USB device (generic or HID)
-type DeviceType int
-
-// List of supported device types
-const (
-	DeviceTypeGeneric DeviceType = 0
-	DeviceTypeHID     DeviceType = 1
-)
-
-// Enumerate returns a list of all the HID devices attached to the system which
-// match the vendor and product id:
-//  - If the vendor id is set to 0 then any vendor matches.
-//  - If the product id is set to 0 then any product matches.
-//  - If the vendor and product id are both 0, all HID devices are returned.
-// func Enumerate(vendorID uint16, productID uint16) []DeviceInfo {
-// }
-
-// DeviceInfo is a generic libusb info interface
-type DeviceInfo interface {
-	// Type returns the type of the device (generic or HID)
-	Type() DeviceType
-
-	// Platform-specific device path
-	GetPath() string
-
-	// IDs returns the vendor and product IDs for the device,
-	// as well as the endpoint id and the usage page.
-	IDs() (uint16, uint16, int, uint16)
-
-	// Open tries to open the USB device represented by the current DeviceInfo
-	Open() (Device, error)
-}
-
-// Device is a generic libusb device interface
-type Device interface {
-	Close() error
-
-	Write(b []byte) (int, error)
-
-	Read(b []byte) (int, error)
-
-	// Type returns the type of the device (generic or HID)
-	Type() DeviceType
-}

+ 6 - 0
vendor/github.com/karalabe/usb/AUTHORS

@@ -0,0 +1,6 @@
+Felix Lange <fjl@twurst.com>
+Guillaume Ballet <gballet@gmail.com>
+Jakob Weisblat <jakobw@yubico.com>
+Mateusz Mikołajczyk <mikolajczyk.mateusz@gmail.com>
+Péter Szilágyi <peterke@gmail.com>
+Rosen Penev <rosenp@gmail.com>

+ 165 - 0
vendor/github.com/karalabe/usb/LICENSE

@@ -0,0 +1,165 @@
+                   GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+  This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+  0. Additional Definitions.
+
+  As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+  "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+  An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+  A "Combined Work" is a work produced by combining or linking an
+Application with the Library.  The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+  The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+  The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+  1. Exception to Section 3 of the GNU GPL.
+
+  You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+  2. Conveying Modified Versions.
+
+  If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+   a) under this License, provided that you make a good faith effort to
+   ensure that, in the event an Application does not supply the
+   function or data, the facility still operates, and performs
+   whatever part of its purpose remains meaningful, or
+
+   b) under the GNU GPL, with none of the additional permissions of
+   this License applicable to that copy.
+
+  3. Object Code Incorporating Material from Library Header Files.
+
+  The object code form of an Application may incorporate material from
+a header file that is part of the Library.  You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+   a) Give prominent notice with each copy of the object code that the
+   Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the object code with a copy of the GNU GPL and this license
+   document.
+
+  4. Combined Works.
+
+  You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+   a) Give prominent notice with each copy of the Combined Work that
+   the Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the Combined Work with a copy of the GNU GPL and this license
+   document.
+
+   c) For a Combined Work that displays copyright notices during
+   execution, include the copyright notice for the Library among
+   these notices, as well as a reference directing the user to the
+   copies of the GNU GPL and this license document.
+
+   d) Do one of the following:
+
+       0) Convey the Minimal Corresponding Source under the terms of this
+       License, and the Corresponding Application Code in a form
+       suitable for, and under terms that permit, the user to
+       recombine or relink the Application with a modified version of
+       the Linked Version to produce a modified Combined Work, in the
+       manner specified by section 6 of the GNU GPL for conveying
+       Corresponding Source.
+
+       1) Use a suitable shared library mechanism for linking with the
+       Library.  A suitable mechanism is one that (a) uses at run time
+       a copy of the Library already present on the user's computer
+       system, and (b) will operate properly with a modified version
+       of the Library that is interface-compatible with the Linked
+       Version.
+
+   e) Provide Installation Information, but only if you would otherwise
+   be required to provide such information under section 6 of the
+   GNU GPL, and only to the extent that such information is
+   necessary to install and execute a modified version of the
+   Combined Work produced by recombining or relinking the
+   Application with a modified version of the Linked Version. (If
+   you use option 4d0, the Installation Information must accompany
+   the Minimal Corresponding Source and Corresponding Application
+   Code. If you use option 4d1, you must provide the Installation
+   Information in the manner specified by section 6 of the GNU GPL
+   for conveying Corresponding Source.)
+
+  5. Combined Libraries.
+
+  You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+   a) Accompany the combined library with a copy of the same work based
+   on the Library, uncombined with any other library facilities,
+   conveyed under the terms of this License.
+
+   b) Give prominent notice with the combined library that part of it
+   is a work based on the Library, and explaining where to find the
+   accompanying uncombined form of the same work.
+
+  6. Revised Versions of the GNU Lesser General Public License.
+
+  The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+  Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+  If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.

+ 47 - 0
vendor/github.com/karalabe/usb/README.md

@@ -0,0 +1,47 @@
+[![Travis][travisimg]][travisurl]
+[![AppVeyor][appveyorimg]][appveyorurl]
+[![GoDoc][docimg]][docurl]
+
+[travisimg]:   https://travis-ci.org/karalabe/usb.svg?branch=master
+[travisurl]:   https://travis-ci.org/karalabe/usb
+[appveyorimg]: https://ci.appveyor.com/api/projects/status/u96eq262bj2itprh/branch/master?svg=true
+[appveyorurl]: https://ci.appveyor.com/project/karalabe/usb
+[docimg]:      https://godoc.org/github.com/karalabe/usb?status.svg
+[docurl]:      https://godoc.org/github.com/karalabe/usb
+
+# Yet another USB library for Go
+
+The `usb` package is a cross platform, fully self-contained library for accessing and communicating with USB devices **either via HID or low level interrupts**. The goal of the library was to create a simple way to find-, attach to- and read/write form USB devices.
+
+There are multiple already existing USB libraries:
+
+ * The original `gousb` package [created by @kylelemons](https://github.com/kylelemons/gousb) and nowadays [maintained by @google](https://github.com/google/gousb) is a CGO wrapper around `libusb`. It is the most advanced USB library for Go out there.
+   * Unfortunately, `gousb` requires the `libusb` C library to be installed both during build as well as during runtime on the host operating system. This breaks binary portability and also adds unnecessary hurdles on Windows.
+   * Furthermore, whilst HID devices are supported by `libusb`, the OS on Macos and Windows explicitly takes over these devices, so only native system calls can be used on recent versions (i.e. you **cannot** use `libusb` for HID).
+ * There is a fork of `gousb` [created by @karalabe](https://github.com/karalabe/gousb) that statically linked `libusb` during build, but with the lack of HID access, that work was abandoned.
+ * For HID-only devices, a previous self-contained package was created at [`github.com/karalabe/hid`](https://github.com/karalabe/hid), which worked well for hardware wallet uses cases in [`go-ethereum`](https://github.com/ethereum/go-ethereum). It's a simple package that does it's thing well.
+   * Unfortunately, `hid` is not capable of talking to generic USB devices. When multiple different devices are needed, eventually some will not support the HID spec (e.g. WebUSB). Pulling in both `hid` and `gousb` will break down due to both depending internally on different versions of `libusb` on Linux.
+
+This `usb` package is a proper integration of `hidapi` and `libusb` so that communication with HID devices is done via system calls, whereas communication with lower level USB devices is done via interrupts. All this detail is hidden away behind a tiny interface.
+
+The package supports Linux, macOS, Windows and FreeBSD. Exclude constraints are also specified for Android and iOS to allow smoother vendoring into cross platform projects.
+
+## Cross-compiling
+
+Using `go get`, the embedded C library is compiled into the binary format of your host OS. Cross compiling to a different platform or architecture entails disabling CGO by default in Go, causing device enumeration `hid.Enumerate()` to yield no results.
+
+To cross compile a functional version of this library, you'll need to enable CGO during cross compilation via `CGO_ENABLED=1` and you'll need to install and set a cross compilation enabled C toolkit via `CC=your-cross-gcc`.
+
+## Acknowledgements
+
+Although the `usb` package is an implementation from scratch, HID support was heavily inspired by the existing [`go.hid`](https://github.com/GeertJohan/go.hid) library, which seems abandoned since 2015; is incompatible with Go 1.6+; and has various external dependencies.
+
+Wide character support in the HID support is done via the [`gowchar`](https://github.com/orofarne/gowchar) library, unmaintained since 2013; non buildable with a modern Go release and failing `go vet` checks. As such, `gowchar` was also vendored in inline.
+
+Error handling for the `libusb` integration originates from the [`gousb`](https://github.com/google/gousb) library.
+
+## License
+
+This USB library is licensed under the [GNU Lesser General Public License v3.0](https://www.gnu.org/licenses/lgpl-3.0.en.html) (dictated by libusb).
+
+If you are only interested in Human Interface devices, a less restrictive package can be found at [`github.com/karalabe/hid`](https://github.com/karalabe/hid).

+ 3 - 3
vendor/github.com/karalabe/hid/appveyor.yml → vendor/github.com/karalabe/usb/appveyor.yml

@@ -1,7 +1,7 @@
 os: Visual Studio 2015
 
 # Clone directly into GOPATH.
-clone_folder: C:\gopath\src\github.com\karalabe\hid
+clone_folder: C:\gopath\src\github.com\karalabe\usb
 clone_depth: 1
 version: "{branch}.{build}"
 environment:
@@ -22,8 +22,8 @@ environment:
 
 install:
   - rmdir C:\go /s /q
-  - appveyor DownloadFile https://storage.googleapis.com/golang/go1.10.1.windows-%GOARCH%.zip
-  - 7z x go1.10.1.windows-%GOARCH%.zip -y -oC:\ > NUL
+  - appveyor DownloadFile https://storage.googleapis.com/golang/go1.12.5.windows-%GOARCH%.zip
+  - 7z x go1.12.5.windows-%GOARCH%.zip -y -oC:\ > NUL
   - go version
   - gcc --version
 

+ 76 - 0
vendor/github.com/karalabe/usb/demo.go

@@ -0,0 +1,76 @@
+// usb - Self contained USB and HID library for Go
+// Copyright 2019 The library Authors
+//
+// 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 3 of the License, or (at your option) any
+// later version.
+//
+// The 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 the library. If not, see <http://www.gnu.org/licenses/>.
+
+// +build none
+
+package main
+
+import (
+	"fmt"
+	"strings"
+
+	"github.com/karalabe/usb"
+)
+
+func main() {
+	// Enumerate all the HID devices in alphabetical path order
+	hids, err := usb.EnumerateHid(0, 0)
+	if err != nil {
+		panic(err)
+	}
+	for i := 0; i < len(hids); i++ {
+		for j := i + 1; j < len(hids); j++ {
+			if hids[i].Path > hids[j].Path {
+				hids[i], hids[j] = hids[j], hids[i]
+			}
+		}
+	}
+	for i, hid := range hids {
+		fmt.Println(strings.Repeat("-", 128))
+		fmt.Printf("HID #%d\n", i)
+		fmt.Printf("  OS Path:      %s\n", hid.Path)
+		fmt.Printf("  Vendor ID:    %#04x\n", hid.VendorID)
+		fmt.Printf("  Product ID:   %#04x\n", hid.ProductID)
+		fmt.Printf("  Release:      %d\n", hid.Release)
+		fmt.Printf("  Serial:       %s\n", hid.Serial)
+		fmt.Printf("  Manufacturer: %s\n", hid.Manufacturer)
+		fmt.Printf("  Product:      %s\n", hid.Product)
+		fmt.Printf("  Usage Page:   %d\n", hid.UsagePage)
+		fmt.Printf("  Usage:        %d\n", hid.Usage)
+		fmt.Printf("  Interface:    %d\n", hid.Interface)
+	}
+	fmt.Println(strings.Repeat("=", 128))
+
+	// Enumerate all the non-HID devices in alphabetical path order
+	raws, err := usb.EnumerateRaw(0, 0)
+	if err != nil {
+		panic(err)
+	}
+	for i := 0; i < len(raws); i++ {
+		for j := i + 1; j < len(raws); j++ {
+			if raws[i].Path > raws[j].Path {
+				raws[i], raws[j] = raws[j], raws[i]
+			}
+		}
+	}
+	for i, raw := range raws {
+		fmt.Printf("RAW #%d\n", i)
+		fmt.Printf("  OS Path:    %s\n", raw.Path)
+		fmt.Printf("  Vendor ID:  %#04x\n", raw.VendorID)
+		fmt.Printf("  Product ID: %#04x\n", raw.ProductID)
+		fmt.Printf("  Interface:  %d\n", raw.Interface)
+		fmt.Println(strings.Repeat("-", 128))
+	}
+}

+ 3 - 0
vendor/github.com/karalabe/usb/go.mod

@@ -0,0 +1,3 @@
+module github.com/karalabe/usb
+
+go 1.12

+ 42 - 0
vendor/github.com/karalabe/usb/hid_disabled.go

@@ -0,0 +1,42 @@
+// usb - Self contained USB and HID library for Go
+// Copyright 2017 The library Authors
+//
+// 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 3 of the License, or (at your option) any
+// later version.
+//
+// The 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 the library. If not, see <http://www.gnu.org/licenses/>.
+
+// +build !freebsd,!linux,!darwin,!windows ios !cgo
+
+package usb
+
+// HidDevice is a live HID USB connected device handle. On platforms that this file
+// implements, the type lacks the actual HID device and all methods are noop.
+type HidDevice struct {
+	DeviceInfo // Embed the infos for easier access
+}
+
+// Close releases the HID USB device handle. On platforms that this file implements,
+// the method is just a noop.
+func (dev *HidDevice) Close() error {
+	return ErrUnsupportedPlatform
+}
+
+// Write sends an output report to a HID device. On platforms that this file
+// implements, the method just returns an error.
+func (dev *HidDevice) Write(b []byte) (int, error) {
+	return 0, ErrUnsupportedPlatform
+}
+
+// Read retrieves an input report from a HID device. On platforms that this file
+// implements, the method just returns an error.
+func (dev *HidDevice) Read(b []byte) (int, error) {
+	return 0, ErrUnsupportedPlatform
+}

+ 187 - 0
vendor/github.com/karalabe/usb/hid_enabled.go

@@ -0,0 +1,187 @@
+// usb - Self contained USB and HID library for Go
+// Copyright 2017 The library Authors
+//
+// 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 3 of the License, or (at your option) any
+// later version.
+//
+// The 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 the library. If not, see <http://www.gnu.org/licenses/>.
+
+// +build freebsd,cgo linux,cgo darwin,!ios,cgo windows,cgo
+
+package usb
+
+/*
+#include <stdlib.h>
+#include "./hidapi/hidapi/hidapi.h"
+*/
+import "C"
+
+import (
+	"errors"
+	"runtime"
+	"sync"
+	"unsafe"
+)
+
+// enumerateHid returns a list of all the HID devices attached to the system which
+// match the vendor and product id:
+//  - If the vendor id is set to 0 then any vendor matches.
+//  - If the product id is set to 0 then any product matches.
+//  - If the vendor and product id are both 0, all HID devices are returned.
+func enumerateHid(vendorID uint16, productID uint16) ([]DeviceInfo, error) {
+	// Gather all device infos and ensure they are freed before returning
+	head := C.hid_enumerate(C.ushort(vendorID), C.ushort(productID))
+	if head == nil {
+		return nil, nil
+	}
+	defer C.hid_free_enumeration(head)
+
+	// Iterate the list and retrieve the device details
+	var infos []DeviceInfo
+	for ; head != nil; head = head.next {
+		info := DeviceInfo{
+			Path:      C.GoString(head.path),
+			VendorID:  uint16(head.vendor_id),
+			ProductID: uint16(head.product_id),
+			Release:   uint16(head.release_number),
+			UsagePage: uint16(head.usage_page),
+			Usage:     uint16(head.usage),
+			Interface: int(head.interface_number),
+		}
+		if head.serial_number != nil {
+			info.Serial, _ = wcharTToString(head.serial_number)
+		}
+		if head.product_string != nil {
+			info.Product, _ = wcharTToString(head.product_string)
+		}
+		if head.manufacturer_string != nil {
+			info.Manufacturer, _ = wcharTToString(head.manufacturer_string)
+		}
+		infos = append(infos, info)
+	}
+	return infos, nil
+}
+
+// openHid connects to an HID device by its path name.
+func openHid(info DeviceInfo) (*hidDevice, error) {
+	path := C.CString(info.Path)
+	defer C.free(unsafe.Pointer(path))
+
+	device := C.hid_open_path(path)
+	if device == nil {
+		return nil, errors.New("hidapi: failed to open device")
+	}
+	return &hidDevice{
+		DeviceInfo: info,
+		device:     device,
+	}, nil
+}
+
+// hidDevice is a live HID USB connected device handle.
+type hidDevice struct {
+	DeviceInfo // Embed the infos for easier access
+
+	device *C.hid_device // Low level HID device to communicate through
+	lock   sync.Mutex
+}
+
+// Close releases the HID USB device handle.
+func (dev *hidDevice) Close() error {
+	dev.lock.Lock()
+	defer dev.lock.Unlock()
+
+	if dev.device != nil {
+		C.hid_close(dev.device)
+		dev.device = nil
+	}
+	return nil
+}
+
+// Write sends an output report to a HID device.
+//
+// Write will send the data on the first OUT endpoint, if one exists. If it does
+// not, it will send the data through the Control Endpoint (Endpoint 0).
+func (dev *hidDevice) Write(b []byte) (int, error) {
+	// Abort if nothing to write
+	if len(b) == 0 {
+		return 0, nil
+	}
+	// Abort if device closed in between
+	dev.lock.Lock()
+	device := dev.device
+	dev.lock.Unlock()
+
+	if device == nil {
+		return 0, ErrDeviceClosed
+	}
+	// Prepend a HID report ID on Windows, other OSes don't need it
+	var report []byte
+	if runtime.GOOS == "windows" {
+		report = append([]byte{0x00}, b...)
+	} else {
+		report = b
+	}
+	// Execute the write operation
+	written := int(C.hid_write(device, (*C.uchar)(&report[0]), C.size_t(len(report))))
+	if written == -1 {
+		// If the write failed, verify if closed or other error
+		dev.lock.Lock()
+		device = dev.device
+		dev.lock.Unlock()
+
+		if device == nil {
+			return 0, ErrDeviceClosed
+		}
+		// Device not closed, some other error occurred
+		message := C.hid_error(device)
+		if message == nil {
+			return 0, errors.New("hidapi: unknown failure")
+		}
+		failure, _ := wcharTToString(message)
+		return 0, errors.New("hidapi: " + failure)
+	}
+	return written, nil
+}
+
+// Read retrieves an input report from a HID device.
+func (dev *hidDevice) Read(b []byte) (int, error) {
+	// Aborth if nothing to read
+	if len(b) == 0 {
+		return 0, nil
+	}
+	// Abort if device closed in between
+	dev.lock.Lock()
+	device := dev.device
+	dev.lock.Unlock()
+
+	if device == nil {
+		return 0, ErrDeviceClosed
+	}
+	// Execute the read operation
+	read := int(C.hid_read(device, (*C.uchar)(&b[0]), C.size_t(len(b))))
+	if read == -1 {
+		// If the read failed, verify if closed or other error
+		dev.lock.Lock()
+		device = dev.device
+		dev.lock.Unlock()
+
+		if device == nil {
+			return 0, ErrDeviceClosed
+		}
+		// Device not closed, some other error occurred
+		message := C.hid_error(device)
+		if message == nil {
+			return 0, errors.New("hidapi: unknown failure")
+		}
+		failure, _ := wcharTToString(message)
+		return 0, errors.New("hidapi: " + failure)
+	}
+	return read, nil
+}

+ 0 - 0
vendor/github.com/karalabe/hid/hidapi/AUTHORS.txt → vendor/github.com/karalabe/usb/hidapi/AUTHORS.txt


+ 0 - 0
vendor/github.com/karalabe/hid/hidapi/LICENSE-bsd.txt → vendor/github.com/karalabe/usb/hidapi/LICENSE-bsd.txt


+ 0 - 0
vendor/github.com/karalabe/hid/hidapi/LICENSE-gpl3.txt → vendor/github.com/karalabe/usb/hidapi/LICENSE-gpl3.txt


+ 0 - 0
vendor/github.com/karalabe/hid/hidapi/LICENSE-orig.txt → vendor/github.com/karalabe/usb/hidapi/LICENSE-orig.txt


+ 0 - 0
vendor/github.com/karalabe/hid/hidapi/LICENSE.txt → vendor/github.com/karalabe/usb/hidapi/LICENSE.txt


+ 0 - 0
vendor/github.com/karalabe/hid/hidapi/README.txt → vendor/github.com/karalabe/usb/hidapi/README.txt


+ 0 - 0
vendor/github.com/karalabe/hid/hidapi/hidapi/hidapi.h → vendor/github.com/karalabe/usb/hidapi/hidapi/hidapi.h


+ 0 - 0
vendor/github.com/karalabe/hid/hidapi/libusb/hid.c → vendor/github.com/karalabe/usb/hidapi/libusb/hid.c


+ 0 - 0
vendor/github.com/karalabe/hid/hidapi/mac/hid.c → vendor/github.com/karalabe/usb/hidapi/mac/hid.c


+ 0 - 0
vendor/github.com/karalabe/hid/hidapi/windows/hid.c → vendor/github.com/karalabe/usb/hidapi/windows/hid.c


+ 74 - 0
vendor/github.com/karalabe/usb/libs.go

@@ -0,0 +1,74 @@
+// usb - Self contained USB and HID library for Go
+// Copyright 2019 The library Authors
+//
+// 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 3 of the License, or (at your option) any
+// later version.
+//
+// The 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 the library. If not, see <http://www.gnu.org/licenses/>.
+
+// +build freebsd,cgo linux,cgo darwin,!ios,cgo windows,cgo
+
+package usb
+
+/*
+#cgo CFLAGS: -I./hidapi/hidapi
+#cgo CFLAGS: -I./libusb/libusb
+#cgo CFLAGS: -DDEFAULT_VISIBILITY=""
+#cgo CFLAGS: -DPOLL_NFDS_TYPE=int
+
+#cgo linux CFLAGS: -DOS_LINUX -D_GNU_SOURCE
+#cgo linux,!android LDFLAGS: -lrt
+#cgo darwin CFLAGS: -DOS_DARWIN -DHAVE_SYS_TIME_H
+#cgo darwin LDFLAGS: -framework CoreFoundation -framework IOKit -lobjc
+#cgo windows CFLAGS: -DOS_WINDOWS
+#cgo windows LDFLAGS: -lsetupapi
+#cgo freebsd CFLAGS: -DOS_FREEBSD
+#cgo freebsd LDFLAGS: -lusb
+#cgo openbsd CFLAGS: -DOS_OPENBSD
+
+#if defined(OS_LINUX) || defined(OS_DARWIN) || defined(DOS_FREEBSD) || defined(OS_OPENBSD)
+	#include <poll.h>
+	#include "os/threads_posix.c"
+	#include "os/poll_posix.c"
+#elif defined(OS_WINDOWS)
+	#include "os/poll_windows.c"
+	#include "os/threads_windows.c"
+#endif
+
+#ifdef OS_LINUX
+	#include "os/linux_usbfs.c"
+	#include "os/linux_netlink.c"
+	#include "hidapi/libusb/hid.c"
+#elif OS_DARWIN
+	#include "os/darwin_usb.c"
+	#include "hidapi/mac/hid.c"
+#elif OS_WINDOWS
+	#include "os/windows_nt_common.c"
+	#include "os/windows_usbdk.c"
+	#include "os/windows_winusb.c"
+	#include "hidapi/windows/hid.c"
+#elif OS_FREEBSD
+	#include <libusb.h>
+	#include "hidapi/libusb/hid.c"
+#elif DOS_OPENBSD
+	#include "os/openbsd_usb.c"
+	#include "hidapi/libusb/hid.c"
+#endif
+
+#ifndef OS_FREEBSD
+	#include "core.c"
+	#include "descriptor.c"
+	#include "hotplug.c"
+	#include "io.c"
+	#include "strerror.c"
+	#include "sync.c"
+#endif
+*/
+import "C"

+ 32 - 2
vendor/github.com/karalabe/hid/libusb/AUTHORS → vendor/github.com/karalabe/usb/libusb/AUTHORS

@@ -8,14 +8,19 @@ Copyright © 2010-2012 Michael Plante <michael.plante@gmail.com>
 Copyright © 2011-2013 Hans de Goede <hdegoede@redhat.com>
 Copyright © 2012-2013 Martin Pieuchot <mpi@openbsd.org>
 Copyright © 2012-2013 Toby Gray <toby.gray@realvnc.com>
-Copyright © 2013-2015 Chris Dickens <christopher.a.dickens@gmail.com>
+Copyright © 2013-2018 Chris Dickens <christopher.a.dickens@gmail.com>
 
 Other contributors:
+Adrian Bunk
 Akshay Jaggi
 Alan Ott
 Alan Stern
 Alex Vatchenko
 Andrew Fernandes
+Andy Chunyu
+Andy McFadden
+Angus Gratton
+Anil Nair
 Anthony Clay
 Antonio Ospite
 Artem Egorkine
@@ -23,12 +28,17 @@ Aurelien Jarno
 Bastien Nocera
 Bei Zhang
 Benjamin Dobell
+Brent Rector
 Carl Karsten
+Christophe Zeitouny
 Colin Walters
 Dave Camarillo
 David Engraf
 David Moore
 Davidlohr Bueso
+Dmitry Fleytman
+Doug Johnston
+Evan Hunter
 Federico Manzan
 Felipe Balbi
 Florian Albrechtskirchinger
@@ -41,23 +51,34 @@ Hans Ulrich Niedermann
 Hector Martin
 Hoi-Ho Chan
 Ilya Konstantinov
+Jakub Klama
 James Hanko
+Jeffrey Nichols
+Johann Richard
 John Sheu
+Jonathon Jongsma
+Joost Muller
+Josh Gao
 Joshua Blake
 Justin Bischoff
+KIMURA Masaru
 Karsten Koenig
 Konrad Rzepecki
 Kuangye Guo
 Lars Kanis
 Lars Wirzenius
+Lei Chen
 Luca Longinotti
 Marcus Meissner
 Markus Heidelberg
 Martin Ettl
 Martin Koegler
+Matthew Stapleton
 Matthias Bolte
+Michel Zou
 Mike Frysinger
 Mikhail Gusarov
+Morgan Leborgne
 Moritz Fischer
 Ларионов Даниил
 Nicholas Corgan
@@ -66,10 +87,17 @@ Orin Eman
 Paul Fertser
 Pekka Nikander
 Rob Walker
+Romain Vimont
+Roman Kalashnikov
+Sameeh Jubran
 Sean McBride
 Sebastian Pipping
+Sergey Serb
 Simon Haggett
 Simon Newton
+Stefan Agner
+Stefan Tauner
+Steinar H. Gunderson
 Thomas Röfer
 Tim Hutt
 Tim Roberts
@@ -81,9 +109,11 @@ Uri Lublin
 Vasily Khoruzhick
 Vegard Storheil Eriksen
 Venkatesh Shukla
+Vianney le Clément de Saint-Marcq
+Victor Toso
 Vitali Lovich
+William Skellenger
 Xiaofan Chen
 Zoltán Kovács
 Роман Донченко
 parafin
-xantares

+ 0 - 0
vendor/github.com/karalabe/hid/libusb/COPYING → vendor/github.com/karalabe/usb/libusb/COPYING


+ 0 - 0
vendor/github.com/karalabe/hid/libusb/libusb/config.h → vendor/github.com/karalabe/usb/libusb/libusb/config.h


+ 201 - 145
vendor/github.com/karalabe/hid/libusb/libusb/core.c → vendor/github.com/karalabe/usb/libusb/libusb/core.c

@@ -44,32 +44,6 @@
 #include "libusbi.h"
 #include "hotplug.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_OPENBSD)
-const struct usbi_os_backend * const usbi_backend = &openbsd_backend;
-#elif defined(OS_NETBSD)
-const struct usbi_os_backend * const usbi_backend = &netbsd_backend;
-#elif defined(OS_WINDOWS)
-
-#if defined(USE_USBDK)
-const struct usbi_os_backend * const usbi_backend = &usbdk_backend;
-#else
-const struct usbi_os_backend * const usbi_backend = &windows_backend;
-#endif
-
-#elif defined(OS_WINCE)
-const struct usbi_os_backend * const usbi_backend = &wince_backend;
-#elif defined(OS_HAIKU)
-const struct usbi_os_backend * const usbi_backend = &haiku_usb_raw_backend;
-#elif defined (OS_SUNOS)
-const struct usbi_os_backend * const usbi_backend = &sunos_backend;
-#else
-#error "Unsupported OS"
-#endif
-
 struct libusb_context *usbi_default_context = NULL;
 static const struct libusb_version libusb_version_internal =
 	{ LIBUSB_MAJOR, LIBUSB_MINOR, LIBUSB_MICRO, LIBUSB_NANO,
@@ -142,15 +116,17 @@ struct list_head active_contexts_list;
  * libusb uses stderr for all logging. By default, logging is set to NONE,
  * which means that no output will be produced. However, unless the library
  * has been compiled with logging disabled, then any application calls to
- * libusb_set_debug(), or the setting of the environmental variable
- * LIBUSB_DEBUG outside of the application, can result in logging being
- * produced. Your application should therefore not close stderr, but instead
- * direct it to the null device if its output is undesirable.
- *
- * The libusb_set_debug() function can be used to enable logging of certain
- * messages. Under standard configuration, libusb doesn't really log much
- * so you are advised to use this function to enable all error/warning/
- * informational messages. It will help debug problems with your software.
+ * libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, level), or the setting of the
+ * environmental variable LIBUSB_DEBUG outside of the application, can result
+ * in logging being produced. Your application should therefore not close
+ * stderr, but instead direct it to the null device if its output is
+ * undesirable.
+ *
+ * The libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, level) function can be
+ * used to enable logging of certain messages. Under standard configuration,
+ * libusb doesn't really log much so you are advised to use this function
+ * to enable all error/warning/ informational messages. It will help debug
+ * problems with your software.
  *
  * The logged messages are unstructured. There is no one-to-one correspondence
  * between messages being logged and success or failure return codes from
@@ -165,18 +141,20 @@ struct list_head active_contexts_list;
  *
  * The LIBUSB_DEBUG environment variable can be used to enable message logging
  * at run-time. This environment variable should be set to a log level number,
- * which is interpreted the same as the libusb_set_debug() parameter. When this
+ * which is interpreted the same as the
+ * libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, level) parameter. When this
  * environment variable is set, the message logging verbosity level is fixed
- * and libusb_set_debug() effectively does nothing.
+ * and libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, level) effectively does
+ * nothing.
  *
  * libusb can be compiled without any logging functions, useful for embedded
- * systems. In this case, libusb_set_debug() and the LIBUSB_DEBUG environment
- * variable have no effects.
+ * systems. In this case, libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, level)
+ * and the LIBUSB_DEBUG environment variable have no effects.
  *
  * libusb can also be compiled with verbose debugging messages always. When
  * the library is compiled in this way, all messages of all verbosities are
- * always logged. libusb_set_debug() and the LIBUSB_DEBUG environment variable
- * have no effects.
+ * always logged. libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, level) and
+ * the LIBUSB_DEBUG environment variable have no effects.
  *
  * \section remarks Other remarks
  *
@@ -187,6 +165,20 @@ struct list_head active_contexts_list;
 /**
  * \page libusb_caveats Caveats
  *
+ * \section fork Fork considerations
+ *
+ * libusb is <em>not</em> designed to work across fork() calls. Depending on
+ * the platform, there may be resources in the parent process that are not
+ * available to the child (e.g. the hotplug monitor thread on Linux). In
+ * addition, since the parent and child will share libusb's internal file
+ * descriptors, using libusb in any way from the child could cause the parent
+ * process's \ref libusb_context to get into an inconsistent state.
+ *
+ * On Linux, libusb's file descriptors will be marked as CLOEXEC, which means
+ * that it is safe to fork() and exec() without worrying about the child
+ * process needing to clean up state or having access to these file descriptors.
+ * Other platforms may not be so forgiving, so consider yourself warned!
+ *
  * \section devresets Device resets
  *
  * The libusb_reset_device() function allows you to reset a device. If your
@@ -291,7 +283,6 @@ if (cfg != desired)
  * information about the end of the short packet, and the user probably wanted
  * that surplus data to arrive in the next logical transfer.
  *
- *
  * \section zlp Zero length packets
  *
  * - libusb is able to send a packet of zero length to an endpoint simply by
@@ -310,7 +301,7 @@ if (cfg != desired)
  * developed modules may both use libusb.
  *
  * libusb is written to allow for these multiple user scenarios. The two
- * "instances" of libusb will not interfere: libusb_set_debug() calls
+ * "instances" of libusb will not interfere: libusb_set_option() calls
  * from one user will not affect the same settings for other users, other
  * users can continue using libusb after one of them calls libusb_exit(), etc.
  *
@@ -435,6 +426,7 @@ if (cfg != desired)
   * - libusb_set_debug()
   * - libusb_set_interface_alt_setting()
   * - libusb_set_iso_packet_lengths()
+  * - libusb_set_option()
   * - libusb_setlocale()
   * - libusb_set_pollfd_notifiers()
   * - libusb_strerror()
@@ -478,6 +470,7 @@ if (cfg != desired)
   * - \ref libusb_iso_sync_type
   * - \ref libusb_iso_usage_type
   * - \ref libusb_log_level
+  * - \ref libusb_option
   * - \ref libusb_request_recipient
   * - \ref libusb_request_type
   * - \ref libusb_speed
@@ -680,7 +673,7 @@ struct discovered_devs *discovered_devs_append(
 struct libusb_device *usbi_alloc_device(struct libusb_context *ctx,
 	unsigned long session_id)
 {
-	size_t priv_size = usbi_backend->device_priv_size;
+	size_t priv_size = usbi_backend.device_priv_size;
 	struct libusb_device *dev = calloc(1, sizeof(*dev) + priv_size);
 	int r;
 
@@ -824,8 +817,8 @@ ssize_t API_EXPORTED libusb_get_device_list(libusb_context *ctx,
 		/* backend provides hotplug support */
 		struct libusb_device *dev;
 
-		if (usbi_backend->hotplug_poll)
-			usbi_backend->hotplug_poll();
+		if (usbi_backend.hotplug_poll)
+			usbi_backend.hotplug_poll();
 
 		usbi_mutex_lock(&ctx->usb_devs_lock);
 		list_for_each_entry(dev, &ctx->usb_devs, list, struct libusb_device) {
@@ -839,7 +832,7 @@ ssize_t API_EXPORTED libusb_get_device_list(libusb_context *ctx,
 		usbi_mutex_unlock(&ctx->usb_devs_lock);
 	} else {
 		/* backend does not provide hotplug support */
-		r = usbi_backend->get_device_list(ctx, &discdevs);
+		r = usbi_backend.get_device_list(ctx, &discdevs);
 	}
 
 	if (r < 0) {
@@ -1167,8 +1160,8 @@ void API_EXPORTED libusb_unref_device(libusb_device *dev)
 
 		libusb_unref_device(dev->parent_dev);
 
-		if (usbi_backend->destroy_device)
-			usbi_backend->destroy_device(dev);
+		if (usbi_backend.destroy_device)
+			usbi_backend.destroy_device(dev);
 
 		if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
 			/* backend does not support hotplug */
@@ -1242,7 +1235,7 @@ int API_EXPORTED libusb_open(libusb_device *dev,
 {
 	struct libusb_context *ctx = DEVICE_CTX(dev);
 	struct libusb_device_handle *_dev_handle;
-	size_t priv_size = usbi_backend->device_handle_priv_size;
+	size_t priv_size = usbi_backend.device_handle_priv_size;
 	int r;
 	usbi_dbg("open %d.%d", dev->bus_number, dev->device_address);
 
@@ -1265,7 +1258,7 @@ int API_EXPORTED libusb_open(libusb_device *dev,
 	_dev_handle->claimed_interfaces = 0;
 	memset(&_dev_handle->os_priv, 0, priv_size);
 
-	r = usbi_backend->open(_dev_handle);
+	r = usbi_backend.open(_dev_handle);
 	if (r < 0) {
 		usbi_dbg("open %d.%d returns %d", dev->bus_number, dev->device_address, r);
 		libusb_unref_device(dev);
@@ -1382,7 +1375,7 @@ static void do_close(struct libusb_context *ctx,
 	list_del(&dev_handle->list);
 	usbi_mutex_unlock(&ctx->open_devs_lock);
 
-	usbi_backend->close(dev_handle);
+	usbi_backend.close(dev_handle);
 	libusb_unref_device(dev_handle->dev);
 	usbi_mutex_destroy(&dev_handle->lock);
 	free(dev_handle);
@@ -1491,8 +1484,8 @@ int API_EXPORTED libusb_get_configuration(libusb_device_handle *dev_handle,
 	int r = LIBUSB_ERROR_NOT_SUPPORTED;
 
 	usbi_dbg("");
-	if (usbi_backend->get_configuration)
-		r = usbi_backend->get_configuration(dev_handle, config);
+	if (usbi_backend.get_configuration)
+		r = usbi_backend.get_configuration(dev_handle, config);
 
 	if (r == LIBUSB_ERROR_NOT_SUPPORTED) {
 		uint8_t tmp = 0;
@@ -1567,7 +1560,7 @@ int API_EXPORTED libusb_set_configuration(libusb_device_handle *dev_handle,
 	int configuration)
 {
 	usbi_dbg("configuration %d", configuration);
-	return usbi_backend->set_configuration(dev_handle, configuration);
+	return usbi_backend.set_configuration(dev_handle, configuration);
 }
 
 /** \ingroup libusb_dev
@@ -1614,7 +1607,7 @@ int API_EXPORTED libusb_claim_interface(libusb_device_handle *dev_handle,
 	if (dev_handle->claimed_interfaces & (1 << interface_number))
 		goto out;
 
-	r = usbi_backend->claim_interface(dev_handle, interface_number);
+	r = usbi_backend.claim_interface(dev_handle, interface_number);
 	if (r == 0)
 		dev_handle->claimed_interfaces |= 1 << interface_number;
 
@@ -1657,7 +1650,7 @@ int API_EXPORTED libusb_release_interface(libusb_device_handle *dev_handle,
 		goto out;
 	}
 
-	r = usbi_backend->release_interface(dev_handle, interface_number);
+	r = usbi_backend.release_interface(dev_handle, interface_number);
 	if (r == 0)
 		dev_handle->claimed_interfaces &= ~(1 << interface_number);
 
@@ -1707,7 +1700,7 @@ int API_EXPORTED libusb_set_interface_alt_setting(libusb_device_handle *dev_hand
 	}
 	usbi_mutex_unlock(&dev_handle->lock);
 
-	return usbi_backend->set_interface_altsetting(dev_handle, interface_number,
+	return usbi_backend.set_interface_altsetting(dev_handle, interface_number,
 		alternate_setting);
 }
 
@@ -1734,7 +1727,7 @@ int API_EXPORTED libusb_clear_halt(libusb_device_handle *dev_handle,
 	if (!dev_handle->dev->attached)
 		return LIBUSB_ERROR_NO_DEVICE;
 
-	return usbi_backend->clear_halt(dev_handle, endpoint);
+	return usbi_backend.clear_halt(dev_handle, endpoint);
 }
 
 /** \ingroup libusb_dev
@@ -1762,7 +1755,7 @@ int API_EXPORTED libusb_reset_device(libusb_device_handle *dev_handle)
 	if (!dev_handle->dev->attached)
 		return LIBUSB_ERROR_NO_DEVICE;
 
-	return usbi_backend->reset_device(dev_handle);
+	return usbi_backend.reset_device(dev_handle);
 }
 
 /** \ingroup libusb_asyncio
@@ -1794,8 +1787,8 @@ int API_EXPORTED libusb_alloc_streams(libusb_device_handle *dev_handle,
 	if (!dev_handle->dev->attached)
 		return LIBUSB_ERROR_NO_DEVICE;
 
-	if (usbi_backend->alloc_streams)
-		return usbi_backend->alloc_streams(dev_handle, num_streams, endpoints,
+	if (usbi_backend.alloc_streams)
+		return usbi_backend.alloc_streams(dev_handle, num_streams, endpoints,
 						   num_endpoints);
 	else
 		return LIBUSB_ERROR_NOT_SUPPORTED;
@@ -1821,8 +1814,8 @@ int API_EXPORTED libusb_free_streams(libusb_device_handle *dev_handle,
 	if (!dev_handle->dev->attached)
 		return LIBUSB_ERROR_NO_DEVICE;
 
-	if (usbi_backend->free_streams)
-		return usbi_backend->free_streams(dev_handle, endpoints,
+	if (usbi_backend.free_streams)
+		return usbi_backend.free_streams(dev_handle, endpoints,
 						  num_endpoints);
 	else
 		return LIBUSB_ERROR_NOT_SUPPORTED;
@@ -1859,8 +1852,8 @@ unsigned char * LIBUSB_CALL libusb_dev_mem_alloc(libusb_device_handle *dev_handl
 	if (!dev_handle->dev->attached)
 		return NULL;
 
-	if (usbi_backend->dev_mem_alloc)
-		return usbi_backend->dev_mem_alloc(dev_handle, length);
+	if (usbi_backend.dev_mem_alloc)
+		return usbi_backend.dev_mem_alloc(dev_handle, length);
 	else
 		return NULL;
 }
@@ -1876,8 +1869,8 @@ unsigned char * LIBUSB_CALL libusb_dev_mem_alloc(libusb_device_handle *dev_handl
 int API_EXPORTED libusb_dev_mem_free(libusb_device_handle *dev_handle,
 	unsigned char *buffer, size_t length)
 {
-	if (usbi_backend->dev_mem_free)
-		return usbi_backend->dev_mem_free(dev_handle, buffer, length);
+	if (usbi_backend.dev_mem_free)
+		return usbi_backend.dev_mem_free(dev_handle, buffer, length);
 	else
 		return LIBUSB_ERROR_NOT_SUPPORTED;
 }
@@ -1907,8 +1900,8 @@ int API_EXPORTED libusb_kernel_driver_active(libusb_device_handle *dev_handle,
 	if (!dev_handle->dev->attached)
 		return LIBUSB_ERROR_NO_DEVICE;
 
-	if (usbi_backend->kernel_driver_active)
-		return usbi_backend->kernel_driver_active(dev_handle, interface_number);
+	if (usbi_backend.kernel_driver_active)
+		return usbi_backend.kernel_driver_active(dev_handle, interface_number);
 	else
 		return LIBUSB_ERROR_NOT_SUPPORTED;
 }
@@ -1942,8 +1935,8 @@ int API_EXPORTED libusb_detach_kernel_driver(libusb_device_handle *dev_handle,
 	if (!dev_handle->dev->attached)
 		return LIBUSB_ERROR_NO_DEVICE;
 
-	if (usbi_backend->detach_kernel_driver)
-		return usbi_backend->detach_kernel_driver(dev_handle, interface_number);
+	if (usbi_backend.detach_kernel_driver)
+		return usbi_backend.detach_kernel_driver(dev_handle, interface_number);
 	else
 		return LIBUSB_ERROR_NOT_SUPPORTED;
 }
@@ -1976,8 +1969,8 @@ int API_EXPORTED libusb_attach_kernel_driver(libusb_device_handle *dev_handle,
 	if (!dev_handle->dev->attached)
 		return LIBUSB_ERROR_NO_DEVICE;
 
-	if (usbi_backend->attach_kernel_driver)
-		return usbi_backend->attach_kernel_driver(dev_handle, interface_number);
+	if (usbi_backend.attach_kernel_driver)
+		return usbi_backend.attach_kernel_driver(dev_handle, interface_number);
 	else
 		return LIBUSB_ERROR_NOT_SUPPORTED;
 }
@@ -2007,7 +2000,7 @@ int API_EXPORTED libusb_attach_kernel_driver(libusb_device_handle *dev_handle,
 int API_EXPORTED libusb_set_auto_detach_kernel_driver(
 	libusb_device_handle *dev_handle, int enable)
 {
-	if (!(usbi_backend->caps & USBI_CAP_SUPPORTS_DETACH_KERNEL_DRIVER))
+	if (!(usbi_backend.caps & USBI_CAP_SUPPORTS_DETACH_KERNEL_DRIVER))
 		return LIBUSB_ERROR_NOT_SUPPORTED;
 
 	dev_handle->auto_detach_kernel_driver = enable;
@@ -2015,37 +2008,100 @@ int API_EXPORTED libusb_set_auto_detach_kernel_driver(
 }
 
 /** \ingroup libusb_lib
- * Set log message verbosity.
- *
- * The default level is LIBUSB_LOG_LEVEL_NONE, which means no messages are ever
- * printed. If you choose to increase the message verbosity level, ensure
- * that your application does not close the stdout/stderr file descriptors.
+ * \deprecated Use libusb_set_option() instead using the
+ * \ref LIBUSB_OPTION_LOG_LEVEL option.
+ */
+void API_EXPORTED libusb_set_debug(libusb_context *ctx, int level)
+{
+#if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING)
+	USBI_GET_CONTEXT(ctx);
+	if (!ctx->debug_fixed) {
+		level = CLAMP(level, LIBUSB_LOG_LEVEL_NONE, LIBUSB_LOG_LEVEL_DEBUG);
+		ctx->debug = (enum libusb_log_level)level;
+	}
+#else
+	UNUSED(ctx);
+	UNUSED(level);
+#endif
+}
+
+/** \ingroup libusb_lib
+ * Set an option in the library.
  *
- * You are advised to use level LIBUSB_LOG_LEVEL_WARNING. libusb is conservative
- * with its message logging and most of the time, will only log messages that
- * explain error conditions and other oddities. This will help you debug
- * your software.
+ * Use this function to configure a specific option within the library.
  *
- * If the LIBUSB_DEBUG environment variable was set when libusb was
- * initialized, this function does nothing: the message verbosity is fixed
- * to the value in the environment variable.
+ * Some options require one or more arguments to be provided. Consult each
+ * option's documentation for specific requirements.
  *
- * If libusb was compiled without any message logging, this function does
- * nothing: you'll never get any messages.
+ * Since version 1.0.22, \ref LIBUSB_API_VERSION >= 0x01000106
  *
- * If libusb was compiled with verbose debug message logging, this function
- * does nothing: you'll always get messages from all levels.
+ * \param ctx context on which to operate
+ * \param option which option to set
+ * \param ... any required arguments for the specified option
  *
- * \param ctx the context to operate on, or NULL for the default context
- * \param level debug level to set
+ * \returns LIBUSB_SUCCESS on success
+ * \returns LIBUSB_ERROR_INVALID_PARAM if the option or arguments are invalid
+ * \returns LIBUSB_ERROR_NOT_SUPPORTED if the option is valid but not supported
+ * on this platform
  */
-void API_EXPORTED libusb_set_debug(libusb_context *ctx, int level)
+int API_EXPORTED libusb_set_option(libusb_context *ctx,
+	enum libusb_option option, ...)
 {
+	int arg, r = LIBUSB_SUCCESS;
+	va_list ap;
+
 	USBI_GET_CONTEXT(ctx);
-	if (!ctx->debug_fixed)
-		ctx->debug = level;
+
+	va_start(ap, option);
+	switch (option) {
+	case LIBUSB_OPTION_LOG_LEVEL:
+		arg = va_arg(ap, int);
+		if (arg < LIBUSB_LOG_LEVEL_NONE || arg > LIBUSB_LOG_LEVEL_DEBUG) {
+			r = LIBUSB_ERROR_INVALID_PARAM;
+			break;
+		}
+#if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING)
+		if (!ctx->debug_fixed)
+			ctx->debug = (enum libusb_log_level)arg;
+#endif
+		break;
+
+	/* Handle all backend-specific options here */
+	case LIBUSB_OPTION_USE_USBDK:
+		if (usbi_backend.set_option)
+			r = usbi_backend.set_option(ctx, option, ap);
+		else
+			r = LIBUSB_ERROR_NOT_SUPPORTED;
+		break;
+
+	default:
+		r = LIBUSB_ERROR_INVALID_PARAM;
+	}
+	va_end(ap);
+
+	return r;
 }
 
+#if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING)
+/* returns the log level as defined in the LIBUSB_DEBUG environment variable.
+ * if LIBUSB_DEBUG is not present or not a number, returns LIBUSB_LOG_LEVEL_NONE.
+ * value is clamped to ensure it is within the valid range of possibilities.
+ */
+static enum libusb_log_level get_env_debug_level(void)
+{
+	const char *dbg = getenv("LIBUSB_DEBUG");
+	enum libusb_log_level level;
+	if (dbg) {
+		int dbg_level = atoi(dbg);
+		dbg_level = CLAMP(dbg_level, LIBUSB_LOG_LEVEL_NONE, LIBUSB_LOG_LEVEL_DEBUG);
+		level = (enum libusb_log_level)dbg_level;
+	} else {
+		level = LIBUSB_LOG_LEVEL_NONE;
+	}
+	return level;
+}
+#endif
+
 /** \ingroup libusb_lib
  * Initialize libusb. This function must be called before calling any other
  * libusb function.
@@ -2062,7 +2118,7 @@ void API_EXPORTED libusb_set_debug(libusb_context *ctx, int level)
 int API_EXPORTED libusb_init(libusb_context **context)
 {
 	struct libusb_device *dev, *next;
-	char *dbg = getenv("LIBUSB_DEBUG");
+	size_t priv_size = usbi_backend.context_priv_size;
 	struct libusb_context *ctx;
 	static int first_init = 1;
 	int r = 0;
@@ -2070,7 +2126,7 @@ int API_EXPORTED libusb_init(libusb_context **context)
 	usbi_mutex_static_lock(&default_context_lock);
 
 	if (!timestamp_origin.tv_sec) {
-		usbi_backend->clock_gettime(USBI_CLOCK_REALTIME, &timestamp_origin);
+		usbi_backend.clock_gettime(USBI_CLOCK_REALTIME, &timestamp_origin);
 	}
 
 	if (!context && usbi_default_context) {
@@ -2080,22 +2136,18 @@ int API_EXPORTED libusb_init(libusb_context **context)
 		return 0;
 	}
 
-	ctx = calloc(1, sizeof(*ctx));
+	ctx = calloc(1, sizeof(*ctx) + priv_size);
 	if (!ctx) {
 		r = LIBUSB_ERROR_NO_MEM;
 		goto err_unlock;
 	}
 
-#ifdef ENABLE_DEBUG_LOGGING
-	ctx->debug = LIBUSB_LOG_LEVEL_DEBUG;
+#if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING)
+	ctx->debug = get_env_debug_level();
+	if (ctx->debug != LIBUSB_LOG_LEVEL_NONE)
+		ctx->debug_fixed = 1;
 #endif
 
-	if (dbg) {
-		ctx->debug = atoi(dbg);
-		if (ctx->debug)
-			ctx->debug_fixed = 1;
-	}
-
 	/* default context should be initialized before calling usbi_dbg */
 	if (!usbi_default_context) {
 		usbi_default_context = ctx;
@@ -2112,6 +2164,7 @@ int API_EXPORTED libusb_init(libusb_context **context)
 	list_init(&ctx->usb_devs);
 	list_init(&ctx->open_devs);
 	list_init(&ctx->hotplug_cbs);
+	ctx->next_hotplug_cb_handle = 1;
 
 	usbi_mutex_static_lock(&active_contexts_lock);
 	if (first_init) {
@@ -2121,8 +2174,8 @@ int API_EXPORTED libusb_init(libusb_context **context)
 	list_add (&ctx->list, &active_contexts_list);
 	usbi_mutex_static_unlock(&active_contexts_lock);
 
-	if (usbi_backend->init) {
-		r = usbi_backend->init(ctx);
+	if (usbi_backend.init) {
+		r = usbi_backend.init(ctx);
 		if (r)
 			goto err_free_ctx;
 	}
@@ -2139,8 +2192,8 @@ int API_EXPORTED libusb_init(libusb_context **context)
 	return 0;
 
 err_backend_exit:
-	if (usbi_backend->exit)
-		usbi_backend->exit();
+	if (usbi_backend.exit)
+		usbi_backend.exit(ctx);
 err_free_ctx:
 	if (ctx == usbi_default_context) {
 		usbi_default_context = NULL;
@@ -2200,7 +2253,7 @@ void API_EXPORTED libusb_exit(struct libusb_context *ctx)
 	usbi_mutex_static_unlock(&active_contexts_lock);
 
 	if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
-		usbi_hotplug_deregister_all(ctx);
+		usbi_hotplug_deregister(ctx, 1);
 
 		/*
 		 * Ensure any pending unplug events are read from the hotplug
@@ -2230,8 +2283,8 @@ void API_EXPORTED libusb_exit(struct libusb_context *ctx)
 		usbi_warn(ctx, "application left some devices open");
 
 	usbi_io_exit(ctx);
-	if (usbi_backend->exit)
-		usbi_backend->exit();
+	if (usbi_backend.exit)
+		usbi_backend.exit(ctx);
 
 	usbi_mutex_destroy(&ctx->open_devs_lock);
 	usbi_mutex_destroy(&ctx->usb_devs_lock);
@@ -2253,15 +2306,17 @@ int API_EXPORTED libusb_has_capability(uint32_t capability)
 	case LIBUSB_CAP_HAS_CAPABILITY:
 		return 1;
 	case LIBUSB_CAP_HAS_HOTPLUG:
-		return !(usbi_backend->get_device_list);
+		return !(usbi_backend.get_device_list);
 	case LIBUSB_CAP_HAS_HID_ACCESS:
-		return (usbi_backend->caps & USBI_CAP_HAS_HID_ACCESS);
+		return (usbi_backend.caps & USBI_CAP_HAS_HID_ACCESS);
 	case LIBUSB_CAP_SUPPORTS_DETACH_KERNEL_DRIVER:
-		return (usbi_backend->caps & USBI_CAP_SUPPORTS_DETACH_KERNEL_DRIVER);
+		return (usbi_backend.caps & USBI_CAP_SUPPORTS_DETACH_KERNEL_DRIVER);
 	}
 	return 0;
 }
 
+#ifdef ENABLE_LOGGING
+
 /* this is defined in libusbi.h if needed */
 #ifdef LIBUSB_PRINTF_WIN32
 /*
@@ -2301,10 +2356,9 @@ int usbi_vsnprintf(char *str, size_t size, const char *format, va_list ap)
 
 	return ret;
 }
-#endif
+#endif /* LIBUSB_PRINTF_WIN32 */
 
-static void usbi_log_str(struct libusb_context *ctx,
-	enum libusb_log_level level, const char * str)
+static void usbi_log_str(enum libusb_log_level level, const char *str)
 {
 #if defined(USE_SYSTEM_LOGGING_FACILITY)
 #if defined(OS_WINDOWS)
@@ -2317,18 +2371,20 @@ static void usbi_log_str(struct libusb_context *ctx,
 #elif defined(__ANDROID__)
 	int priority = ANDROID_LOG_UNKNOWN;
 	switch (level) {
-	case LIBUSB_LOG_LEVEL_INFO: priority = ANDROID_LOG_INFO; break;
-	case LIBUSB_LOG_LEVEL_WARNING: priority = ANDROID_LOG_WARN; break;
+	case LIBUSB_LOG_LEVEL_NONE: return;
 	case LIBUSB_LOG_LEVEL_ERROR: priority = ANDROID_LOG_ERROR; break;
+	case LIBUSB_LOG_LEVEL_WARNING: priority = ANDROID_LOG_WARN; break;
+	case LIBUSB_LOG_LEVEL_INFO: priority = ANDROID_LOG_INFO; break;
 	case LIBUSB_LOG_LEVEL_DEBUG: priority = ANDROID_LOG_DEBUG; break;
 	}
 	__android_log_write(priority, "libusb", str);
 #elif defined(HAVE_SYSLOG_FUNC)
 	int syslog_level = LOG_INFO;
 	switch (level) {
-	case LIBUSB_LOG_LEVEL_INFO: syslog_level = LOG_INFO; break;
-	case LIBUSB_LOG_LEVEL_WARNING: syslog_level = LOG_WARNING; break;
+	case LIBUSB_LOG_LEVEL_NONE: return;
 	case LIBUSB_LOG_LEVEL_ERROR: syslog_level = LOG_ERR; break;
+	case LIBUSB_LOG_LEVEL_WARNING: syslog_level = LOG_WARNING; break;
+	case LIBUSB_LOG_LEVEL_INFO: syslog_level = LOG_INFO; break;
 	case LIBUSB_LOG_LEVEL_DEBUG: syslog_level = LOG_DEBUG; break;
 	}
 	syslog(syslog_level, "%s", str);
@@ -2339,14 +2395,13 @@ static void usbi_log_str(struct libusb_context *ctx,
 #else
 	fputs(str, stderr);
 #endif /* USE_SYSTEM_LOGGING_FACILITY */
-	UNUSED(ctx);
 	UNUSED(level);
 }
 
 void usbi_log_v(struct libusb_context *ctx, enum libusb_log_level level,
 	const char *function, const char *format, va_list args)
 {
-	const char *prefix = "";
+	const char *prefix;
 	char buf[USBI_MAX_LOG_LEN];
 	struct timespec now;
 	int global_debug, header_len, text_len;
@@ -2356,18 +2411,15 @@ void usbi_log_v(struct libusb_context *ctx, enum libusb_log_level level,
 	global_debug = 1;
 	UNUSED(ctx);
 #else
-	int ctx_level = 0;
+	enum libusb_log_level ctx_level = LIBUSB_LOG_LEVEL_NONE;
 
 	USBI_GET_CONTEXT(ctx);
-	if (ctx) {
+	if (ctx)
 		ctx_level = ctx->debug;
-	} else {
-		char *dbg = getenv("LIBUSB_DEBUG");
-		if (dbg)
-			ctx_level = atoi(dbg);
-	}
-	global_debug = (ctx_level == LIBUSB_LOG_LEVEL_DEBUG);
-	if (!ctx_level)
+	else
+		ctx_level = get_env_debug_level();
+
+	if (ctx_level == LIBUSB_LOG_LEVEL_NONE)
 		return;
 	if (level == LIBUSB_LOG_LEVEL_WARNING && ctx_level < LIBUSB_LOG_LEVEL_WARNING)
 		return;
@@ -2375,13 +2427,15 @@ void usbi_log_v(struct libusb_context *ctx, enum libusb_log_level level,
 		return;
 	if (level == LIBUSB_LOG_LEVEL_DEBUG && ctx_level < LIBUSB_LOG_LEVEL_DEBUG)
 		return;
+
+	global_debug = (ctx_level == LIBUSB_LOG_LEVEL_DEBUG);
 #endif
 
-	usbi_backend->clock_gettime(USBI_CLOCK_REALTIME, &now);
+	usbi_backend.clock_gettime(USBI_CLOCK_REALTIME, &now);
 	if ((global_debug) && (!has_debug_header_been_displayed)) {
 		has_debug_header_been_displayed = 1;
-		usbi_log_str(ctx, LIBUSB_LOG_LEVEL_DEBUG, "[timestamp] [threadID] facility level [function call] <message>" USBI_LOG_LINE_END);
-		usbi_log_str(ctx, LIBUSB_LOG_LEVEL_DEBUG, "--------------------------------------------------------------------------------" USBI_LOG_LINE_END);
+		usbi_log_str(LIBUSB_LOG_LEVEL_DEBUG, "[timestamp] [threadID] facility level [function call] <message>" USBI_LOG_LINE_END);
+		usbi_log_str(LIBUSB_LOG_LEVEL_DEBUG, "--------------------------------------------------------------------------------" USBI_LOG_LINE_END);
 	}
 	if (now.tv_nsec < timestamp_origin.tv_nsec) {
 		now.tv_sec--;
@@ -2391,20 +2445,20 @@ void usbi_log_v(struct libusb_context *ctx, enum libusb_log_level level,
 	now.tv_nsec -= timestamp_origin.tv_nsec;
 
 	switch (level) {
-	case LIBUSB_LOG_LEVEL_INFO:
-		prefix = "info";
+	case LIBUSB_LOG_LEVEL_NONE:
+		return;
+	case LIBUSB_LOG_LEVEL_ERROR:
+		prefix = "error";
 		break;
 	case LIBUSB_LOG_LEVEL_WARNING:
 		prefix = "warning";
 		break;
-	case LIBUSB_LOG_LEVEL_ERROR:
-		prefix = "error";
+	case LIBUSB_LOG_LEVEL_INFO:
+		prefix = "info";
 		break;
 	case LIBUSB_LOG_LEVEL_DEBUG:
 		prefix = "debug";
 		break;
-	case LIBUSB_LOG_LEVEL_NONE:
-		return;
 	default:
 		prefix = "unknown";
 		break;
@@ -2439,7 +2493,7 @@ void usbi_log_v(struct libusb_context *ctx, enum libusb_log_level level,
 	}
 	strcpy(buf + header_len + text_len, USBI_LOG_LINE_END);
 
-	usbi_log_str(ctx, level, buf);
+	usbi_log_str(level, buf);
 }
 
 void usbi_log(struct libusb_context *ctx, enum libusb_log_level level,
@@ -2452,6 +2506,8 @@ void usbi_log(struct libusb_context *ctx, enum libusb_log_level level,
 	va_end (args);
 }
 
+#endif /* ENABLE_LOGGING */
+
 /** \ingroup libusb_misc
  * Returns a constant NULL-terminated string with the ASCII name of a libusb
  * error or transfer status code. The caller must not free() the returned

+ 11 - 10
vendor/github.com/karalabe/hid/libusb/libusb/descriptor.c → vendor/github.com/karalabe/usb/libusb/libusb/descriptor.c

@@ -333,7 +333,7 @@ static int parse_interface(libusb_context *ctx,
 					goto err;
 				if (r == 0) {
 					ifp->bNumEndpoints = (uint8_t)i;
-					break;;
+					break;
 				}
 
 				buffer += r;
@@ -513,7 +513,7 @@ int usbi_device_cache_descriptor(libusb_device *dev)
 {
 	int r, host_endian = 0;
 
-	r = usbi_backend->get_device_descriptor(dev, (unsigned char *) &dev->device_descriptor,
+	r = usbi_backend.get_device_descriptor(dev, (unsigned char *) &dev->device_descriptor,
 						&host_endian);
 	if (r < 0)
 		return r;
@@ -572,7 +572,7 @@ int API_EXPORTED libusb_get_active_config_descriptor(libusb_device *dev,
 	int host_endian = 0;
 	int r;
 
-	r = usbi_backend->get_active_config_descriptor(dev, tmp,
+	r = usbi_backend.get_active_config_descriptor(dev, tmp,
 		LIBUSB_DT_CONFIG_SIZE, &host_endian);
 	if (r < 0)
 		return r;
@@ -587,7 +587,7 @@ int API_EXPORTED libusb_get_active_config_descriptor(libusb_device *dev,
 	if (!buf)
 		return LIBUSB_ERROR_NO_MEM;
 
-	r = usbi_backend->get_active_config_descriptor(dev, buf,
+	r = usbi_backend.get_active_config_descriptor(dev, buf,
 		_config.wTotalLength, &host_endian);
 	if (r >= 0)
 		r = raw_desc_to_config(dev->ctx, buf, r, host_endian, config);
@@ -625,7 +625,7 @@ int API_EXPORTED libusb_get_config_descriptor(libusb_device *dev,
 	if (config_index >= dev->num_configurations)
 		return LIBUSB_ERROR_NOT_FOUND;
 
-	r = usbi_backend->get_config_descriptor(dev, config_index, tmp,
+	r = usbi_backend.get_config_descriptor(dev, config_index, tmp,
 		LIBUSB_DT_CONFIG_SIZE, &host_endian);
 	if (r < 0)
 		return r;
@@ -640,7 +640,7 @@ int API_EXPORTED libusb_get_config_descriptor(libusb_device *dev,
 	if (!buf)
 		return LIBUSB_ERROR_NO_MEM;
 
-	r = usbi_backend->get_config_descriptor(dev, config_index, buf,
+	r = usbi_backend.get_config_descriptor(dev, config_index, buf,
 		_config.wTotalLength, &host_endian);
 	if (r >= 0)
 		r = raw_desc_to_config(dev->ctx, buf, r, host_endian, config);
@@ -663,7 +663,7 @@ int usbi_get_config_index_by_value(struct libusb_device *dev,
 	for (i = 0; i < dev->num_configurations; i++) {
 		unsigned char tmp[6];
 		int host_endian;
-		int r = usbi_backend->get_config_descriptor(dev, i, tmp, sizeof(tmp),
+		int r = usbi_backend.get_config_descriptor(dev, i, tmp, sizeof(tmp),
 			&host_endian);
 		if (r < 0) {
 			*idx = -1;
@@ -702,8 +702,8 @@ int API_EXPORTED libusb_get_config_descriptor_by_value(libusb_device *dev,
 	int r, idx, host_endian;
 	unsigned char *buf = NULL;
 
-	if (usbi_backend->get_config_descriptor_by_value) {
-		r = usbi_backend->get_config_descriptor_by_value(dev,
+	if (usbi_backend.get_config_descriptor_by_value) {
+		r = usbi_backend.get_config_descriptor_by_value(dev,
 			bConfigurationValue, &buf, &host_endian);
 		if (r < 0)
 			return r;
@@ -1176,7 +1176,8 @@ int API_EXPORTED libusb_get_string_descriptor_ascii(libusb_device_handle *dev_ha
 	if (tbuf[0] > r)
 		return LIBUSB_ERROR_IO;
 
-	for (di = 0, si = 2; si < tbuf[0]; si += 2) {
+	di = 0;
+	for (si = 2; si < tbuf[0]; si += 2) {
 		if (di >= (length - 1))
 			break;
 

+ 73 - 50
vendor/github.com/karalabe/hid/libusb/libusb/hotplug.c → vendor/github.com/karalabe/usb/libusb/libusb/hotplug.c

@@ -154,36 +154,30 @@ int main (void) {
 \endcode
  */
 
-static int usbi_hotplug_match_cb (struct libusb_context *ctx,
+static int usbi_hotplug_match_cb(struct libusb_context *ctx,
 	struct libusb_device *dev, libusb_hotplug_event event,
 	struct libusb_hotplug_callback *hotplug_cb)
 {
-	/* Handle lazy deregistration of callback */
-	if (hotplug_cb->needs_free) {
-		/* Free callback */
-		return 1;
-	}
-
-	if (!(hotplug_cb->events & event)) {
+	if (!(hotplug_cb->flags & event)) {
 		return 0;
 	}
 
-	if (LIBUSB_HOTPLUG_MATCH_ANY != hotplug_cb->vendor_id &&
+	if ((hotplug_cb->flags & USBI_HOTPLUG_VENDOR_ID_VALID) &&
 	    hotplug_cb->vendor_id != dev->device_descriptor.idVendor) {
 		return 0;
 	}
 
-	if (LIBUSB_HOTPLUG_MATCH_ANY != hotplug_cb->product_id &&
+	if ((hotplug_cb->flags & USBI_HOTPLUG_PRODUCT_ID_VALID) &&
 	    hotplug_cb->product_id != dev->device_descriptor.idProduct) {
 		return 0;
 	}
 
-	if (LIBUSB_HOTPLUG_MATCH_ANY != hotplug_cb->dev_class &&
+	if ((hotplug_cb->flags & USBI_HOTPLUG_DEV_CLASS_VALID) &&
 	    hotplug_cb->dev_class != dev->device_descriptor.bDeviceClass) {
 		return 0;
 	}
 
-	return hotplug_cb->cb (ctx, dev, event, hotplug_cb->user_data);
+	return hotplug_cb->cb(ctx, dev, event, hotplug_cb->user_data);
 }
 
 void usbi_hotplug_match(struct libusb_context *ctx, struct libusb_device *dev,
@@ -195,8 +189,13 @@ void usbi_hotplug_match(struct libusb_context *ctx, struct libusb_device *dev,
 	usbi_mutex_lock(&ctx->hotplug_cbs_lock);
 
 	list_for_each_entry_safe(hotplug_cb, next, &ctx->hotplug_cbs, list, struct libusb_hotplug_callback) {
+		if (hotplug_cb->flags & USBI_HOTPLUG_NEEDS_FREE) {
+			/* process deregistration in usbi_hotplug_deregister() */
+			continue;
+		}
+
 		usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
-		ret = usbi_hotplug_match_cb (ctx, dev, event, hotplug_cb);
+		ret = usbi_hotplug_match_cb(ctx, dev, event, hotplug_cb);
 		usbi_mutex_lock(&ctx->hotplug_cbs_lock);
 
 		if (ret) {
@@ -206,15 +205,13 @@ void usbi_hotplug_match(struct libusb_context *ctx, struct libusb_device *dev,
 	}
 
 	usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
-
-	/* the backend is expected to call the callback for each active transfer */
 }
 
 void usbi_hotplug_notification(struct libusb_context *ctx, struct libusb_device *dev,
 	libusb_hotplug_event event)
 {
 	int pending_events;
-	libusb_hotplug_message *message = calloc(1, sizeof(*message));
+	struct libusb_hotplug_message *message = calloc(1, sizeof(*message));
 
 	if (!message) {
 		usbi_err(ctx, "error allocating hotplug message");
@@ -240,59 +237,70 @@ int API_EXPORTED libusb_hotplug_register_callback(libusb_context *ctx,
 	libusb_hotplug_callback_fn cb_fn, void *user_data,
 	libusb_hotplug_callback_handle *callback_handle)
 {
-	libusb_hotplug_callback *new_callback;
-	static int handle_id = 1;
-
-	/* check for hotplug support */
-	if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
-		return LIBUSB_ERROR_NOT_SUPPORTED;
-	}
+	struct libusb_hotplug_callback *new_callback;
 
 	/* check for sane values */
-	if ((LIBUSB_HOTPLUG_MATCH_ANY != vendor_id && (~0xffff & vendor_id)) ||
+	if ((!events || (~(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) & events)) ||
+	    (flags && (~LIBUSB_HOTPLUG_ENUMERATE & flags)) ||
+	    (LIBUSB_HOTPLUG_MATCH_ANY != vendor_id && (~0xffff & vendor_id)) ||
 	    (LIBUSB_HOTPLUG_MATCH_ANY != product_id && (~0xffff & product_id)) ||
 	    (LIBUSB_HOTPLUG_MATCH_ANY != dev_class && (~0xff & dev_class)) ||
 	    !cb_fn) {
 		return LIBUSB_ERROR_INVALID_PARAM;
 	}
 
+	/* check for hotplug support */
+	if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
+		return LIBUSB_ERROR_NOT_SUPPORTED;
+	}
+
 	USBI_GET_CONTEXT(ctx);
 
-	new_callback = (libusb_hotplug_callback *)calloc(1, sizeof (*new_callback));
+	new_callback = calloc(1, sizeof(*new_callback));
 	if (!new_callback) {
 		return LIBUSB_ERROR_NO_MEM;
 	}
 
-	new_callback->ctx = ctx;
-	new_callback->vendor_id = vendor_id;
-	new_callback->product_id = product_id;
-	new_callback->dev_class = dev_class;
-	new_callback->flags = flags;
-	new_callback->events = events;
+	new_callback->flags = (uint8_t)events;
+	if (LIBUSB_HOTPLUG_MATCH_ANY != vendor_id) {
+		new_callback->flags |= USBI_HOTPLUG_VENDOR_ID_VALID;
+		new_callback->vendor_id = (uint16_t)vendor_id;
+	}
+	if (LIBUSB_HOTPLUG_MATCH_ANY != product_id) {
+		new_callback->flags |= USBI_HOTPLUG_PRODUCT_ID_VALID;
+		new_callback->product_id = (uint16_t)product_id;
+	}
+	if (LIBUSB_HOTPLUG_MATCH_ANY != dev_class) {
+		new_callback->flags |= USBI_HOTPLUG_DEV_CLASS_VALID;
+		new_callback->dev_class = (uint8_t)dev_class;
+	}
 	new_callback->cb = cb_fn;
 	new_callback->user_data = user_data;
-	new_callback->needs_free = 0;
 
 	usbi_mutex_lock(&ctx->hotplug_cbs_lock);
 
-	/* protect the handle by the context hotplug lock. it doesn't matter if the same handle
-	 * is used for different contexts only that the handle is unique for this context */
-	new_callback->handle = handle_id++;
+	/* protect the handle by the context hotplug lock */
+	new_callback->handle = ctx->next_hotplug_cb_handle++;
+
+	/* handle the unlikely case of overflow */
+	if (ctx->next_hotplug_cb_handle < 0)
+		ctx->next_hotplug_cb_handle = 1;
 
 	list_add(&new_callback->list, &ctx->hotplug_cbs);
 
 	usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
 
+	usbi_dbg("new hotplug cb %p with handle %d", new_callback, new_callback->handle);
 
-	if (flags & LIBUSB_HOTPLUG_ENUMERATE) {
-		int i, len;
+	if ((flags & LIBUSB_HOTPLUG_ENUMERATE) && (events & LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED)) {
+		ssize_t i, len;
 		struct libusb_device **devs;
 
-		len = (int) libusb_get_device_list(ctx, &devs);
+		len = libusb_get_device_list(ctx, &devs);
 		if (len < 0) {
 			libusb_hotplug_deregister_callback(ctx,
 							new_callback->handle);
-			return len;
+			return (int)len;
 		}
 
 		for (i = 0; i < len; i++) {
@@ -311,10 +319,11 @@ int API_EXPORTED libusb_hotplug_register_callback(libusb_context *ctx,
 	return LIBUSB_SUCCESS;
 }
 
-void API_EXPORTED libusb_hotplug_deregister_callback (struct libusb_context *ctx,
+void API_EXPORTED libusb_hotplug_deregister_callback(struct libusb_context *ctx,
 	libusb_hotplug_callback_handle callback_handle)
 {
 	struct libusb_hotplug_callback *hotplug_cb;
+	int deregistered = 0;
 
 	/* check for hotplug support */
 	if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
@@ -323,28 +332,42 @@ void API_EXPORTED libusb_hotplug_deregister_callback (struct libusb_context *ctx
 
 	USBI_GET_CONTEXT(ctx);
 
+	usbi_dbg("deregister hotplug cb %d", callback_handle);
+
 	usbi_mutex_lock(&ctx->hotplug_cbs_lock);
-	list_for_each_entry(hotplug_cb, &ctx->hotplug_cbs, list,
-			    struct libusb_hotplug_callback) {
+	list_for_each_entry(hotplug_cb, &ctx->hotplug_cbs, list, struct libusb_hotplug_callback) {
 		if (callback_handle == hotplug_cb->handle) {
 			/* Mark this callback for deregistration */
-			hotplug_cb->needs_free = 1;
+			hotplug_cb->flags |= USBI_HOTPLUG_NEEDS_FREE;
+			deregistered = 1;
 		}
 	}
 	usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
 
-	usbi_hotplug_notification(ctx, NULL, 0);
+	if (deregistered) {
+		int pending_events;
+
+		usbi_mutex_lock(&ctx->event_data_lock);
+		pending_events = usbi_pending_events(ctx);
+		ctx->event_flags |= USBI_EVENT_HOTPLUG_CB_DEREGISTERED;
+		if (!pending_events)
+			usbi_signal_event(ctx);
+		usbi_mutex_unlock(&ctx->event_data_lock);
+	}
 }
 
-void usbi_hotplug_deregister_all(struct libusb_context *ctx) {
+void usbi_hotplug_deregister(struct libusb_context *ctx, int forced)
+{
 	struct libusb_hotplug_callback *hotplug_cb, *next;
 
 	usbi_mutex_lock(&ctx->hotplug_cbs_lock);
-	list_for_each_entry_safe(hotplug_cb, next, &ctx->hotplug_cbs, list,
-				 struct libusb_hotplug_callback) {
-		list_del(&hotplug_cb->list);
-		free(hotplug_cb);
+	list_for_each_entry_safe(hotplug_cb, next, &ctx->hotplug_cbs, list, struct libusb_hotplug_callback) {
+		if (forced || (hotplug_cb->flags & USBI_HOTPLUG_NEEDS_FREE)) {
+			usbi_dbg("freeing hotplug cb %p with handle %d", hotplug_cb,
+				 hotplug_cb->handle);
+			list_del(&hotplug_cb->list);
+			free(hotplug_cb);
+		}
 	}
-
 	usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
 }

+ 34 - 25
vendor/github.com/karalabe/hid/libusb/libusb/hotplug.h → vendor/github.com/karalabe/usb/libusb/libusb/hotplug.h

@@ -19,12 +19,34 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
-#if !defined(USBI_HOTPLUG_H)
+#ifndef USBI_HOTPLUG_H
 #define USBI_HOTPLUG_H
 
-#ifndef LIBUSBI_H
 #include "libusbi.h"
-#endif
+
+enum usbi_hotplug_flags {
+	/* This callback is interested in device arrivals */
+	USBI_HOTPLUG_DEVICE_ARRIVED = LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED,
+
+	/* This callback is interested in device removals */
+	USBI_HOTPLUG_DEVICE_LEFT = LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT,
+
+	/* IMPORTANT: The values for the below entries must start *after*
+	 * the highest value of the above entries!!!
+	 */
+
+	/* The vendor_id field is valid for matching */
+	USBI_HOTPLUG_VENDOR_ID_VALID = (1 << 3),
+
+	/* The product_id field is valid for matching */
+	USBI_HOTPLUG_PRODUCT_ID_VALID = (1 << 4),
+
+	/* The dev_class field is valid for matching */
+	USBI_HOTPLUG_DEV_CLASS_VALID = (1 << 5),
+
+	/* This callback has been unregistered and needs to be freed */
+	USBI_HOTPLUG_NEEDS_FREE = (1 << 6),
+};
 
 /** \ingroup hotplug
  * The hotplug callback structure. The user populates this structure with
@@ -32,23 +54,17 @@
  * to receive notification of hotplug events.
  */
 struct libusb_hotplug_callback {
-	/** Context this callback is associated with */
-	struct libusb_context *ctx;
-
-	/** Vendor ID to match or LIBUSB_HOTPLUG_MATCH_ANY */
-	int vendor_id;
-
-	/** Product ID to match or LIBUSB_HOTPLUG_MATCH_ANY */
-	int product_id;
+	/** Flags that control how this callback behaves */
+	uint8_t flags;
 
-	/** Device class to match or LIBUSB_HOTPLUG_MATCH_ANY */
-	int dev_class;
+	/** Vendor ID to match (if flags says this is valid) */
+	uint16_t vendor_id;
 
-	/** Hotplug callback flags */
-	libusb_hotplug_flag flags;
+	/** Product ID to match (if flags says this is valid) */
+	uint16_t product_id;
 
-	/** Event(s) that will trigger this callback */
-	libusb_hotplug_event events;
+	/** Device class to match (if flags says this is valid) */
+	uint8_t dev_class;
 
 	/** Callback function to invoke for matching event/device */
 	libusb_hotplug_callback_fn cb;
@@ -59,15 +75,10 @@ struct libusb_hotplug_callback {
 	/** User data that will be passed to the callback function */
 	void *user_data;
 
-	/** Callback is marked for deletion */
-	int needs_free;
-
 	/** List this callback is registered in (ctx->hotplug_cbs) */
 	struct list_head list;
 };
 
-typedef struct libusb_hotplug_callback libusb_hotplug_callback;
-
 struct libusb_hotplug_message {
 	/** The hotplug event that occurred */
 	libusb_hotplug_event event;
@@ -79,9 +90,7 @@ struct libusb_hotplug_message {
 	struct list_head list;
 };
 
-typedef struct libusb_hotplug_message libusb_hotplug_message;
-
-void usbi_hotplug_deregister_all(struct libusb_context *ctx);
+void usbi_hotplug_deregister(struct libusb_context *ctx, int forced);
 void usbi_hotplug_match(struct libusb_context *ctx, struct libusb_device *dev,
 			libusb_hotplug_event event);
 void usbi_hotplug_notification(struct libusb_context *ctx, struct libusb_device *dev,

+ 38 - 35
vendor/github.com/karalabe/hid/libusb/libusb/io.c → vendor/github.com/karalabe/usb/libusb/libusb/io.c

@@ -1144,8 +1144,8 @@ int usbi_io_init(struct libusb_context *ctx)
 		goto err_close_pipe;
 
 #ifdef USBI_TIMERFD_AVAILABLE
-	ctx->timerfd = timerfd_create(usbi_backend->get_timerfd_clockid(),
-		TFD_NONBLOCK);
+	ctx->timerfd = timerfd_create(usbi_backend.get_timerfd_clockid(),
+		TFD_NONBLOCK | TFD_CLOEXEC);
 	if (ctx->timerfd >= 0) {
 		usbi_dbg("using timerfd for timeouts");
 		r = usbi_add_pollfd(ctx, ctx->timerfd, POLLIN);
@@ -1205,10 +1205,12 @@ static int calculate_timeout(struct usbi_transfer *transfer)
 	unsigned int timeout =
 		USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer)->timeout;
 
-	if (!timeout)
+	if (!timeout) {
+		timerclear(&transfer->timeout);
 		return 0;
+	}
 
-	r = usbi_backend->clock_gettime(USBI_CLOCK_MONOTONIC, &current_time);
+	r = usbi_backend.clock_gettime(USBI_CLOCK_MONOTONIC, &current_time);
 	if (r < 0) {
 		usbi_err(ITRANSFER_CTX(transfer),
 			"failed to read monotonic clock, errno=%d", errno);
@@ -1255,7 +1257,7 @@ struct libusb_transfer * LIBUSB_CALL libusb_alloc_transfer(
 	int iso_packets)
 {
 	struct libusb_transfer *transfer;
-	size_t os_alloc_size = usbi_backend->transfer_priv_size;
+	size_t os_alloc_size = usbi_backend.transfer_priv_size;
 	size_t alloc_size = sizeof(struct usbi_transfer)
 		+ sizeof(struct libusb_transfer)
 		+ (sizeof(struct libusb_iso_packet_descriptor) * iso_packets)
@@ -1526,7 +1528,7 @@ int API_EXPORTED libusb_submit_transfer(struct libusb_transfer *transfer)
 	 */
 	usbi_mutex_unlock(&ctx->flying_transfers_lock);
 
-	r = usbi_backend->submit_transfer(itransfer);
+	r = usbi_backend.submit_transfer(itransfer);
 	if (r == LIBUSB_SUCCESS) {
 		itransfer->state_flags |= USBI_TRANSFER_IN_FLIGHT;
 		/* keep a reference to this device */
@@ -1567,7 +1569,7 @@ int API_EXPORTED libusb_cancel_transfer(struct libusb_transfer *transfer)
 		r = LIBUSB_ERROR_NOT_FOUND;
 		goto out;
 	}
-	r = usbi_backend->cancel_transfer(itransfer);
+	r = usbi_backend.cancel_transfer(itransfer);
 	if (r < 0) {
 		if (r != LIBUSB_ERROR_NOT_FOUND &&
 		    r != LIBUSB_ERROR_NO_DEVICE)
@@ -2004,7 +2006,7 @@ static int handle_timeouts_locked(struct libusb_context *ctx)
 		return 0;
 
 	/* get current time */
-	r = usbi_backend->clock_gettime(USBI_CLOCK_MONOTONIC, &systime_ts);
+	r = usbi_backend.clock_gettime(USBI_CLOCK_MONOTONIC, &systime_ts);
 	if (r < 0)
 		return r;
 
@@ -2077,7 +2079,6 @@ static int handle_events(struct libusb_context *ctx, struct timeval *tv)
 	struct pollfd *fds = NULL;
 	int i = -1;
 	int timeout_ms;
-	int special_event;
 
 	/* prevent attempts to recursively handle events (e.g. calling into
 	 * libusb_handle_events() from within a hotplug or transfer callback) */
@@ -2146,32 +2147,30 @@ static int handle_events(struct libusb_context *ctx, struct timeval *tv)
 	if (tv->tv_usec % 1000)
 		timeout_ms++;
 
-redo_poll:
 	usbi_dbg("poll() %d fds with timeout in %dms", nfds, timeout_ms);
 	r = usbi_poll(fds, nfds, timeout_ms);
 	usbi_dbg("poll() returned %d", r);
 	if (r == 0) {
 		r = handle_timeouts(ctx);
 		goto done;
-	}
-	else if (r == -1 && errno == EINTR) {
+	} else if (r == -1 && errno == EINTR) {
 		r = LIBUSB_ERROR_INTERRUPTED;
 		goto done;
-	}
-	else if (r < 0) {
+	} else if (r < 0) {
 		usbi_err(ctx, "poll failed %d err=%d", r, errno);
 		r = LIBUSB_ERROR_IO;
 		goto done;
 	}
 
-	special_event = 0;
-
 	/* fds[0] is always the event pipe */
 	if (fds[0].revents) {
-		libusb_hotplug_message *message = NULL;
+		struct list_head hotplug_msgs;
 		struct usbi_transfer *itransfer;
+		int hotplug_cb_deregistered = 0;
 		int ret = 0;
 
+		list_init(&hotplug_msgs);
+
 		usbi_dbg("caught a fish on the event pipe");
 
 		/* take the the event data lock while processing events */
@@ -2186,6 +2185,12 @@ redo_poll:
 			ctx->event_flags &= ~USBI_EVENT_USER_INTERRUPT;
 		}
 
+		if (ctx->event_flags & USBI_EVENT_HOTPLUG_CB_DEREGISTERED) {
+			usbi_dbg("someone unregistered a hotplug cb");
+			ctx->event_flags &= ~USBI_EVENT_HOTPLUG_CB_DEREGISTERED;
+			hotplug_cb_deregistered = 1;
+		}
+
 		/* check if someone is closing a device */
 		if (ctx->device_close)
 			usbi_dbg("someone is closing a device");
@@ -2193,9 +2198,7 @@ redo_poll:
 		/* check for any pending hotplug messages */
 		if (!list_empty(&ctx->hotplug_msgs)) {
 			usbi_dbg("hotplug message received");
-			special_event = 1;
-			message = list_first_entry(&ctx->hotplug_msgs, libusb_hotplug_message, list);
-			list_del(&message->list);
+			list_cut(&hotplug_msgs, &ctx->hotplug_msgs);
 		}
 
 		/* complete any pending transfers */
@@ -2203,7 +2206,7 @@ redo_poll:
 			itransfer = list_first_entry(&ctx->completed_transfers, struct usbi_transfer, completed_list);
 			list_del(&itransfer->completed_list);
 			usbi_mutex_unlock(&ctx->event_data_lock);
-			ret = usbi_backend->handle_transfer_completion(itransfer);
+			ret = usbi_backend.handle_transfer_completion(itransfer);
 			if (ret)
 				usbi_err(ctx, "backend handle_transfer_completion failed with error %d", ret);
 			usbi_mutex_lock(&ctx->event_data_lock);
@@ -2215,14 +2218,21 @@ redo_poll:
 
 		usbi_mutex_unlock(&ctx->event_data_lock);
 
-		/* process the hotplug message, if any */
-		if (message) {
+		if (hotplug_cb_deregistered)
+			usbi_hotplug_deregister(ctx, 0);
+
+		/* process the hotplug messages, if any */
+		while (!list_empty(&hotplug_msgs)) {
+			struct libusb_hotplug_message *message =
+				list_first_entry(&hotplug_msgs, struct libusb_hotplug_message, list);
+
 			usbi_hotplug_match(ctx, message->device, message->event);
 
 			/* the device left, dereference the device */
 			if (LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT == message->event)
 				libusb_unref_device(message->device);
 
+			list_del(&message->list);
 			free(message);
 		}
 
@@ -2233,7 +2243,7 @@ redo_poll:
 		}
 
 		if (0 == --r)
-			goto handled;
+			goto done;
 	}
 
 #ifdef USBI_TIMERFD_AVAILABLE
@@ -2242,7 +2252,6 @@ redo_poll:
 		/* timerfd indicates that a timeout has expired */
 		int ret;
 		usbi_dbg("timerfd triggered");
-		special_event = 1;
 
 		ret = handle_timerfd_trigger(ctx);
 		if (ret < 0) {
@@ -2252,20 +2261,14 @@ redo_poll:
 		}
 
 		if (0 == --r)
-			goto handled;
+			goto done;
 	}
 #endif
 
-	r = usbi_backend->handle_events(ctx, fds + internal_nfds, nfds - internal_nfds, r);
+	r = usbi_backend.handle_events(ctx, fds + internal_nfds, nfds - internal_nfds, r);
 	if (r)
 		usbi_err(ctx, "backend handle_events failed with error %d", r);
 
-handled:
-	if (r == 0 && special_event) {
-		timeout_ms = 0;
-		goto redo_poll;
-	}
-
 done:
 	usbi_end_event_handling(ctx);
 	return r;
@@ -2583,7 +2586,7 @@ int API_EXPORTED libusb_get_next_timeout(libusb_context *ctx,
 		return 0;
 	}
 
-	r = usbi_backend->clock_gettime(USBI_CLOCK_MONOTONIC, &cur_ts);
+	r = usbi_backend.clock_gettime(USBI_CLOCK_MONOTONIC, &cur_ts);
 	if (r < 0) {
 		usbi_err(ctx, "failed to read monotonic clock, errno=%d", errno);
 		return 0;
@@ -2811,7 +2814,7 @@ void usbi_handle_disconnect(struct libusb_device_handle *dev_handle)
 			 USBI_TRANSFER_TO_LIBUSB_TRANSFER(to_cancel));
 
 		usbi_mutex_lock(&to_cancel->lock);
-		usbi_backend->clear_transfer_priv(to_cancel);
+		usbi_backend.clear_transfer_priv(to_cancel);
 		usbi_mutex_unlock(&to_cancel->lock);
 		usbi_handle_transfer_completion(to_cancel, LIBUSB_TRANSFER_NO_DEVICE);
 	}

+ 63 - 32
vendor/github.com/karalabe/hid/libusb/libusb/libusb.h → vendor/github.com/karalabe/usb/libusb/libusb/libusb.h

@@ -54,13 +54,19 @@ typedef unsigned __int32  uint32_t;
 #include <sys/types.h>
 #endif
 
-#if defined(__linux) || defined(__APPLE__) || defined(__CYGWIN__) || defined(__HAIKU__)
+#if defined(__linux__) || defined(__APPLE__) || defined(__CYGWIN__) || defined(__HAIKU__)
 #include <sys/time.h>
 #endif
 
 #include <time.h>
 #include <limits.h>
 
+#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
+#define ZERO_SIZED_ARRAY		/* [] - valid C99 code */
+#else
+#define ZERO_SIZED_ARRAY	0	/* [0] - non-standard, but usually working code */
+#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
@@ -79,6 +85,8 @@ typedef unsigned __int32  uint32_t;
 #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
 #define LIBUSB_DEPRECATED_FOR(f) \
   __attribute__((deprecated("Use " #f " instead")))
+#elif __GNUC__ >= 3
+#define LIBUSB_DEPRECATED_FOR(f) __attribute__((deprecated))
 #else
 #define LIBUSB_DEPRECATED_FOR(f)
 #endif /* __GNUC__ */
@@ -141,7 +149,7 @@ typedef unsigned __int32  uint32_t;
  * Internally, LIBUSB_API_VERSION is defined as follows:
  * (libusb major << 24) | (libusb minor << 16) | (16 bit incremental)
  */
-#define LIBUSB_API_VERSION 0x01000105
+#define LIBUSB_API_VERSION 0x01000106
 
 /* The following is kept for compatibility, but will be deprecated in the future */
 #define LIBUSBX_API_VERSION LIBUSB_API_VERSION
@@ -729,13 +737,7 @@ struct libusb_bos_dev_capability_descriptor {
 	/** Device Capability type */
 	uint8_t bDevCapabilityType;
 	/** Device Capability data (bLength - 3 bytes) */
-	uint8_t dev_capability_data
-#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
-	[] /* valid C99 code */
-#else
-	[0] /* non-standard, but usually working code */
-#endif
-	;
+	uint8_t dev_capability_data[ZERO_SIZED_ARRAY];
 };
 
 /** \ingroup libusb_desc
@@ -760,13 +762,7 @@ struct libusb_bos_descriptor {
 	uint8_t  bNumDeviceCaps;
 
 	/** bNumDeviceCap Device Capability Descriptors */
-	struct libusb_bos_dev_capability_descriptor *dev_capability
-#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
-	[] /* valid C99 code */
-#else
-	[0] /* non-standard, but usually working code */
-#endif
-	;
+	struct libusb_bos_dev_capability_descriptor *dev_capability[ZERO_SIZED_ARRAY];
 };
 
 /** \ingroup libusb_desc
@@ -927,7 +923,7 @@ struct libusb_version {
  * sessions allows for your program to use two libraries (or dynamically
  * load two modules) which both independently use libusb. This will prevent
  * interference between the individual libusb users - for example
- * libusb_set_debug() will not affect the other user of the library, and
+ * libusb_set_option() will not affect the other user of the library, and
  * libusb_exit() will not destroy resources that the other user is still
  * using.
  *
@@ -987,6 +983,9 @@ enum libusb_speed {
 
 	/** The device is operating at super speed (5000MBit/s). */
 	LIBUSB_SPEED_SUPER = 4,
+
+	/** The device is operating at super speed plus (10000MBit/s). */
+	LIBUSB_SPEED_SUPER_PLUS = 5,
 };
 
 /** \ingroup libusb_dev
@@ -1256,13 +1255,7 @@ struct libusb_transfer {
 	int num_iso_packets;
 
 	/** Isochronous packet descriptors, for isochronous transfers only. */
-	struct libusb_iso_packet_descriptor iso_packet_desc
-#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
-	[] /* valid C99 code */
-#else
-	[0] /* non-standard, but usually working code */
-#endif
-	;
+	struct libusb_iso_packet_descriptor iso_packet_desc[ZERO_SIZED_ARRAY];
 };
 
 /** \ingroup libusb_misc
@@ -1290,21 +1283,20 @@ enum libusb_capability {
  *  - LIBUSB_LOG_LEVEL_NONE (0)    : no messages ever printed by the library (default)
  *  - LIBUSB_LOG_LEVEL_ERROR (1)   : error messages are printed to stderr
  *  - LIBUSB_LOG_LEVEL_WARNING (2) : warning and error messages are printed to stderr
- *  - LIBUSB_LOG_LEVEL_INFO (3)    : informational messages are printed to stdout, warning
- *    and error messages are printed to stderr
- *  - LIBUSB_LOG_LEVEL_DEBUG (4)   : debug and informational messages are printed to stdout,
- *    warnings and errors to stderr
+ *  - LIBUSB_LOG_LEVEL_INFO (3)    : informational messages are printed to stderr
+ *  - LIBUSB_LOG_LEVEL_DEBUG (4)   : debug and informational messages are printed to stderr
  */
 enum libusb_log_level {
 	LIBUSB_LOG_LEVEL_NONE = 0,
-	LIBUSB_LOG_LEVEL_ERROR,
-	LIBUSB_LOG_LEVEL_WARNING,
-	LIBUSB_LOG_LEVEL_INFO,
-	LIBUSB_LOG_LEVEL_DEBUG,
+	LIBUSB_LOG_LEVEL_ERROR = 1,
+	LIBUSB_LOG_LEVEL_WARNING = 2,
+	LIBUSB_LOG_LEVEL_INFO = 3,
+	LIBUSB_LOG_LEVEL_DEBUG = 4,
 };
 
 int LIBUSB_CALL libusb_init(libusb_context **ctx);
 void LIBUSB_CALL libusb_exit(libusb_context *ctx);
+LIBUSB_DEPRECATED_FOR(libusb_set_option)
 void LIBUSB_CALL libusb_set_debug(libusb_context *ctx, int level);
 const struct libusb_version * LIBUSB_CALL libusb_get_version(void);
 int LIBUSB_CALL libusb_has_capability(uint32_t capability);
@@ -2001,6 +1993,45 @@ int LIBUSB_CALL libusb_hotplug_register_callback(libusb_context *ctx,
 void LIBUSB_CALL libusb_hotplug_deregister_callback(libusb_context *ctx,
 						libusb_hotplug_callback_handle callback_handle);
 
+/** \ingroup libusb_lib
+ * Available option values for libusb_set_option().
+ */
+enum libusb_option {
+	/** Set the log message verbosity.
+	 *
+	 * The default level is LIBUSB_LOG_LEVEL_NONE, which means no messages are ever
+	 * printed. If you choose to increase the message verbosity level, ensure
+	 * that your application does not close the stderr file descriptor.
+	 *
+	 * You are advised to use level LIBUSB_LOG_LEVEL_WARNING. libusb is conservative
+	 * with its message logging and most of the time, will only log messages that
+	 * explain error conditions and other oddities. This will help you debug
+	 * your software.
+	 *
+	 * If the LIBUSB_DEBUG environment variable was set when libusb was
+	 * initialized, this function does nothing: the message verbosity is fixed
+	 * to the value in the environment variable.
+	 *
+	 * If libusb was compiled without any message logging, this function does
+	 * nothing: you'll never get any messages.
+	 *
+	 * If libusb was compiled with verbose debug message logging, this function
+	 * does nothing: you'll always get messages from all levels.
+	 */
+	LIBUSB_OPTION_LOG_LEVEL,
+
+	/** Use the UsbDk backend for a specific context, if available.
+	 *
+	 * This option should be set immediately after calling libusb_init(), otherwise
+	 * unspecified behavior may occur.
+	 *
+	 * Only valid on Windows.
+	 */
+	LIBUSB_OPTION_USE_USBDK,
+};
+
+int LIBUSB_CALL libusb_set_option(libusb_context *ctx, enum libusb_option option, ...);
+
 #ifdef __cplusplus
 }
 #endif

+ 89 - 73
vendor/github.com/karalabe/hid/libusb/libusb/libusbi.h → vendor/github.com/karalabe/usb/libusb/libusb/libusbi.h

@@ -39,6 +39,20 @@
 #include "libusb.h"
 #include "version.h"
 
+/* Attribute to ensure that a structure member is aligned to a natural
+ * pointer alignment. Used for os_priv member. */
+#if defined(_MSC_VER)
+#if defined(_WIN64)
+#define PTR_ALIGNED __declspec(align(8))
+#else
+#define PTR_ALIGNED __declspec(align(4))
+#endif
+#elif defined(__GNUC__)
+#define PTR_ALIGNED __attribute__((aligned(sizeof(void *))))
+#else
+#define PTR_ALIGNED
+#endif
+
 /* Inside the libusb code, mark all public functions as follows:
  *   return_type API_EXPORTED function_name(params) { ... }
  * But if the function returns a pointer, mark it as follows:
@@ -139,6 +153,19 @@ static inline void list_del(struct list_head *entry)
 	entry->next = entry->prev = NULL;
 }
 
+static inline void list_cut(struct list_head *list, struct list_head *head)
+{
+	if (list_empty(head))
+		return;
+
+	list->next = head->next;
+	list->next->prev = list;
+	list->prev = head->prev;
+	list->prev->next = list;
+
+	list_init(head);
+}
+
 static inline void *usbi_reallocf(void *ptr, size_t size)
 {
 	void *ret = realloc(ptr, size);
@@ -151,6 +178,9 @@ static inline void *usbi_reallocf(void *ptr, size_t size)
 	const typeof( ((type *)0)->member ) *mptr = (ptr);	\
 	(type *)( (char *)mptr - offsetof(type,member) );})
 
+#ifndef CLAMP
+#define CLAMP(val, min, max) ((val) < (min) ? (min) : ((val) > (max) ? (max) : (val)))
+#endif
 #ifndef MIN
 #define MIN(a, b)	((a) < (b) ? (a) : (b))
 #endif
@@ -175,29 +205,33 @@ static inline void *usbi_reallocf(void *ptr, size_t size)
 	} while (0)
 #endif
 
+#ifdef ENABLE_LOGGING
+
+#if defined(_MSC_VER) && (_MSC_VER < 1900)
+#define snprintf usbi_snprintf
+#define vsnprintf usbi_vsnprintf
+int usbi_snprintf(char *dst, size_t size, const char *format, ...);
+int usbi_vsnprintf(char *dst, size_t size, const char *format, va_list ap);
+#define LIBUSB_PRINTF_WIN32
+#endif /* defined(_MSC_VER) && (_MSC_VER < 1900) */
+
 void usbi_log(struct libusb_context *ctx, enum libusb_log_level level,
 	const char *function, const char *format, ...);
 
 void usbi_log_v(struct libusb_context *ctx, enum libusb_log_level level,
 	const char *function, const char *format, va_list args);
 
-#if !defined(_MSC_VER) || _MSC_VER >= 1400
+#if !defined(_MSC_VER) || (_MSC_VER >= 1400)
 
-#ifdef ENABLE_LOGGING
 #define _usbi_log(ctx, level, ...) usbi_log(ctx, level, __FUNCTION__, __VA_ARGS__)
-#define usbi_dbg(...) _usbi_log(NULL, LIBUSB_LOG_LEVEL_DEBUG, __VA_ARGS__)
-#else
-#define _usbi_log(ctx, level, ...) do { (void)(ctx); } while(0)
-#define usbi_dbg(...) do {} while(0)
-#endif
 
-#define usbi_info(ctx, ...) _usbi_log(ctx, LIBUSB_LOG_LEVEL_INFO, __VA_ARGS__)
-#define usbi_warn(ctx, ...) _usbi_log(ctx, LIBUSB_LOG_LEVEL_WARNING, __VA_ARGS__)
 #define usbi_err(ctx, ...) _usbi_log(ctx, LIBUSB_LOG_LEVEL_ERROR, __VA_ARGS__)
+#define usbi_warn(ctx, ...) _usbi_log(ctx, LIBUSB_LOG_LEVEL_WARNING, __VA_ARGS__)
+#define usbi_info(ctx, ...) _usbi_log(ctx, LIBUSB_LOG_LEVEL_INFO, __VA_ARGS__)
+#define usbi_dbg(...) _usbi_log(NULL, LIBUSB_LOG_LEVEL_DEBUG, __VA_ARGS__)
 
-#else /* !defined(_MSC_VER) || _MSC_VER >= 1400 */
+#else /* !defined(_MSC_VER) || (_MSC_VER >= 1400) */
 
-#ifdef ENABLE_LOGGING
 #define LOG_BODY(ctxt, level)				\
 {							\
 	va_list args;					\
@@ -205,24 +239,26 @@ void usbi_log_v(struct libusb_context *ctx, enum libusb_log_level level,
 	usbi_log_v(ctxt, level, "", format, args);	\
 	va_end(args);					\
 }
-#else
-#define LOG_BODY(ctxt, level)				\
-{							\
-	(void)(ctxt);					\
-}
-#endif
 
-static inline void usbi_info(struct libusb_context *ctx, const char *format, ...)
-	LOG_BODY(ctx, LIBUSB_LOG_LEVEL_INFO)
-static inline void usbi_warn(struct libusb_context *ctx, const char *format, ...)
-	LOG_BODY(ctx, LIBUSB_LOG_LEVEL_WARNING)
 static inline void usbi_err(struct libusb_context *ctx, const char *format, ...)
 	LOG_BODY(ctx, LIBUSB_LOG_LEVEL_ERROR)
-
+static inline void usbi_warn(struct libusb_context *ctx, const char *format, ...)
+	LOG_BODY(ctx, LIBUSB_LOG_LEVEL_WARNING)
+static inline void usbi_info(struct libusb_context *ctx, const char *format, ...)
+	LOG_BODY(ctx, LIBUSB_LOG_LEVEL_INFO)
 static inline void usbi_dbg(const char *format, ...)
 	LOG_BODY(NULL, LIBUSB_LOG_LEVEL_DEBUG)
 
-#endif /* !defined(_MSC_VER) || _MSC_VER >= 1400 */
+#endif /* !defined(_MSC_VER) || (_MSC_VER >= 1400) */
+
+#else /* ENABLE_LOGGING */
+
+#define usbi_err(ctx, ...) do { (void)ctx; } while (0)
+#define usbi_warn(ctx, ...) do { (void)ctx; } while (0)
+#define usbi_info(ctx, ...) do { (void)ctx; } while (0)
+#define usbi_dbg(...) do {} while (0)
+
+#endif /* ENABLE_LOGGING */
 
 #define USBI_GET_CONTEXT(ctx)				\
 	do {						\
@@ -254,8 +290,10 @@ extern struct libusb_context *usbi_default_context;
 struct pollfd;
 
 struct libusb_context {
-	int debug;
+#if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING)
+	enum libusb_log_level debug;
 	int debug_fixed;
+#endif
 
 	/* internal event pipe, used for signalling occurrence of an internal event. */
 	int event_pipe[2];
@@ -270,6 +308,7 @@ struct libusb_context {
 
 	/* A list of registered hotplug callbacks */
 	struct list_head hotplug_cbs;
+	libusb_hotplug_callback_handle next_hotplug_cb_handle;
 	usbi_mutex_t hotplug_cbs_lock;
 
 	/* this is a list of in-flight transfer handles, sorted by timeout
@@ -331,6 +370,8 @@ struct libusb_context {
 #endif
 
 	struct list_head list;
+
+	PTR_ALIGNED unsigned char os_priv[ZERO_SIZED_ARRAY];
 };
 
 enum usbi_event_flags {
@@ -339,6 +380,9 @@ enum usbi_event_flags {
 
 	/* The user has interrupted the event handler */
 	USBI_EVENT_USER_INTERRUPT = 1 << 1,
+
+	/* A hotplug callback deregistration is pending */
+	USBI_EVENT_HOTPLUG_CB_DEREGISTERED = 1 << 2,
 };
 
 /* Macros for managing event handling state */
@@ -383,17 +427,7 @@ struct libusb_device {
 	struct libusb_device_descriptor device_descriptor;
 	int attached;
 
-	unsigned char os_priv
-#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
-	[] /* valid C99 code */
-#else
-	[0] /* non-standard, but usually working code */
-#endif
-#if defined(OS_SUNOS)
-	__attribute__ ((aligned (8)));
-#else
-	;
-#endif
+	PTR_ALIGNED unsigned char os_priv[ZERO_SIZED_ARRAY];
 };
 
 struct libusb_device_handle {
@@ -404,17 +438,8 @@ struct libusb_device_handle {
 	struct list_head list;
 	struct libusb_device *dev;
 	int auto_detach_kernel_driver;
-	unsigned char os_priv
-#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
-	[] /* valid C99 code */
-#else
-	[0] /* non-standard, but usually working code */
-#endif
-#if defined(OS_SUNOS)
-	__attribute__ ((aligned (8)));
-#else
-	;
-#endif
+
+	PTR_ALIGNED unsigned char os_priv[ZERO_SIZED_ARRAY];
 };
 
 enum {
@@ -540,14 +565,6 @@ int usbi_clear_event(struct libusb_context *ctx);
 #include "os/poll_windows.h"
 #endif
 
-#if defined(_MSC_VER) && (_MSC_VER < 1900)
-#define snprintf usbi_snprintf
-#define vsnprintf usbi_vsnprintf
-int usbi_snprintf(char *dst, size_t size, const char *format, ...);
-int usbi_vsnprintf(char *dst, size_t size, const char *format, va_list ap);
-#define LIBUSB_PRINTF_WIN32
-#endif
-
 struct usbi_pollfd {
 	/* must come first */
 	struct libusb_pollfd pollfd;
@@ -568,13 +585,7 @@ void usbi_remove_pollfd(struct libusb_context *ctx, int fd);
 struct discovered_devs {
 	size_t len;
 	size_t capacity;
-	struct libusb_device *devices
-#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
-	[] /* valid C99 code */
-#else
-	[0] /* non-standard, but usually working code */
-#endif
-	;
+	struct libusb_device *devices[ZERO_SIZED_ARRAY];
 };
 
 struct discovered_devs *discovered_devs_append(
@@ -607,7 +618,17 @@ struct usbi_os_backend {
 	 *
 	 * This function is called when the user deinitializes the library.
 	 */
-	void (*exit)(void);
+	void (*exit)(struct libusb_context *ctx);
+
+	/* Set a backend-specific option. Optional.
+	 *
+	 * This function is called when the user calls libusb_set_option() and
+	 * the option is not handled by the core library.
+	 *
+	 * Return 0 on success, or a LIBUSB_ERROR code on failure.
+	 */
+	int (*set_option)(struct libusb_context *ctx, enum libusb_option option,
+		va_list args);
 
 	/* Enumerate all the USB devices on the system, returning them in a list
 	 * of discovered devices.
@@ -1110,6 +1131,11 @@ struct usbi_os_backend {
 	clockid_t (*get_timerfd_clockid)(void);
 #endif
 
+	/* Number of bytes to reserve for per-context private backend data.
+	 * This private data area is accessible through the "os_priv" field of
+	 * struct libusb_context. */
+	size_t context_priv_size;
+
 	/* Number of bytes to reserve for per-device private backend data.
 	 * This private data area is accessible through the "os_priv" field of
 	 * struct libusb_device. */
@@ -1127,17 +1153,7 @@ struct usbi_os_backend {
 	size_t transfer_priv_size;
 };
 
-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 openbsd_backend;
-extern const struct usbi_os_backend netbsd_backend;
-extern const struct usbi_os_backend windows_backend;
-extern const struct usbi_os_backend usbdk_backend;
-extern const struct usbi_os_backend wince_backend;
-extern const struct usbi_os_backend haiku_usb_raw_backend;
-extern const struct usbi_os_backend sunos_backend;
+extern const struct usbi_os_backend usbi_backend;
 
 extern struct list_head active_contexts_list;
 extern usbi_mutex_static_t active_contexts_lock;

+ 75 - 27
vendor/github.com/karalabe/hid/libusb/libusb/os/darwin_usb.c → vendor/github.com/karalabe/usb/libusb/libusb/os/darwin_usb.c

@@ -1,7 +1,7 @@
 /* -*- Mode: C; indent-tabs-mode:nil -*- */
 /*
  * darwin backend for libusb 1.0
- * Copyright © 2008-2016 Nathan Hjelm <hjelmn@users.sourceforge.net>
+ * Copyright © 2008-2017 Nathan Hjelm <hjelmn@users.sourceforge.net>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -36,6 +36,10 @@
 #include <mach/mach_host.h>
 #include <mach/mach_port.h>
 
+/* Suppress warnings about the use of the deprecated objc_registerThreadWithCollector
+ * function. Its use is also conditionalized to only older deployment targets. */
+#define OBJC_SILENCE_GC_DEPRECATIONS 1
+
 #include <AvailabilityMacros.h>
 #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 && MAC_OS_X_VERSION_MIN_REQUIRED < 101200
   #include <objc/objc-auto.h>
@@ -55,6 +59,14 @@ _Atomic int32_t initCount = ATOMIC_VAR_INIT(0);
 #define libusb_darwin_atomic_fetch_add(x, y) (OSAtomicAdd32Barrier(y, x) - y)
 
 static volatile int32_t initCount = 0;
+
+#endif
+
+/* On 10.12 and later, use newly available clock_*() functions */
+#if MAC_OS_X_VERSION_MIN_REQUIRED >= 101200
+#define OSX_USE_CLOCK_GETTIME 1
+#else
+#define OSX_USE_CLOCK_GETTIME 0
 #endif
 
 #include "darwin_usb.h"
@@ -65,15 +77,17 @@ static pthread_cond_t  libusb_darwin_at_cond = PTHREAD_COND_INITIALIZER;
 
 static pthread_once_t darwin_init_once = PTHREAD_ONCE_INIT;
 
+#if !OSX_USE_CLOCK_GETTIME
 static clock_serv_t clock_realtime;
 static clock_serv_t clock_monotonic;
+#endif
 
 static CFRunLoopRef libusb_darwin_acfl = NULL; /* event cf loop */
 static CFRunLoopSourceRef libusb_darwin_acfls = NULL; /* shutdown signal for event cf loop */
 
 static usbi_mutex_t darwin_cached_devices_lock = PTHREAD_MUTEX_INITIALIZER;
 static struct list_head darwin_cached_devices = {&darwin_cached_devices, &darwin_cached_devices};
-static char *darwin_device_class = kIOUSBDeviceClassName;
+static const char *darwin_device_class = kIOUSBDeviceClassName;
 
 #define DARWIN_CACHED_DEVICE(a) ((struct darwin_cached_device *) (((struct darwin_device_priv *)((a)->os_priv))->dev))
 
@@ -219,20 +233,21 @@ static int usb_setup_device_iterator (io_iterator_t *deviceIterator, UInt32 loca
                                                                          &kCFTypeDictionaryKeyCallBacks,
                                                                          &kCFTypeDictionaryValueCallBacks);
 
-    if (propertyMatchDict) {
-      /* there are no unsigned CFNumber types so treat the value as signed. the os seems to do this
-         internally (CFNumberType of locationID is 3) */
-      CFTypeRef locationCF = CFNumberCreate (NULL, kCFNumberSInt32Type, &location);
+    /* there are no unsigned CFNumber types so treat the value as signed. the OS seems to do this
+         internally (CFNumberType of locationID is kCFNumberSInt32Type) */
+    CFTypeRef locationCF = CFNumberCreate (NULL, kCFNumberSInt32Type, &location);
 
+    if (propertyMatchDict && locationCF) {
       CFDictionarySetValue (propertyMatchDict, CFSTR(kUSBDevicePropertyLocationID), locationCF);
-      /* release our reference to the CFNumber (CFDictionarySetValue retains it) */
-      CFRelease (locationCF);
-
       CFDictionarySetValue (matchingDict, CFSTR(kIOPropertyMatchKey), propertyMatchDict);
-      /* release out reference to the CFMutableDictionaryRef (CFDictionarySetValue retains it) */
-      CFRelease (propertyMatchDict);
     }
     /* else we can still proceed as long as the caller accounts for the possibility of other devices in the iterator */
+
+    /* release our references as per the Create Rule */
+    if (propertyMatchDict)
+      CFRelease (propertyMatchDict);
+    if (locationCF)
+      CFRelease (locationCF);
   }
 
   return IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, deviceIterator);
@@ -300,6 +315,7 @@ static usb_device_t **darwin_device_from_service (io_service_t service)
 }
 
 static void darwin_devices_attached (void *ptr, io_iterator_t add_devices) {
+  UNUSED(ptr);
   struct libusb_context *ctx;
   io_service_t service;
 
@@ -308,7 +324,7 @@ static void darwin_devices_attached (void *ptr, io_iterator_t add_devices) {
   while ((service = IOIteratorNext(add_devices))) {
     /* add this device to each active context's device list */
     list_for_each_entry(ctx, &active_contexts_list, list, struct libusb_context) {
-      process_new_device (ctx, service);;
+      process_new_device (ctx, service);
     }
 
     IOObjectRelease(service);
@@ -318,6 +334,7 @@ static void darwin_devices_attached (void *ptr, io_iterator_t add_devices) {
 }
 
 static void darwin_devices_detached (void *ptr, io_iterator_t rem_devices) {
+  UNUSED(ptr);
   struct libusb_device *dev = NULL;
   struct libusb_context *ctx;
   struct darwin_cached_device *old_device;
@@ -516,7 +533,6 @@ static void darwin_check_version (void) {
 }
 
 static int darwin_init(struct libusb_context *ctx) {
-  host_name_port_t host_self;
   int rc;
 
   rc = pthread_once (&darwin_init_once, darwin_check_version);
@@ -530,12 +546,15 @@ static int darwin_init(struct libusb_context *ctx) {
   }
 
   if (libusb_darwin_atomic_fetch_add (&initCount, 1) == 0) {
-    /* create the clocks that will be used */
+#if !OSX_USE_CLOCK_GETTIME
+    /* create the clocks that will be used if clock_gettime() is not available */
+    host_name_port_t host_self;
 
     host_self = mach_host_self();
     host_get_clock_service(host_self, CALENDAR_CLOCK, &clock_realtime);
     host_get_clock_service(host_self, SYSTEM_CLOCK, &clock_monotonic);
     mach_port_deallocate(mach_task_self(), host_self);
+#endif
 
     pthread_create (&libusb_darwin_at, NULL, darwin_event_thread_main, ctx);
 
@@ -548,10 +567,13 @@ static int darwin_init(struct libusb_context *ctx) {
   return rc;
 }
 
-static void darwin_exit (void) {
+static void darwin_exit (struct libusb_context *ctx) {
+  UNUSED(ctx);
   if (libusb_darwin_atomic_fetch_add (&initCount, -1) == 1) {
+#if !OSX_USE_CLOCK_GETTIME
     mach_port_deallocate(mach_task_self(), clock_realtime);
     mach_port_deallocate(mach_task_self(), clock_monotonic);
+#endif
 
     /* stop the event runloop and wait for the thread to terminate. */
     CFRunLoopSourceSignal(libusb_darwin_acfls);
@@ -866,14 +888,29 @@ static int get_device_port (io_service_t service, UInt8 *port) {
   return ret;
 }
 
+static int get_device_parent_sessionID(io_service_t service, UInt64 *parent_sessionID) {
+  kern_return_t result;
+  io_service_t parent;
+
+  /* Walk up the tree in the IOService plane until we find a parent that has a sessionID */
+  parent = service;
+  while((result = IORegistryEntryGetParentEntry (parent, kIOServicePlane, &parent)) == kIOReturnSuccess) {
+    if (get_ioregistry_value_number (parent, CFSTR("sessionID"), kCFNumberSInt64Type, parent_sessionID)) {
+        /* Success */
+        return 1;
+    }
+  }
+
+  /* We ran out of parents */
+  return 0;
+}
+
 static int darwin_get_cached_device(struct libusb_context *ctx, io_service_t service,
                                     struct darwin_cached_device **cached_out) {
   struct darwin_cached_device *new_device;
   UInt64 sessionID = 0, parent_sessionID = 0;
   int ret = LIBUSB_SUCCESS;
   usb_device_t **device;
-  io_service_t parent;
-  kern_return_t result;
   UInt8 port = 0;
 
   /* get some info from the io registry */
@@ -884,11 +921,8 @@ static int darwin_get_cached_device(struct libusb_context *ctx, io_service_t ser
 
   usbi_dbg("finding cached device for sessionID 0x%" PRIx64, sessionID);
 
-  result = IORegistryEntryGetParentEntry (service, kIOUSBPlane, &parent);
-
-  if (kIOReturnSuccess == result) {
-    (void) get_ioregistry_value_number (parent, CFSTR("sessionID"), kCFNumberSInt64Type, &parent_sessionID);
-    IOObjectRelease(parent);
+  if (get_device_parent_sessionID(service, &parent_sessionID)) {
+    usbi_dbg("parent sessionID: 0x%" PRIx64, parent_sessionID);
   }
 
   usbi_mutex_lock(&darwin_cached_devices_lock);
@@ -1005,8 +1039,11 @@ static int process_new_device (struct libusb_context *ctx, io_service_t service)
     case kUSBDeviceSpeedLow: dev->speed = LIBUSB_SPEED_LOW; break;
     case kUSBDeviceSpeedFull: dev->speed = LIBUSB_SPEED_FULL; break;
     case kUSBDeviceSpeedHigh: dev->speed = LIBUSB_SPEED_HIGH; break;
-#if DeviceVersion >= 500
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
     case kUSBDeviceSpeedSuper: dev->speed = LIBUSB_SPEED_SUPER; break;
+#endif
+#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101200
+    case kUSBDeviceSpeedSuperPlus: dev->speed = LIBUSB_SPEED_SUPER_PLUS; break;
 #endif
     default:
       usbi_warn (ctx, "Got unknown device speed %d", devSpeed);
@@ -1216,9 +1253,9 @@ static int get_endpoints (struct libusb_device_handle *dev_handle, int iface) {
 
   kern_return_t kresult;
 
-  u_int8_t numep, direction, number;
-  u_int8_t dont_care1, dont_care3;
-  u_int16_t dont_care2;
+  UInt8 numep, direction, number;
+  UInt8 dont_care1, dont_care3;
+  UInt16 dont_care2;
   int rc;
 
   usbi_dbg ("building table of endpoints.");
@@ -1965,6 +2002,7 @@ static int darwin_handle_transfer_completion (struct usbi_transfer *itransfer) {
 }
 
 static int darwin_clock_gettime(int clk_id, struct timespec *tp) {
+#if !OSX_USE_CLOCK_GETTIME
   mach_timespec_t sys_time;
   clock_serv_t clock_ref;
 
@@ -1987,6 +2025,16 @@ static int darwin_clock_gettime(int clk_id, struct timespec *tp) {
   tp->tv_nsec = sys_time.tv_nsec;
 
   return 0;
+#else
+  switch (clk_id) {
+  case USBI_CLOCK_MONOTONIC:
+    return clock_gettime(CLOCK_MONOTONIC, tp);
+  case USBI_CLOCK_REALTIME:
+    return clock_gettime(CLOCK_REALTIME, tp);
+  default:
+    return LIBUSB_ERROR_INVALID_PARAM;
+  }
+#endif
 }
 
 #if InterfaceVersion >= 550
@@ -2047,7 +2095,7 @@ static int darwin_free_streams (struct libusb_device_handle *dev_handle, unsigne
 }
 #endif
 
-const struct usbi_os_backend darwin_backend = {
+const struct usbi_os_backend usbi_backend = {
         .name = "Darwin",
         .caps = 0,
         .init = darwin_init,

+ 48 - 13
vendor/github.com/karalabe/hid/libusb/libusb/os/darwin_usb.h → vendor/github.com/karalabe/usb/libusb/libusb/os/darwin_usb.h

@@ -28,37 +28,58 @@
 #include <IOKit/IOCFPlugIn.h>
 
 /* IOUSBInterfaceInferface */
-#if defined (kIOUSBInterfaceInterfaceID700) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_9
+
+/* New in OS 10.12.0. */
+#if defined (kIOUSBInterfaceInterfaceID800) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 101200)
+
+#define usb_interface_t IOUSBInterfaceInterface800
+#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID800
+#define InterfaceVersion 800
+
+/* New in OS 10.10.0. */
+#elif defined (kIOUSBInterfaceInterfaceID700) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 101000)
 
 #define usb_interface_t IOUSBInterfaceInterface700
 #define InterfaceInterfaceID kIOUSBInterfaceInterfaceID700
 #define InterfaceVersion 700
 
-#elif defined (kIOUSBInterfaceInterfaceID550) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_9
+/* New in OS 10.9.0. */
+#elif defined (kIOUSBInterfaceInterfaceID650) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1090)
+
+#define usb_interface_t IOUSBInterfaceInterface650
+#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID650
+#define InterfaceVersion 650
+
+/* New in OS 10.8.2 but can't test deployment target to that granularity, so round up. */
+#elif defined (kIOUSBInterfaceInterfaceID550) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1090)
 
 #define usb_interface_t IOUSBInterfaceInterface550
 #define InterfaceInterfaceID kIOUSBInterfaceInterfaceID550
 #define InterfaceVersion 550
 
-#elif defined (kIOUSBInterfaceInterfaceID500)
+/* New in OS 10.7.3 but can't test deployment target to that granularity, so round up. */
+#elif defined (kIOUSBInterfaceInterfaceID500) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1080)
 
 #define usb_interface_t IOUSBInterfaceInterface500
 #define InterfaceInterfaceID kIOUSBInterfaceInterfaceID500
 #define InterfaceVersion 500
 
-#elif defined (kIOUSBInterfaceInterfaceID300)
+/* New in OS 10.5.0. */
+#elif defined (kIOUSBInterfaceInterfaceID300) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1050)
 
 #define usb_interface_t IOUSBInterfaceInterface300
 #define InterfaceInterfaceID kIOUSBInterfaceInterfaceID300
 #define InterfaceVersion 300
 
-#elif defined (kIOUSBInterfaceInterfaceID245)
+/* New in OS 10.4.5 (or 10.4.6?) but can't test deployment target to that granularity, so round up. */
+#elif defined (kIOUSBInterfaceInterfaceID245) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1050)
 
 #define usb_interface_t IOUSBInterfaceInterface245
 #define InterfaceInterfaceID kIOUSBInterfaceInterfaceID245
 #define InterfaceVersion 245
 
-#elif defined (kIOUSBInterfaceInterfaceID220)
+/* New in OS 10.4.0. */
+#elif defined (kIOUSBInterfaceInterfaceID220) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1040)
 
 #define usb_interface_t IOUSBInterfaceInterface220
 #define InterfaceInterfaceID kIOUSBInterfaceInterfaceID220
@@ -66,43 +87,57 @@
 
 #else
 
-#error "IOUSBFamily is too old. Please upgrade your OS"
+#error "IOUSBFamily is too old. Please upgrade your SDK and/or deployment target"
 
 #endif
 
 /* IOUSBDeviceInterface */
-#if defined (kIOUSBDeviceInterfaceID500) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_9
+
+/* New in OS 10.9.0. */
+#if defined (kIOUSBDeviceInterfaceID650) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1090)
+
+#define usb_device_t    IOUSBDeviceInterface650
+#define DeviceInterfaceID kIOUSBDeviceInterfaceID650
+#define DeviceVersion 650
+
+/* New in OS 10.7.3 but can't test deployment target to that granularity, so round up. */
+#elif defined (kIOUSBDeviceInterfaceID500) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1080)
 
 #define usb_device_t    IOUSBDeviceInterface500
 #define DeviceInterfaceID kIOUSBDeviceInterfaceID500
 #define DeviceVersion 500
 
-#elif defined (kIOUSBDeviceInterfaceID320)
+/* New in OS 10.5.4 but can't test deployment target to that granularity, so round up. */
+#elif defined (kIOUSBDeviceInterfaceID320) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1060)
 
 #define usb_device_t    IOUSBDeviceInterface320
 #define DeviceInterfaceID kIOUSBDeviceInterfaceID320
 #define DeviceVersion 320
 
-#elif defined (kIOUSBDeviceInterfaceID300)
+/* New in OS 10.5.0. */
+#elif defined (kIOUSBDeviceInterfaceID300) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1050)
 
 #define usb_device_t    IOUSBDeviceInterface300
 #define DeviceInterfaceID kIOUSBDeviceInterfaceID300
 #define DeviceVersion 300
 
-#elif defined (kIOUSBDeviceInterfaceID245)
+/* New in OS 10.4.5 (or 10.4.6?) but can't test deployment target to that granularity, so round up. */
+#elif defined (kIOUSBDeviceInterfaceID245) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1050)
 
 #define usb_device_t    IOUSBDeviceInterface245
 #define DeviceInterfaceID kIOUSBDeviceInterfaceID245
 #define DeviceVersion 245
 
-#elif defined (kIOUSBDeviceInterfaceID220)
+/* New in OS 10.2.3 but can't test deployment target to that granularity, so round up. */
+#elif defined (kIOUSBDeviceInterfaceID197) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1030)
+
 #define usb_device_t    IOUSBDeviceInterface197
 #define DeviceInterfaceID kIOUSBDeviceInterfaceID197
 #define DeviceVersion 197
 
 #else
 
-#error "IOUSBFamily is too old. Please upgrade your OS"
+#error "IOUSBFamily is too old. Please upgrade your SDK and/or deployment target"
 
 #endif
 

+ 0 - 0
vendor/github.com/karalabe/hid/libusb/libusb/os/haiku_pollfs.cpp → vendor/github.com/karalabe/usb/libusb/libusb/os/haiku_pollfs.cpp


+ 0 - 0
vendor/github.com/karalabe/hid/libusb/libusb/os/haiku_usb.h → vendor/github.com/karalabe/usb/libusb/libusb/os/haiku_usb.h


+ 0 - 0
vendor/github.com/karalabe/hid/libusb/libusb/os/haiku_usb_backend.cpp → vendor/github.com/karalabe/usb/libusb/libusb/os/haiku_usb_backend.cpp


+ 5 - 2
vendor/github.com/karalabe/hid/libusb/libusb/os/haiku_usb_raw.cpp → vendor/github.com/karalabe/usb/libusb/libusb/os/haiku_usb_raw.cpp

@@ -38,8 +38,9 @@ haiku_init(struct libusb_context *ctx)
 }
 
 static void
-haiku_exit(void)
+haiku_exit(struct libusb_context *ctx)
 {
+	UNUSED(ctx);
 	if (atomic_add(&gInitCount, -1) == 1)
 		gUsbRoster.Stop();
 }
@@ -195,11 +196,12 @@ haiku_clock_gettime(int clkid, struct timespec *tp)
 	return LIBUSB_ERROR_INVALID_PARAM;
 }
 
-const struct usbi_os_backend haiku_usb_raw_backend = {
+const struct usbi_os_backend usbi_backend = {
 	/*.name =*/ "Haiku usbfs",
 	/*.caps =*/ 0,
 	/*.init =*/ haiku_init,
 	/*.exit =*/ haiku_exit,
+	/*.set_option =*/ NULL,
 	/*.get_device_list =*/ NULL,
 	/*.hotplug_poll =*/ NULL,
 	/*.open =*/ haiku_open,
@@ -244,6 +246,7 @@ const struct usbi_os_backend haiku_usb_raw_backend = {
 	/*.get_timerfd_clockid =*/ NULL,
 #endif
 
+	/*.context_priv_size=*/ 0,
 	/*.device_priv_size =*/ sizeof(USBDevice *),
 	/*.device_handle_priv_size =*/ sizeof(USBDeviceHandle *),
 	/*.transfer_priv_size =*/ sizeof(USBTransfer *),

+ 0 - 0
vendor/github.com/karalabe/hid/libusb/libusb/os/haiku_usb_raw.h → vendor/github.com/karalabe/usb/libusb/libusb/os/haiku_usb_raw.h


+ 37 - 28
vendor/github.com/karalabe/hid/libusb/libusb/os/linux_netlink.c → vendor/github.com/karalabe/usb/libusb/libusb/os/linux_netlink.c

@@ -45,24 +45,33 @@
 
 #define NL_GROUP_KERNEL 1
 
+#ifndef SOCK_CLOEXEC
+#define SOCK_CLOEXEC	0
+#endif
+
+#ifndef SOCK_NONBLOCK
+#define SOCK_NONBLOCK	0
+#endif
+
 static int linux_netlink_socket = -1;
 static int netlink_control_pipe[2] = { -1, -1 };
 static pthread_t libusb_linux_event_thread;
 
 static void *linux_netlink_event_thread_main(void *arg);
 
-static int set_fd_cloexec_nb(int fd)
+static int set_fd_cloexec_nb(int fd, int socktype)
 {
 	int flags;
 
 #if defined(FD_CLOEXEC)
-	flags = fcntl(fd, F_GETFD);
-	if (flags == -1) {
-		usbi_err(NULL, "failed to get netlink fd flags (%d)", errno);
-		return -1;
-	}
+	/* Make sure the netlink socket file descriptor is marked as CLOEXEC */
+	if (!(socktype & SOCK_CLOEXEC)) {
+		flags = fcntl(fd, F_GETFD);
+		if (flags == -1) {
+			usbi_err(NULL, "failed to get netlink fd flags (%d)", errno);
+			return -1;
+		}
 
-	if (!(flags & FD_CLOEXEC)) {
 		if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) {
 			usbi_err(NULL, "failed to set netlink fd flags (%d)", errno);
 			return -1;
@@ -70,13 +79,14 @@ static int set_fd_cloexec_nb(int fd)
 	}
 #endif
 
-	flags = fcntl(fd, F_GETFL);
-	if (flags == -1) {
-		usbi_err(NULL, "failed to get netlink fd status flags (%d)", errno);
-		return -1;
-	}
+	/* Make sure the netlink socket is non-blocking */
+	if (!(socktype & SOCK_NONBLOCK)) {
+		flags = fcntl(fd, F_GETFL);
+		if (flags == -1) {
+			usbi_err(NULL, "failed to get netlink fd status flags (%d)", errno);
+			return -1;
+		}
 
-	if (!(flags & O_NONBLOCK)) {
 		if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
 			usbi_err(NULL, "failed to set netlink fd status flags (%d)", errno);
 			return -1;
@@ -89,21 +99,15 @@ static int set_fd_cloexec_nb(int fd)
 int linux_netlink_start_event_monitor(void)
 {
 	struct sockaddr_nl sa_nl = { .nl_family = AF_NETLINK, .nl_groups = NL_GROUP_KERNEL };
-	int socktype = SOCK_RAW;
+	int socktype = SOCK_RAW | SOCK_NONBLOCK | SOCK_CLOEXEC;
 	int opt = 1;
 	int ret;
 
-#if defined(SOCK_CLOEXEC)
-	socktype |= SOCK_CLOEXEC;
-#endif
-#if defined(SOCK_NONBLOCK)
-	socktype |= SOCK_NONBLOCK;
-#endif
-
 	linux_netlink_socket = socket(PF_NETLINK, socktype, NETLINK_KOBJECT_UEVENT);
 	if (linux_netlink_socket == -1 && errno == EINVAL) {
 		usbi_dbg("failed to create netlink socket of type %d, attempting SOCK_RAW", socktype);
-		linux_netlink_socket = socket(PF_NETLINK, SOCK_RAW, NETLINK_KOBJECT_UEVENT);
+		socktype = SOCK_RAW;
+		linux_netlink_socket = socket(PF_NETLINK, socktype, NETLINK_KOBJECT_UEVENT);
 	}
 
 	if (linux_netlink_socket == -1) {
@@ -111,7 +115,7 @@ int linux_netlink_start_event_monitor(void)
 		goto err;
 	}
 
-	ret = set_fd_cloexec_nb(linux_netlink_socket);
+	ret = set_fd_cloexec_nb(linux_netlink_socket, socktype);
 	if (ret == -1)
 		goto err_close_socket;
 
@@ -162,7 +166,7 @@ int linux_netlink_stop_event_monitor(void)
 
 	/* Write some dummy data to the control pipe and
 	 * wait for the thread to exit */
-	r = usbi_write(netlink_control_pipe[1], &dummy, sizeof(dummy));
+	r = write(netlink_control_pipe[1], &dummy, sizeof(dummy));
 	if (r <= 0)
 		usbi_warn(NULL, "netlink control pipe signal failed");
 
@@ -356,7 +360,8 @@ static int linux_netlink_read_message(void)
 static void *linux_netlink_event_thread_main(void *arg)
 {
 	char dummy;
-	ssize_t r;
+	int r;
+	ssize_t nb;
 	struct pollfd fds[] = {
 		{ .fd = netlink_control_pipe[0],
 		  .events = POLLIN },
@@ -368,11 +373,15 @@ static void *linux_netlink_event_thread_main(void *arg)
 
 	usbi_dbg("netlink event thread entering");
 
-	while (poll(fds, 2, -1) >= 0) {
+	while ((r = poll(fds, 2, -1)) >= 0 || errno == EINTR) {
+		if (r < 0) {
+			/* temporary failure */
+			continue;
+		}
 		if (fds[0].revents & POLLIN) {
 			/* activity on control pipe, read the byte and exit */
-			r = usbi_read(netlink_control_pipe[0], &dummy, sizeof(dummy));
-			if (r <= 0)
+			nb = read(netlink_control_pipe[0], &dummy, sizeof(dummy));
+			if (nb <= 0)
 				usbi_warn(NULL, "netlink control pipe read failed");
 			break;
 		}

+ 26 - 8
vendor/github.com/karalabe/hid/libusb/libusb/os/linux_udev.c → vendor/github.com/karalabe/usb/libusb/libusb/os/linux_udev.c

@@ -82,17 +82,33 @@ int linux_udev_start_event_monitor(void)
 
 	udev_monitor_fd = udev_monitor_get_fd(udev_monitor);
 
+#if defined(FD_CLOEXEC)
+	/* Make sure the udev file descriptor is marked as CLOEXEC */
+	r = fcntl(udev_monitor_fd, F_GETFD);
+	if (r == -1) {
+		usbi_err(NULL, "geting udev monitor fd flags (%d)", errno);
+		goto err_free_monitor;
+	}
+	if (!(r & FD_CLOEXEC)) {
+		if (fcntl(udev_monitor_fd, F_SETFD, r | FD_CLOEXEC) == -1) {
+			usbi_err(NULL, "setting udev monitor fd flags (%d)", errno);
+			goto err_free_monitor;
+		}
+	}
+#endif
+
 	/* Some older versions of udev are not non-blocking by default,
 	 * so make sure this is set */
 	r = fcntl(udev_monitor_fd, F_GETFL);
 	if (r == -1) {
-		usbi_err(NULL, "getting udev monitor fd flags (%d)", errno);
+		usbi_err(NULL, "getting udev monitor fd status flags (%d)", errno);
 		goto err_free_monitor;
 	}
-	r = fcntl(udev_monitor_fd, F_SETFL, r | O_NONBLOCK);
-	if (r) {
-		usbi_err(NULL, "setting udev monitor fd flags (%d)", errno);
-		goto err_free_monitor;
+	if (!(r & O_NONBLOCK)) {
+		if (fcntl(udev_monitor_fd, F_SETFL, r | O_NONBLOCK) == -1) {
+			usbi_err(NULL, "setting udev monitor fd status flags (%d)", errno);
+			goto err_free_monitor;
+		}
 	}
 
 	r = usbi_pipe(udev_control_pipe);
@@ -134,7 +150,7 @@ int linux_udev_stop_event_monitor(void)
 
 	/* Write some dummy data to the control pipe and
 	 * wait for the thread to exit */
-	r = usbi_write(udev_control_pipe[1], &dummy, sizeof(dummy));
+	r = write(udev_control_pipe[1], &dummy, sizeof(dummy));
 	if (r <= 0) {
 		usbi_warn(NULL, "udev control pipe signal failed");
 	}
@@ -162,6 +178,7 @@ static void *linux_udev_event_thread_main(void *arg)
 {
 	char dummy;
 	int r;
+	ssize_t nb;
 	struct udev_device* udev_dev;
 	struct pollfd fds[] = {
 		{.fd = udev_control_pipe[0],
@@ -179,8 +196,8 @@ static void *linux_udev_event_thread_main(void *arg)
 		}
 		if (fds[0].revents & POLLIN) {
 			/* activity on control pipe, read the byte and exit */
-			r = usbi_read(udev_control_pipe[0], &dummy, sizeof(dummy));
-			if (r <= 0) {
+			nb = read(udev_control_pipe[0], &dummy, sizeof(dummy));
+			if (nb <= 0) {
 				usbi_warn(NULL, "udev control pipe read failed");
 			}
 			break;
@@ -274,6 +291,7 @@ int linux_udev_scan_devices(struct libusb_context *ctx)
 	udev_enumerate_scan_devices(enumerator);
 	devices = udev_enumerate_get_list_entry(enumerator);
 
+	entry = NULL;
 	udev_list_entry_foreach(entry, devices) {
 		const char *path = udev_list_entry_get_name(entry);
 		uint8_t busnum = 0, devaddr = 0;

+ 162 - 100
vendor/github.com/karalabe/hid/libusb/libusb/os/linux_usbfs.c → vendor/github.com/karalabe/usb/libusb/libusb/os/linux_usbfs.c

@@ -81,6 +81,19 @@ static const char *usbfs_path = NULL;
 /* use usbdev*.* device names in /dev instead of the usbfs bus directories */
 static int usbdev_names = 0;
 
+/* Linux has changed the maximum length of an individual isochronous packet
+ * over time.  Initially this limit was 1,023 bytes, but Linux 2.6.18
+ * (commit 3612242e527eb47ee4756b5350f8bdf791aa5ede) increased this value to
+ * 8,192 bytes to support higher bandwidth devices.  Linux 3.10
+ * (commit e2e2f0ea1c935edcf53feb4c4c8fdb4f86d57dd9) further increased this
+ * value to 49,152 bytes to support super speed devices.
+ */
+static unsigned int max_iso_packet_len = 0;
+
+/* Linux 2.6.23 adds support for O_CLOEXEC when opening files, which marks the
+ * close-on-exec flag in the underlying file descriptor. */
+static int supports_flag_cloexec = -1;
+
 /* Linux 2.6.32 adds support for a bulk continuation URB flag. this basically
  * allows us to mark URBs as being part of a specific logical transfer when
  * we submit them to the kernel. then, on any error except a cancellation, all
@@ -136,6 +149,12 @@ static int detach_kernel_driver_and_claim(struct libusb_device_handle *, int);
 static int linux_default_scan_devices (struct libusb_context *ctx);
 #endif
 
+struct kernel_version {
+	int major;
+	int minor;
+	int sublevel;
+};
+
 struct linux_device_priv {
 	char *sysfs_dir;
 	unsigned char *descriptors;
@@ -180,6 +199,16 @@ struct linux_transfer_priv {
 	int iso_packet_offset;
 };
 
+static int _open(const char *path, int flags)
+{
+#if defined(O_CLOEXEC)
+	if (supports_flag_cloexec)
+		return open(path, flags | O_CLOEXEC);
+	else
+#endif
+		return open(path, flags);
+}
+
 static int _get_usbfs_fd(struct libusb_device *dev, mode_t mode, int silent)
 {
 	struct libusb_context *ctx = DEVICE_CTX(dev);
@@ -194,7 +223,7 @@ static int _get_usbfs_fd(struct libusb_device *dev, mode_t mode, int silent)
 		snprintf(path, PATH_MAX, "%s/%03d/%03d",
 			usbfs_path, dev->bus_number, dev->device_address);
 
-	fd = open(path, mode);
+	fd = _open(path, mode);
 	if (fd != -1)
 		return fd; /* Success */
 
@@ -205,7 +234,7 @@ static int _get_usbfs_fd(struct libusb_device *dev, mode_t mode, int silent)
 		/* Wait 10ms for USB device path creation.*/
 		nanosleep(&(struct timespec){delay / 1000000, (delay * 1000) % 1000000000UL}, NULL);
 
-		fd = open(path, mode);
+		fd = _open(path, mode);
 		if (fd != -1)
 			return fd; /* Success */
 	}
@@ -342,39 +371,59 @@ static clockid_t find_monotonic_clock(void)
 	return CLOCK_REALTIME;
 }
 
-static int kernel_version_ge(int major, int minor, int sublevel)
+static int get_kernel_version(struct libusb_context *ctx,
+	struct kernel_version *ver)
 {
 	struct utsname uts;
-	int atoms, kmajor, kminor, ksublevel;
+	int atoms;
 
-	if (uname(&uts) < 0)
+	if (uname(&uts) < 0) {
+		usbi_err(ctx, "uname failed, errno %d", errno);
 		return -1;
-	atoms = sscanf(uts.release, "%d.%d.%d", &kmajor, &kminor, &ksublevel);
-	if (atoms < 1)
+	}
+
+	atoms = sscanf(uts.release, "%d.%d.%d", &ver->major, &ver->minor, &ver->sublevel);
+	if (atoms < 1) {
+		usbi_err(ctx, "failed to parse uname release '%s'", uts.release);
 		return -1;
+	}
+
+	if (atoms < 2)
+		ver->minor = -1;
+	if (atoms < 3)
+		ver->sublevel = -1;
 
-	if (kmajor > major)
+	usbi_dbg("reported kernel version is %s", uts.release);
+
+	return 0;
+}
+
+static int kernel_version_ge(const struct kernel_version *ver,
+	int major, int minor, int sublevel)
+{
+	if (ver->major > major)
 		return 1;
-	if (kmajor < major)
+	else if (ver->major < major)
 		return 0;
 
 	/* kmajor == major */
-	if (atoms < 2)
+	if (ver->minor == -1 && ver->sublevel == -1)
 		return 0 == minor && 0 == sublevel;
-	if (kminor > minor)
+	else if (ver->minor > minor)
 		return 1;
-	if (kminor < minor)
+	else if (ver->minor < minor)
 		return 0;
 
 	/* kminor == minor */
-	if (atoms < 3)
+	if (ver->sublevel == -1)
 		return 0 == sublevel;
 
-	return ksublevel >= sublevel;
+	return ver->sublevel >= sublevel;
 }
 
 static int op_init(struct libusb_context *ctx)
 {
+	struct kernel_version kversion;
 	struct stat statbuf;
 	int r;
 
@@ -387,13 +436,17 @@ static int op_init(struct libusb_context *ctx)
 	if (monotonic_clkid == -1)
 		monotonic_clkid = find_monotonic_clock();
 
+	if (get_kernel_version(ctx, &kversion) < 0)
+		return LIBUSB_ERROR_OTHER;
+
+	if (supports_flag_cloexec == -1) {
+		/* O_CLOEXEC flag available from Linux 2.6.23 */
+		supports_flag_cloexec = kernel_version_ge(&kversion,2,6,23);
+	}
+
 	if (supports_flag_bulk_continuation == -1) {
 		/* bulk continuation URB flag available from Linux 2.6.32 */
-		supports_flag_bulk_continuation = kernel_version_ge(2,6,32);
-		if (supports_flag_bulk_continuation == -1) {
-			usbi_err(ctx, "error checking for bulk continuation support");
-			return LIBUSB_ERROR_OTHER;
-		}
+		supports_flag_bulk_continuation = kernel_version_ge(&kversion,2,6,32);
 	}
 
 	if (supports_flag_bulk_continuation)
@@ -401,32 +454,31 @@ static int op_init(struct libusb_context *ctx)
 
 	if (-1 == supports_flag_zero_packet) {
 		/* zero length packet URB flag fixed since Linux 2.6.31 */
-		supports_flag_zero_packet = kernel_version_ge(2,6,31);
-		if (-1 == supports_flag_zero_packet) {
-			usbi_err(ctx, "error checking for zero length packet support");
-			return LIBUSB_ERROR_OTHER;
-		}
+		supports_flag_zero_packet = kernel_version_ge(&kversion,2,6,31);
 	}
 
 	if (supports_flag_zero_packet)
 		usbi_dbg("zero length packet flag supported");
 
+	if (!max_iso_packet_len) {
+		if (kernel_version_ge(&kversion,3,10,0))
+			max_iso_packet_len = 49152;
+		else if (kernel_version_ge(&kversion,2,6,18))
+			max_iso_packet_len = 8192;
+		else
+			max_iso_packet_len = 1023;
+	}
+
+	usbi_dbg("max iso packet length is (likely) %u bytes", max_iso_packet_len);
+
 	if (-1 == sysfs_has_descriptors) {
 		/* sysfs descriptors has all descriptors since Linux 2.6.26 */
-		sysfs_has_descriptors = kernel_version_ge(2,6,26);
-		if (-1 == sysfs_has_descriptors) {
-			usbi_err(ctx, "error checking for sysfs descriptors");
-			return LIBUSB_ERROR_OTHER;
-		}
+		sysfs_has_descriptors = kernel_version_ge(&kversion,2,6,26);
 	}
 
 	if (-1 == sysfs_can_relate_devices) {
 		/* sysfs has busnum since Linux 2.6.22 */
-		sysfs_can_relate_devices = kernel_version_ge(2,6,22);
-		if (-1 == sysfs_can_relate_devices) {
-			usbi_err(ctx, "error checking for sysfs busnum");
-			return LIBUSB_ERROR_OTHER;
-		}
+		sysfs_can_relate_devices = kernel_version_ge(&kversion,2,6,22);
 	}
 
 	if (sysfs_can_relate_devices || sysfs_has_descriptors) {
@@ -463,8 +515,9 @@ static int op_init(struct libusb_context *ctx)
 	return r;
 }
 
-static void op_exit(void)
+static void op_exit(struct libusb_context *ctx)
 {
+	UNUSED(ctx);
 	usbi_mutex_static_lock(&linux_hotplug_startstop_lock);
 	assert(init_count != 0);
 	if (!--init_count) {
@@ -526,7 +579,7 @@ static int _open_sysfs_attr(struct libusb_device *dev, const char *attr)
 
 	snprintf(filename, PATH_MAX, "%s/%s/%s",
 		SYSFS_DEVICE_PATH, priv->sysfs_dir, attr);
-	fd = open(filename, O_RDONLY);
+	fd = _open(filename, O_RDONLY);
 	if (fd < 0) {
 		usbi_err(DEVICE_CTX(dev),
 			"open %s failed ret=%d errno=%d", filename, fd, errno);
@@ -542,12 +595,12 @@ static int __read_sysfs_attr(struct libusb_context *ctx,
 {
 	char filename[PATH_MAX];
 	FILE *f;
-	int r, value;
+	int fd, r, value;
 
 	snprintf(filename, PATH_MAX, "%s/%s/%s", SYSFS_DEVICE_PATH,
 		 devname, attr);
-	f = fopen(filename, "r");
-	if (f == NULL) {
+	fd = _open(filename, O_RDONLY);
+	if (fd == -1) {
 		if (errno == ENOENT) {
 			/* File doesn't exist. Assume the device has been
 			   disconnected (see trac ticket #70). */
@@ -557,6 +610,13 @@ static int __read_sysfs_attr(struct libusb_context *ctx,
 		return LIBUSB_ERROR_IO;
 	}
 
+	f = fdopen(fd, "r");
+	if (f == NULL) {
+		usbi_err(ctx, "fdopen %s failed errno=%d", filename, errno);
+		close(fd);
+		return LIBUSB_ERROR_OTHER;
+	}
+
 	r = fscanf(f, "%d", &value);
 	fclose(f);
 	if (r != 1) {
@@ -806,7 +866,7 @@ static int op_get_active_config_descriptor(struct libusb_device *dev,
 	if (r < 0)
 		return r;
 
-	len = MIN(len, r);
+	len = MIN(len, (size_t)r);
 	memcpy(buffer, config_desc, len);
 	return len;
 }
@@ -836,7 +896,7 @@ static int op_get_config_descriptor(struct libusb_device *dev,
 		descriptors += r;
 	}
 
-	len = MIN(len, r);
+	len = MIN(len, (size_t)r);
 	memcpy(buffer, descriptors, len);
 	return len;
 }
@@ -911,6 +971,7 @@ static int initialize_device(struct libusb_device *dev, uint8_t busnum,
 			case    12: dev->speed = LIBUSB_SPEED_FULL; break;
 			case   480: dev->speed = LIBUSB_SPEED_HIGH; break;
 			case  5000: dev->speed = LIBUSB_SPEED_SUPER; break;
+			case 10000: dev->speed = LIBUSB_SPEED_SUPER_PLUS; break;
 			default:
 				usbi_warn(DEVICE_CTX(dev), "Unknown device speed: %d Mbps", speed);
 			}
@@ -1239,11 +1300,12 @@ static int sysfs_get_device_list(struct libusb_context *ctx)
 {
 	DIR *devices = opendir(SYSFS_DEVICE_PATH);
 	struct dirent *entry;
-	int r = LIBUSB_ERROR_IO;
+	int num_devices = 0;
+	int num_enumerated = 0;
 
 	if (!devices) {
 		usbi_err(ctx, "opendir devices failed errno=%d", errno);
-		return r;
+		return LIBUSB_ERROR_IO;
 	}
 
 	while ((entry = readdir(devices))) {
@@ -1251,16 +1313,23 @@ static int sysfs_get_device_list(struct libusb_context *ctx)
 				|| strchr(entry->d_name, ':'))
 			continue;
 
+		num_devices++;
+
 		if (sysfs_scan_device(ctx, entry->d_name)) {
 			usbi_dbg("failed to enumerate dir entry %s", entry->d_name);
 			continue;
 		}
 
-		r = 0;
+		num_enumerated++;
 	}
 
 	closedir(devices);
-	return r;
+
+	/* successful if at least one device was enumerated or no devices were found */
+	if (num_enumerated || !num_devices)
+		return LIBUSB_SUCCESS;
+	else
+		return LIBUSB_ERROR_IO;
 }
 
 static int linux_default_scan_devices (struct libusb_context *ctx)
@@ -1685,10 +1754,7 @@ static int detach_kernel_driver_and_claim(struct libusb_device_handle *handle,
 	strcpy(dc.driver, "usbfs");
 	dc.flags = USBFS_DISCONNECT_CLAIM_EXCEPT_DRIVER;
 	r = ioctl(fd, IOCTL_USBFS_DISCONNECT_CLAIM, &dc);
-	if (r == 0 || (r != 0 && errno != ENOTTY)) {
-		if (r == 0)
-			return 0;
-
+	if (r != 0 && errno != ENOTTY) {
 		switch (errno) {
 		case EBUSY:
 			return LIBUSB_ERROR_BUSY;
@@ -1700,7 +1766,8 @@ static int detach_kernel_driver_and_claim(struct libusb_device_handle *handle,
 		usbi_err(HANDLE_CTX(handle),
 			"disconnect-and-claim failed errno %d", errno);
 		return LIBUSB_ERROR_OTHER;
-	}
+	} else if (r == 0)
+		return 0;
 
 	/* Fallback code for kernels which don't support the
 	   disconnect-and-claim ioctl */
@@ -1973,38 +2040,43 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer)
 	struct linux_device_handle_priv *dpriv =
 		_device_handle_priv(transfer->dev_handle);
 	struct usbfs_urb **urbs;
-	size_t alloc_size;
 	int num_packets = transfer->num_iso_packets;
-	int i;
-	int this_urb_len = 0;
-	int num_urbs = 1;
-	int packet_offset = 0;
+	int num_packets_remaining;
+	int i, j;
+	int num_urbs;
 	unsigned int packet_len;
+	unsigned int total_len = 0;
 	unsigned char *urb_buffer = transfer->buffer;
 
+	if (num_packets < 1)
+		return LIBUSB_ERROR_INVALID_PARAM;
+
 	/* usbfs places arbitrary limits on iso URBs. this limit has changed
-	 * at least three times, and it's difficult to accurately detect which
-	 * limit this running kernel might impose. so we attempt to submit
-	 * whatever the user has provided. if the kernel rejects the request
-	 * due to its size, we return an error indicating such to the user.
+	 * at least three times, but we attempt to detect this limit during
+	 * init and check it here. if the kernel rejects the request due to
+	 * its size, we return an error indicating such to the user.
 	 */
-
-	/* calculate how many URBs we need */
 	for (i = 0; i < num_packets; i++) {
-		unsigned int space_remaining = MAX_ISO_BUFFER_LENGTH - this_urb_len;
 		packet_len = transfer->iso_packet_desc[i].length;
 
-		if (packet_len > space_remaining) {
-			num_urbs++;
-			this_urb_len = packet_len;
-			/* check that we can actually support this packet length */
-			if (this_urb_len > MAX_ISO_BUFFER_LENGTH)
-				return LIBUSB_ERROR_INVALID_PARAM;
-		} else {
-			this_urb_len += packet_len;
+		if (packet_len > max_iso_packet_len) {
+			usbi_warn(TRANSFER_CTX(transfer),
+				"iso packet length of %u bytes exceeds maximum of %u bytes",
+				packet_len, max_iso_packet_len);
+			return LIBUSB_ERROR_INVALID_PARAM;
 		}
+
+		total_len += packet_len;
 	}
-	usbi_dbg("need %d %dk URBs for transfer", num_urbs, MAX_ISO_BUFFER_LENGTH / 1024);
+
+	if (transfer->length < (int)total_len)
+		return LIBUSB_ERROR_INVALID_PARAM;
+
+	/* usbfs limits the number of iso packets per URB */
+	num_urbs = (num_packets + (MAX_ISO_PACKETS_PER_URB - 1)) / MAX_ISO_PACKETS_PER_URB;
+
+	usbi_dbg("need %d urbs for new transfer with length %d", num_urbs,
+		transfer->length);
 
 	urbs = calloc(num_urbs, sizeof(*urbs));
 	if (!urbs)
@@ -2017,31 +2089,15 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer)
 	tpriv->iso_packet_offset = 0;
 
 	/* allocate + initialize each URB with the correct number of packets */
-	for (i = 0; i < num_urbs; i++) {
+	num_packets_remaining = num_packets;
+	for (i = 0, j = 0; i < num_urbs; i++) {
+		int num_packets_in_urb = MIN(num_packets_remaining, MAX_ISO_PACKETS_PER_URB);
 		struct usbfs_urb *urb;
-		unsigned int space_remaining_in_urb = MAX_ISO_BUFFER_LENGTH;
-		int urb_packet_offset = 0;
-		unsigned char *urb_buffer_orig = urb_buffer;
-		int j;
+		size_t alloc_size;
 		int k;
 
-		/* swallow up all the packets we can fit into this URB */
-		while (packet_offset < transfer->num_iso_packets) {
-			packet_len = transfer->iso_packet_desc[packet_offset].length;
-			if (packet_len <= space_remaining_in_urb) {
-				/* throw it in */
-				urb_packet_offset++;
-				packet_offset++;
-				space_remaining_in_urb -= packet_len;
-				urb_buffer += packet_len;
-			} else {
-				/* it can't fit, save it for the next URB */
-				break;
-			}
-		}
-
 		alloc_size = sizeof(*urb)
-			+ (urb_packet_offset * sizeof(struct usbfs_iso_packet_desc));
+			+ (num_packets_in_urb * sizeof(struct usbfs_iso_packet_desc));
 		urb = calloc(1, alloc_size);
 		if (!urb) {
 			free_iso_urbs(tpriv);
@@ -2050,10 +2106,10 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer)
 		urbs[i] = urb;
 
 		/* populate packet lengths */
-		for (j = 0, k = packet_offset - urb_packet_offset;
-				k < packet_offset; k++, j++) {
-			packet_len = transfer->iso_packet_desc[k].length;
-			urb->iso_frame_desc[j].length = packet_len;
+		for (k = 0; k < num_packets_in_urb; j++, k++) {
+			packet_len = transfer->iso_packet_desc[j].length;
+			urb->buffer_length += packet_len;
+			urb->iso_frame_desc[k].length = packet_len;
 		}
 
 		urb->usercontext = itransfer;
@@ -2061,8 +2117,11 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer)
 		/* FIXME: interface for non-ASAP data? */
 		urb->flags = USBFS_URB_ISO_ASAP;
 		urb->endpoint = transfer->endpoint;
-		urb->number_of_packets = urb_packet_offset;
-		urb->buffer = urb_buffer_orig;
+		urb->number_of_packets = num_packets_in_urb;
+		urb->buffer = urb_buffer;
+
+		urb_buffer += urb->buffer_length;
+		num_packets_remaining -= num_packets_in_urb;
 	}
 
 	/* submit URBs */
@@ -2075,6 +2134,10 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer)
 				usbi_warn(TRANSFER_CTX(transfer),
 					"submiturb failed, transfer too large");
 				r = LIBUSB_ERROR_INVALID_PARAM;
+			} else if (errno == EMSGSIZE) {
+				usbi_warn(TRANSFER_CTX(transfer),
+					"submiturb failed, iso packet length too large");
+				r = LIBUSB_ERROR_INVALID_PARAM;
 			} else {
 				usbi_err(TRANSFER_CTX(transfer),
 					"submiturb failed error %d errno=%d", r, errno);
@@ -2213,7 +2276,6 @@ static void op_clear_transfer_priv(struct usbi_transfer *itransfer)
 		USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
 	struct linux_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer);
 
-	/* urbs can be freed also in submit_transfer so lock mutex first */
 	switch (transfer->type) {
 	case LIBUSB_TRANSFER_TYPE_CONTROL:
 	case LIBUSB_TRANSFER_TYPE_BULK:
@@ -2685,7 +2747,7 @@ static clockid_t op_get_timerfd_clockid(void)
 }
 #endif
 
-const struct usbi_os_backend linux_usbfs_backend = {
+const struct usbi_os_backend usbi_backend = {
 	.name = "Linux usbfs",
 	.caps = USBI_CAP_HAS_HID_ACCESS|USBI_CAP_SUPPORTS_DETACH_KERNEL_DRIVER,
 	.init = op_init,

+ 2 - 1
vendor/github.com/karalabe/hid/libusb/libusb/os/linux_usbfs.h → vendor/github.com/karalabe/usb/libusb/libusb/os/linux_usbfs.h

@@ -81,10 +81,11 @@ struct usbfs_iso_packet_desc {
 	unsigned int status;
 };
 
-#define MAX_ISO_BUFFER_LENGTH		49152 * 128
 #define MAX_BULK_BUFFER_LENGTH		16384
 #define MAX_CTRL_BUFFER_LENGTH		4096
 
+#define MAX_ISO_PACKETS_PER_URB		128
+
 struct usbfs_urb {
 	unsigned char type;
 	unsigned char endpoint;

+ 3 - 3
vendor/github.com/karalabe/hid/libusb/libusb/os/netbsd_usb.c → vendor/github.com/karalabe/usb/libusb/libusb/os/netbsd_usb.c

@@ -86,11 +86,12 @@ static int _sync_control_transfer(struct usbi_transfer *);
 static int _sync_gen_transfer(struct usbi_transfer *);
 static int _access_endpoint(struct libusb_transfer *);
 
-const struct usbi_os_backend netbsd_backend = {
+const struct usbi_os_backend usbi_backend = {
 	"Synchronous NetBSD backend",
 	0,
 	NULL,				/* init() */
 	NULL,				/* exit() */
+	NULL,				/* set_option() */
 	netbsd_get_device_list,
 	NULL,				/* hotplug_poll */
 	netbsd_open,
@@ -131,6 +132,7 @@ const struct usbi_os_backend netbsd_backend = {
 	netbsd_handle_transfer_completion,
 
 	netbsd_clock_gettime,
+	0,				/* context_priv_size */
 	sizeof(struct device_priv),
 	sizeof(struct handle_priv),
 	0,				/* transfer_priv_size */
@@ -212,7 +214,6 @@ error:
 int
 netbsd_open(struct libusb_device_handle *handle)
 {
-	struct handle_priv *hpriv = (struct handle_priv *)handle->os_priv;
 	struct device_priv *dpriv = (struct device_priv *)handle->dev->os_priv;
 
 	dpriv->fd = open(dpriv->devnode, O_RDWR);
@@ -230,7 +231,6 @@ netbsd_open(struct libusb_device_handle *handle)
 void
 netbsd_close(struct libusb_device_handle *handle)
 {
-	struct handle_priv *hpriv = (struct handle_priv *)handle->os_priv;
 	struct device_priv *dpriv = (struct device_priv *)handle->dev->os_priv;
 
 	usbi_dbg("close: fd %d", dpriv->fd);

+ 3 - 3
vendor/github.com/karalabe/hid/libusb/libusb/os/openbsd_usb.c → vendor/github.com/karalabe/usb/libusb/libusb/os/openbsd_usb.c

@@ -89,11 +89,12 @@ static int _access_endpoint(struct libusb_transfer *);
 static int _bus_open(int);
 
 
-const struct usbi_os_backend openbsd_backend = {
+const struct usbi_os_backend usbi_backend = {
 	"Synchronous OpenBSD backend",
 	0,
 	NULL,				/* init() */
 	NULL,				/* exit() */
+	NULL,				/* set_option() */
 	obsd_get_device_list,
 	NULL,				/* hotplug_poll */
 	obsd_open,
@@ -134,6 +135,7 @@ const struct usbi_os_backend openbsd_backend = {
 	obsd_handle_transfer_completion,
 
 	obsd_clock_gettime,
+	0,				/* context_priv_size */
 	sizeof(struct device_priv),
 	sizeof(struct handle_priv),
 	0,				/* transfer_priv_size */
@@ -246,7 +248,6 @@ obsd_get_device_list(struct libusb_context * ctx,
 int
 obsd_open(struct libusb_device_handle *handle)
 {
-	struct handle_priv *hpriv = (struct handle_priv *)handle->os_priv;
 	struct device_priv *dpriv = (struct device_priv *)handle->dev->os_priv;
 	char devnode[16];
 
@@ -270,7 +271,6 @@ obsd_open(struct libusb_device_handle *handle)
 void
 obsd_close(struct libusb_device_handle *handle)
 {
-	struct handle_priv *hpriv = (struct handle_priv *)handle->os_priv;
 	struct device_priv *dpriv = (struct device_priv *)handle->dev->os_priv;
 
 	if (dpriv->devname) {

+ 36 - 5
vendor/github.com/karalabe/hid/libusb/libusb/os/poll_posix.c → vendor/github.com/karalabe/usb/libusb/libusb/os/poll_posix.c

@@ -29,25 +29,56 @@
 
 int usbi_pipe(int pipefd[2])
 {
+#if defined(HAVE_PIPE2)
+	int ret = pipe2(pipefd, O_CLOEXEC);
+#else
 	int ret = pipe(pipefd);
+#endif
+
 	if (ret != 0) {
+		usbi_err(NULL, "failed to create pipe (%d)", errno);
 		return ret;
 	}
+
+#if !defined(HAVE_PIPE2) && defined(FD_CLOEXEC)
+	ret = fcntl(pipefd[0], F_GETFD);
+	if (ret == -1) {
+		usbi_err(NULL, "failed to get pipe fd flags (%d)", errno);
+		goto err_close_pipe;
+	}
+	ret = fcntl(pipefd[0], F_SETFD, ret | FD_CLOEXEC);
+	if (ret == -1) {
+		usbi_err(NULL, "failed to set pipe fd flags (%d)", errno);
+		goto err_close_pipe;
+	}
+
+	ret = fcntl(pipefd[1], F_GETFD);
+	if (ret == -1) {
+		usbi_err(NULL, "failed to get pipe fd flags (%d)", errno);
+		goto err_close_pipe;
+	}
+	ret = fcntl(pipefd[1], F_SETFD, ret | FD_CLOEXEC);
+	if (ret == -1) {
+		usbi_err(NULL, "failed to set pipe fd flags (%d)", errno);
+		goto err_close_pipe;
+	}
+#endif
+
 	ret = fcntl(pipefd[1], F_GETFL);
 	if (ret == -1) {
-		usbi_dbg("Failed to get pipe fd flags: %d", errno);
+		usbi_err(NULL, "failed to get pipe fd status flags (%d)", errno);
 		goto err_close_pipe;
 	}
 	ret = fcntl(pipefd[1], F_SETFL, ret | O_NONBLOCK);
-	if (ret != 0) {
-		usbi_dbg("Failed to set non-blocking on new pipe: %d", errno);
+	if (ret == -1) {
+		usbi_err(NULL, "failed to set pipe fd status flags (%d)", errno);
 		goto err_close_pipe;
 	}
 
 	return 0;
 
 err_close_pipe:
-	usbi_close(pipefd[0]);
-	usbi_close(pipefd[1]);
+	close(pipefd[0]);
+	close(pipefd[1]);
 	return ret;
 }

+ 0 - 0
vendor/github.com/karalabe/hid/libusb/libusb/os/poll_posix.h → vendor/github.com/karalabe/usb/libusb/libusb/os/poll_posix.h


+ 364 - 0
vendor/github.com/karalabe/usb/libusb/libusb/os/poll_windows.c

@@ -0,0 +1,364 @@
+/*
+ * poll_windows: poll compatibility wrapper for Windows
+ * Copyright © 2017 Chris Dickens <christopher.a.dickens@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * 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.
+ * - 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 <config.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#include "libusbi.h"
+#include "windows_common.h"
+
+// public fd data
+const struct winfd INVALID_WINFD = { -1, NULL };
+
+// private data
+struct file_descriptor {
+	enum fd_type { FD_TYPE_PIPE, FD_TYPE_TRANSFER } type;
+	OVERLAPPED overlapped;
+};
+
+static usbi_mutex_static_t fd_table_lock = USBI_MUTEX_INITIALIZER;
+static struct file_descriptor *fd_table[MAX_FDS];
+
+static struct file_descriptor *create_fd(enum fd_type type)
+{
+	struct file_descriptor *fd = calloc(1, sizeof(*fd));
+	if (fd == NULL)
+		return NULL;
+	fd->overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+	if (fd->overlapped.hEvent == NULL) {
+		free(fd);
+		return NULL;
+	}
+	fd->type = type;
+	return fd;
+}
+
+static void free_fd(struct file_descriptor *fd)
+{
+	CloseHandle(fd->overlapped.hEvent);
+	free(fd);
+}
+
+/*
+ * Create both an fd and an OVERLAPPED, so that it can be used with our
+ * polling function
+ * The handle MUST support overlapped transfers (usually requires CreateFile
+ * with FILE_FLAG_OVERLAPPED)
+ * Return a pollable file descriptor struct, or INVALID_WINFD on error
+ *
+ * Note that the fd returned by this function is a per-transfer fd, rather
+ * than a per-session fd and cannot be used for anything else but our
+ * custom functions.
+ * 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(void)
+{
+	struct file_descriptor *fd;
+	struct winfd wfd;
+
+	fd = create_fd(FD_TYPE_TRANSFER);
+	if (fd == NULL)
+		return INVALID_WINFD;
+
+	usbi_mutex_static_lock(&fd_table_lock);
+	for (wfd.fd = 0; wfd.fd < MAX_FDS; wfd.fd++) {
+		if (fd_table[wfd.fd] != NULL)
+			continue;
+		fd_table[wfd.fd] = fd;
+		break;
+	}
+	usbi_mutex_static_unlock(&fd_table_lock);
+
+	if (wfd.fd == MAX_FDS) {
+		free_fd(fd);
+		return INVALID_WINFD;
+	}
+
+	wfd.overlapped = &fd->overlapped;
+
+	return wfd;
+}
+
+static int check_pollfds(struct pollfd *fds, unsigned int nfds,
+	HANDLE *wait_handles, DWORD *nb_wait_handles)
+{
+	struct file_descriptor *fd;
+	unsigned int n;
+	int nready = 0;
+
+	usbi_mutex_static_lock(&fd_table_lock);
+
+	for (n = 0; n < nfds; ++n) {
+		fds[n].revents = 0;
+
+		// Keep it simple - only allow either POLLIN *or* POLLOUT
+		assert((fds[n].events == POLLIN) || (fds[n].events == POLLOUT));
+		if ((fds[n].events != POLLIN) && (fds[n].events != POLLOUT)) {
+			fds[n].revents = POLLNVAL;
+			nready++;
+			continue;
+		}
+
+		if ((fds[n].fd >= 0) && (fds[n].fd < MAX_FDS))
+			fd = fd_table[fds[n].fd];
+		else
+			fd = NULL;
+
+		assert(fd != NULL);
+		if (fd == NULL) {
+			fds[n].revents = POLLNVAL;
+			nready++;
+			continue;
+		}
+
+		if (HasOverlappedIoCompleted(&fd->overlapped)
+				&& (WaitForSingleObject(fd->overlapped.hEvent, 0) == WAIT_OBJECT_0)) {
+			fds[n].revents = fds[n].events;
+			nready++;
+		} else if (wait_handles != NULL) {
+			if (*nb_wait_handles == MAXIMUM_WAIT_OBJECTS) {
+				usbi_warn(NULL, "too many HANDLEs to wait on");
+				continue;
+			}
+			wait_handles[*nb_wait_handles] = fd->overlapped.hEvent;
+			(*nb_wait_handles)++;
+		}
+	}
+
+	usbi_mutex_static_unlock(&fd_table_lock);
+
+	return nready;
+}
+/*
+ * 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)
+{
+	HANDLE wait_handles[MAXIMUM_WAIT_OBJECTS];
+	DWORD nb_wait_handles = 0;
+	DWORD ret;
+	int nready;
+
+	nready = check_pollfds(fds, nfds, wait_handles, &nb_wait_handles);
+
+	// If nothing was triggered, wait on all fds that require it
+	if ((nready == 0) && (nb_wait_handles != 0) && (timeout != 0)) {
+		ret = WaitForMultipleObjects(nb_wait_handles, wait_handles,
+			FALSE, (timeout < 0) ? INFINITE : (DWORD)timeout);
+		if (ret < (WAIT_OBJECT_0 + nb_wait_handles)) {
+			nready = check_pollfds(fds, nfds, NULL, NULL);
+		} else if (ret != WAIT_TIMEOUT) {
+			if (ret == WAIT_FAILED)
+				usbi_err(NULL, "WaitForMultipleObjects failed: %u", (unsigned int)GetLastError());
+			nready = -1;
+		}
+	}
+
+	return nready;
+}
+
+/*
+ * close a fake file descriptor
+ */
+int usbi_close(int _fd)
+{
+	struct file_descriptor *fd;
+
+	if (_fd < 0 || _fd >= MAX_FDS)
+		goto err_badfd;
+
+	usbi_mutex_static_lock(&fd_table_lock);
+	fd = fd_table[_fd];
+	fd_table[_fd] = NULL;
+	usbi_mutex_static_unlock(&fd_table_lock);
+
+	if (fd == NULL)
+		goto err_badfd;
+
+	if (fd->type == FD_TYPE_PIPE) {
+		// InternalHigh is our reference count
+		fd->overlapped.InternalHigh--;
+		if (fd->overlapped.InternalHigh == 0)
+			free_fd(fd);
+	} else {
+		free_fd(fd);
+	}
+
+	return 0;
+
+err_badfd:
+	errno = EBADF;
+	return -1;
+}
+
+/*
+* Create a fake pipe.
+* As libusb only uses pipes for signaling, all we need from a pipe is an
+* event. To that extent, we create a single wfd and overlapped as a means
+* to access that event.
+*/
+int usbi_pipe(int filedes[2])
+{
+	struct file_descriptor *fd;
+	int r_fd = -1, w_fd = -1;
+	int i;
+
+	fd = create_fd(FD_TYPE_PIPE);
+	if (fd == NULL) {
+		errno = ENOMEM;
+		return -1;
+	}
+
+	// Use InternalHigh as a reference count
+	fd->overlapped.Internal = STATUS_PENDING;
+	fd->overlapped.InternalHigh = 2;
+
+	usbi_mutex_static_lock(&fd_table_lock);
+	do {
+		for (i = 0; i < MAX_FDS; i++) {
+			if (fd_table[i] != NULL)
+				continue;
+			if (r_fd == -1) {
+				r_fd = i;
+			} else if (w_fd == -1) {
+				w_fd = i;
+				break;
+			}
+		}
+
+		if (i == MAX_FDS)
+			break;
+
+		fd_table[r_fd] = fd;
+		fd_table[w_fd] = fd;
+
+	} while (0);
+	usbi_mutex_static_unlock(&fd_table_lock);
+
+	if (i == MAX_FDS) {
+		free_fd(fd);
+		errno = EMFILE;
+		return -1;
+	}
+
+	filedes[0] = r_fd;
+	filedes[1] = w_fd;
+
+	return 0;
+}
+
+/*
+ * synchronous write for fake "pipe" signaling
+ */
+ssize_t usbi_write(int fd, const void *buf, size_t count)
+{
+	int error = EBADF;
+
+	UNUSED(buf);
+
+	if (fd < 0 || fd >= MAX_FDS)
+		goto err_out;
+
+	if (count != sizeof(unsigned char)) {
+		usbi_err(NULL, "this function should only used for signaling");
+		error = EINVAL;
+		goto err_out;
+	}
+
+	usbi_mutex_static_lock(&fd_table_lock);
+	if ((fd_table[fd] != NULL) && (fd_table[fd]->type == FD_TYPE_PIPE)) {
+		assert(fd_table[fd]->overlapped.Internal == STATUS_PENDING);
+		assert(fd_table[fd]->overlapped.InternalHigh == 2);
+		fd_table[fd]->overlapped.Internal = STATUS_WAIT_0;
+		SetEvent(fd_table[fd]->overlapped.hEvent);
+		error = 0;
+	}
+	usbi_mutex_static_unlock(&fd_table_lock);
+
+	if (error)
+		goto err_out;
+
+	return sizeof(unsigned char);
+
+err_out:
+	errno = error;
+	return -1;
+}
+
+/*
+ * synchronous read for fake "pipe" signaling
+ */
+ssize_t usbi_read(int fd, void *buf, size_t count)
+{
+	int error = EBADF;
+
+	UNUSED(buf);
+
+	if (fd < 0 || fd >= MAX_FDS)
+		goto err_out;
+
+	if (count != sizeof(unsigned char)) {
+		usbi_err(NULL, "this function should only used for signaling");
+		error = EINVAL;
+		goto err_out;
+	}
+
+	usbi_mutex_static_lock(&fd_table_lock);
+	if ((fd_table[fd] != NULL) && (fd_table[fd]->type == FD_TYPE_PIPE)) {
+		assert(fd_table[fd]->overlapped.Internal == STATUS_WAIT_0);
+		assert(fd_table[fd]->overlapped.InternalHigh == 2);
+		fd_table[fd]->overlapped.Internal = STATUS_PENDING;
+		ResetEvent(fd_table[fd]->overlapped.hEvent);
+		error = 0;
+	}
+	usbi_mutex_static_unlock(&fd_table_lock);
+
+	if (error)
+		goto err_out;
+
+	return sizeof(unsigned char);
+
+err_out:
+	errno = error;
+	return -1;
+}

+ 9 - 43
vendor/github.com/karalabe/hid/libusb/libusb/os/poll_windows.h → vendor/github.com/karalabe/usb/libusb/libusb/os/poll_windows.h

@@ -2,6 +2,7 @@
  * Windows compat: POSIX compatibility wrapper
  * Copyright © 2012-2013 RealVNC Ltd.
  * Copyright © 2009-2010 Pete Batard <pete@akeo.ie>
+ * Copyright © 2016-2018 Chris Dickens <christopher.a.dickens@gmail.com>
  * With contributions from Michael Plante, Orin Eman et al.
  * Parts of poll implementation from libusb-win32, by Stephan Meyer et al.
  *
@@ -40,21 +41,6 @@
 
 #define DUMMY_HANDLE ((HANDLE)(LONG_PTR)-2)
 
-/* Windows versions */
-enum windows_version {
-	WINDOWS_CE = -2,
-	WINDOWS_UNDEFINED = -1,
-	WINDOWS_UNSUPPORTED = 0,
-	WINDOWS_XP = 0x51,
-	WINDOWS_2003 = 0x52,	// Also XP x64
-	WINDOWS_VISTA = 0x60,
-	WINDOWS_7 = 0x61,
-	WINDOWS_8 = 0x62,
-	WINDOWS_8_1_OR_LATER = 0x63,
-	WINDOWS_MAX
-};
-extern int windows_version;
-
 #define MAX_FDS     256
 
 #define POLLIN      0x0001    /* There is data to read */
@@ -65,46 +51,26 @@ extern int windows_version;
 #define POLLNVAL    0x0020    /* Invalid request: fd not open */
 
 struct pollfd {
-    int fd;           /* file descriptor */
-    short events;     /* requested events */
-    short revents;    /* returned events */
-};
-
-// access modes
-enum rw_type {
-	RW_NONE,
-	RW_READ,
-	RW_WRITE,
+	int fd;		/* file descriptor */
+	short events;	/* requested events */
+	short revents;	/* returned events */
 };
 
-// fd struct that can be used for polling on Windows
-typedef int cancel_transfer(struct usbi_transfer *itransfer);
-
 struct winfd {
-	int fd;							// what's exposed to libusb core
-	HANDLE handle;					// what we need to attach overlapped to the I/O op, so we can poll it
-	OVERLAPPED* overlapped;			// what will report our I/O status
-	struct usbi_transfer *itransfer;		// Associated transfer, or NULL if completed
-	cancel_transfer *cancel_fn;		// Function pointer to cancel transfer API
-	enum rw_type rw;				// I/O transfer direction: read *XOR* write (NOT BOTH)
+	int fd;				// what's exposed to libusb core
+	OVERLAPPED *overlapped;		// what will report our I/O status
 };
+
 extern const struct winfd INVALID_WINFD;
 
+struct winfd usbi_create_fd(void);
+
 int usbi_pipe(int pipefd[2]);
 int usbi_poll(struct pollfd *fds, unsigned int nfds, int timeout);
 ssize_t usbi_write(int fd, const void *buf, size_t count);
 ssize_t usbi_read(int fd, void *buf, size_t count);
 int usbi_close(int fd);
 
-void init_polling(void);
-void exit_polling(void);
-struct winfd usbi_create_fd(HANDLE handle, int access_mode, 
-	struct usbi_transfer *transfer, cancel_transfer *cancel_fn);
-void usbi_free_fd(struct winfd* winfd);
-struct winfd fd_to_winfd(int fd);
-struct winfd handle_to_winfd(HANDLE handle);
-struct winfd overlapped_to_winfd(OVERLAPPED* overlapped);
-
 /*
  * Timeval operations
  */

+ 475 - 92
vendor/github.com/karalabe/hid/libusb/libusb/os/sunos_usb.c → vendor/github.com/karalabe/usb/libusb/libusb/os/sunos_usb.c

@@ -21,6 +21,7 @@
 
 #include <sys/time.h>
 #include <sys/types.h>
+#include <sys/list.h>
 #include <sys/stat.h>
 #include <strings.h>
 #include <errno.h>
@@ -28,21 +29,34 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <wait.h>
 #include <unistd.h>
 #include <aio.h>
 #include <libdevinfo.h>
+#include <sys/nvpair.h>
+#include <sys/devctl.h>
 #include <sys/usb/clients/ugen/usb_ugen.h>
+#include <errno.h>
 #include <sys/usb/usba.h>
 #include <sys/pci.h>
 
 #include "libusbi.h"
 #include "sunos_usb.h"
 
+#define UPDATEDRV_PATH	"/usr/sbin/update_drv"
+#define UPDATEDRV	"update_drv"
+
+typedef list_t string_list_t;
+typedef struct string_node {
+	char		*string;
+	list_node_t	link;
+} string_node_t;
+
 /*
  * Backend functions
  */
 static int sunos_init(struct libusb_context *);
-static void sunos_exit(void);
+static void sunos_exit(struct libusb_context *);
 static int sunos_get_device_list(struct libusb_context *,
     struct discovered_devs **);
 static int sunos_open(struct libusb_device_handle *);
@@ -67,6 +81,162 @@ static int sunos_cancel_transfer(struct usbi_transfer *);
 static void sunos_clear_transfer_priv(struct usbi_transfer *);
 static int sunos_handle_transfer_completion(struct usbi_transfer *);
 static int sunos_clock_gettime(int, struct timespec *);
+static int sunos_kernel_driver_active(struct libusb_device_handle *, int interface);
+static int sunos_detach_kernel_driver (struct libusb_device_handle *dev, int interface_number);
+static int sunos_attach_kernel_driver (struct libusb_device_handle *dev, int interface_number);
+static int sunos_usb_open_ep0(sunos_dev_handle_priv_t *hpriv, sunos_dev_priv_t *dpriv);
+static int sunos_usb_ioctl(struct libusb_device *dev, int cmd);
+
+static struct devctl_iocdata iocdata;
+static int sunos_get_link(di_devlink_t devlink, void *arg)
+{
+	walk_link_t *larg = (walk_link_t *)arg;
+	const char *p;
+	const char *q;
+
+	if (larg->path) {
+		char *content = (char *)di_devlink_content(devlink);
+		char *start = strstr(content, "/devices/");
+		start += strlen("/devices");
+		usbi_dbg("%s", start);
+
+		/* line content must have minor node */
+		if (start == NULL ||
+		    strncmp(start, larg->path, larg->len) != 0 ||
+		    start[larg->len] != ':')
+			return (DI_WALK_CONTINUE);
+	}
+
+	p = di_devlink_path(devlink);
+	q = strrchr(p, '/');
+	usbi_dbg("%s", q);
+
+	*(larg->linkpp) = strndup(p, strlen(p) - strlen(q));
+
+	return (DI_WALK_TERMINATE);
+}
+
+
+static int sunos_physpath_to_devlink(
+	const char *node_path, const char *match, char **link_path)
+{
+	walk_link_t larg;
+	di_devlink_handle_t hdl;
+
+	*link_path = NULL;
+	larg.linkpp = link_path;
+	if ((hdl = di_devlink_init(NULL, 0)) == NULL) {
+		usbi_dbg("di_devlink_init failure");
+		return (-1);
+	}
+
+	larg.len = strlen(node_path);
+	larg.path = (char *)node_path;
+
+	(void) di_devlink_walk(hdl, match, NULL, DI_PRIMARY_LINK,
+	    (void *)&larg, sunos_get_link);
+
+	(void) di_devlink_fini(&hdl);
+
+	if (*link_path == NULL) {
+		usbi_dbg("there is no devlink for this path");
+		return (-1);
+	}
+
+	return 0;
+}
+
+static int
+sunos_usb_ioctl(struct libusb_device *dev, int cmd)
+{
+	int fd;
+	nvlist_t *nvlist;
+	char *end;
+	char *phypath;
+	char *hubpath;
+	char path_arg[PATH_MAX];
+	sunos_dev_priv_t *dpriv;
+	devctl_ap_state_t devctl_ap_state;
+
+	dpriv = (sunos_dev_priv_t *)dev->os_priv;
+	phypath = dpriv->phypath;
+
+	end = strrchr(phypath, '/');
+	if (end == NULL)
+		return (-1);
+	hubpath = strndup(phypath, end - phypath);
+	if (hubpath == NULL)
+		return (-1);
+
+	end = strrchr(hubpath, '@');
+	if (end == NULL) {
+		free(hubpath);
+		return (-1);
+	}
+	end++;
+	usbi_dbg("unitaddr: %s", end);
+
+	nvlist_alloc(&nvlist, NV_UNIQUE_NAME_TYPE, KM_NOSLEEP);
+	nvlist_add_int32(nvlist, "port", dev->port_number);
+	//find the hub path
+	snprintf(path_arg, sizeof(path_arg), "/devices%s:hubd", hubpath);
+	usbi_dbg("ioctl hub path: %s", path_arg);
+
+	fd = open(path_arg, O_RDONLY);
+	if (fd < 0) {
+		usbi_err(DEVICE_CTX(dev), "open failed: %d (%s)", errno, strerror(errno));
+		nvlist_free(nvlist);
+		free(hubpath);
+		return (-1);
+	}
+
+	memset(&iocdata, 0, sizeof(iocdata));
+	memset(&devctl_ap_state, 0, sizeof(devctl_ap_state));
+
+	nvlist_pack(nvlist, (char **)&iocdata.nvl_user, &iocdata.nvl_usersz, NV_ENCODE_NATIVE, 0);
+
+	iocdata.cmd = DEVCTL_AP_GETSTATE;
+	iocdata.flags = 0;
+	iocdata.c_nodename = "hub";
+	iocdata.c_unitaddr = end;
+	iocdata.cpyout_buf = &devctl_ap_state;
+	usbi_dbg("%p, %d", iocdata.nvl_user, iocdata.nvl_usersz);
+
+	errno = 0;
+	if (ioctl(fd, DEVCTL_AP_GETSTATE, &iocdata) == -1) {
+		usbi_err(DEVICE_CTX(dev), "ioctl failed: fd %d, cmd %x, errno %d (%s)",
+			 fd, DEVCTL_AP_GETSTATE, errno, strerror(errno));
+	} else {
+		usbi_dbg("dev rstate: %d", devctl_ap_state.ap_rstate);
+		usbi_dbg("dev ostate: %d", devctl_ap_state.ap_ostate);
+	}
+
+	errno = 0;
+	iocdata.cmd = cmd;
+	if (ioctl(fd, (int)cmd, &iocdata) != 0) {
+		usbi_err(DEVICE_CTX(dev), "ioctl failed: fd %d, cmd %x, errno %d (%s)",
+			 fd, cmd, errno, strerror(errno));
+		sleep(2);
+	}
+
+	close(fd);
+	free(iocdata.nvl_user);
+	nvlist_free(nvlist);
+	free(hubpath);
+
+	return (-errno);
+}
+
+static int
+sunos_kernel_driver_active(struct libusb_device_handle *dev, int interface)
+{
+	sunos_dev_priv_t *dpriv;
+	dpriv = (sunos_dev_priv_t *)dev->dev->os_priv;
+
+	usbi_dbg("%s", dpriv->ugenpath);
+
+	return (dpriv->ugenpath == NULL);
+}
 
 /*
  * Private functions
@@ -79,11 +249,229 @@ static int sunos_init(struct libusb_context *ctx)
 	return (LIBUSB_SUCCESS);
 }
 
-static void sunos_exit(void)
+static void sunos_exit(struct libusb_context *ctx)
 {
 	usbi_dbg("");
 }
 
+static string_list_t *
+sunos_new_string_list(void)
+{
+	string_list_t *list;
+
+	list = calloc(1, sizeof(*list));
+	if (list != NULL)
+		list_create(list, sizeof(string_node_t),
+			    offsetof(string_node_t, link));
+
+	return (list);
+}
+
+static int
+sunos_append_to_string_list(string_list_t *list, const char *arg)
+{
+	string_node_t *np;
+
+	np = calloc(1, sizeof(*np));
+	if (!np)
+		return (-1);
+
+	np->string = strdup(arg);
+	if (!np->string) {
+		free(np);
+		return (-1);
+	}
+
+	list_insert_tail(list, np);
+
+	return (0);
+}
+
+static void
+sunos_free_string_list(string_list_t *list)
+{
+	string_node_t *np;
+
+	while ((np = list_remove_head(list)) != NULL) {
+		free(np->string);
+		free(np);
+	}
+
+	free(list);
+}
+
+static char **
+sunos_build_argv_list(string_list_t *list)
+{
+	char **argv_list;
+	string_node_t *np;
+	int n;
+
+	n = 1; /* Start at 1 for NULL terminator */
+	for (np = list_head(list); np != NULL; np = list_next(list, np))
+		n++;
+
+	argv_list = calloc(n, sizeof(char *));
+	if (argv_list == NULL)
+		return NULL;
+
+	n = 0;
+	for (np = list_head(list); np != NULL; np = list_next(list, np))
+		argv_list[n++] = np->string;
+
+	return (argv_list);
+}
+
+
+static int
+sunos_exec_command(struct libusb_context *ctx, const char *path,
+	string_list_t *list)
+{
+	pid_t pid;
+	int status;
+	int waitstat;
+	int exit_status;
+	char **argv_list;
+
+	argv_list = sunos_build_argv_list(list);
+	if (argv_list == NULL)
+		return (-1);
+
+	pid = fork();
+	if (pid == 0) {
+		/* child */
+		execv(path, argv_list);
+		_exit(127);
+	} else if (pid > 0) {
+		/* parent */
+		do {
+			waitstat = waitpid(pid, &status, 0);
+		} while ((waitstat == -1 && errno == EINTR) ||
+			 (waitstat == 0 && !WIFEXITED(status) && !WIFSIGNALED(status)));
+
+		if (waitstat == 0) {
+			if (WIFEXITED(status))
+				exit_status = WEXITSTATUS(status);
+			else
+				exit_status = WTERMSIG(status);
+		} else {
+			usbi_err(ctx, "waitpid failed: errno %d (%s)", errno, strerror(errno));
+			exit_status = -1;
+		}
+	} else {
+		/* fork failed */
+		usbi_err(ctx, "fork failed: errno %d (%s)", errno, strerror(errno));
+		exit_status = -1;
+	}
+
+	free(argv_list);
+
+	return (exit_status);
+}
+
+static int
+sunos_detach_kernel_driver(struct libusb_device_handle *dev_handle,
+	int interface_number)
+{
+	struct libusb_context *ctx = HANDLE_CTX(dev_handle);
+	string_list_t *list;
+	char path_arg[PATH_MAX];
+	sunos_dev_priv_t *dpriv;
+	int r;
+
+	dpriv = (sunos_dev_priv_t *)dev_handle->dev->os_priv;
+	snprintf(path_arg, sizeof(path_arg), "\'\"%s\"\'", dpriv->phypath);
+	usbi_dbg("%s", path_arg);
+
+	list = sunos_new_string_list();
+	if (list == NULL)
+		return (LIBUSB_ERROR_NO_MEM);
+
+	/* attach ugen driver */
+	r = 0;
+	r |= sunos_append_to_string_list(list, UPDATEDRV);
+	r |= sunos_append_to_string_list(list, "-a"); /* add rule */
+	r |= sunos_append_to_string_list(list, "-i"); /* specific device */
+	r |= sunos_append_to_string_list(list, path_arg); /* physical path */
+	r |= sunos_append_to_string_list(list, "ugen");
+	if (r) {
+		sunos_free_string_list(list);
+		return (LIBUSB_ERROR_NO_MEM);
+	}
+
+	r = sunos_exec_command(ctx, UPDATEDRV_PATH, list);
+	sunos_free_string_list(list);
+	if (r < 0)
+		return (LIBUSB_ERROR_OTHER);
+
+	/* reconfigure the driver node */
+	r = 0;
+	r |= sunos_usb_ioctl(dev_handle->dev, DEVCTL_AP_DISCONNECT);
+	r |= sunos_usb_ioctl(dev_handle->dev, DEVCTL_AP_CONFIGURE);
+	if (r)
+		usbi_warn(HANDLE_CTX(dev_handle), "one or more ioctls failed");
+
+	snprintf(path_arg, sizeof(path_arg), "^usb/%x.%x", dpriv->dev_descr.idVendor,
+	    dpriv->dev_descr.idProduct);
+	sunos_physpath_to_devlink(dpriv->phypath, path_arg, &dpriv->ugenpath);
+
+	if (access(dpriv->ugenpath, F_OK) == -1) {
+		usbi_err(HANDLE_CTX(dev_handle), "fail to detach kernel driver");
+		return (LIBUSB_ERROR_IO);
+	}
+
+	return sunos_usb_open_ep0((sunos_dev_handle_priv_t *)dev_handle->os_priv, dpriv);
+}
+
+static int
+sunos_attach_kernel_driver(struct libusb_device_handle *dev_handle,
+	int interface_number)
+{
+	struct libusb_context *ctx = HANDLE_CTX(dev_handle);
+	string_list_t *list;
+	char path_arg[PATH_MAX];
+	sunos_dev_priv_t *dpriv;
+	int r;
+
+	/* we open the dev in detach driver, so we need close it first. */
+	sunos_close(dev_handle);
+
+	dpriv = (sunos_dev_priv_t *)dev_handle->dev->os_priv;
+	snprintf(path_arg, sizeof(path_arg), "\'\"%s\"\'", dpriv->phypath);
+	usbi_dbg("%s", path_arg);
+
+	list = sunos_new_string_list();
+	if (list == NULL)
+		return (LIBUSB_ERROR_NO_MEM);
+
+	/* detach ugen driver */
+	r = 0;
+	r |= sunos_append_to_string_list(list, UPDATEDRV);
+	r |= sunos_append_to_string_list(list, "-d"); /* add rule */
+	r |= sunos_append_to_string_list(list, "-i"); /* specific device */
+	r |= sunos_append_to_string_list(list, path_arg); /* physical path */
+	r |= sunos_append_to_string_list(list, "ugen");
+	if (r) {
+		sunos_free_string_list(list);
+		return (LIBUSB_ERROR_NO_MEM);
+	}
+
+	r = sunos_exec_command(ctx, UPDATEDRV_PATH, list);
+	sunos_free_string_list(list);
+	if (r < 0)
+		return (LIBUSB_ERROR_OTHER);
+
+	/* reconfigure the driver node */
+	r = 0;
+	r |= sunos_usb_ioctl(dev_handle->dev, DEVCTL_AP_CONFIGURE);
+	r |= sunos_usb_ioctl(dev_handle->dev, DEVCTL_AP_DISCONNECT);
+	r |= sunos_usb_ioctl(dev_handle->dev, DEVCTL_AP_CONFIGURE);
+	if (r)
+		usbi_warn(HANDLE_CTX(dev_handle), "one or more ioctls failed");
+
+	return 0;
+}
+
 static int
 sunos_fill_in_dev_info(di_node_t node, struct libusb_device *dev)
 {
@@ -93,6 +481,7 @@ sunos_fill_in_dev_info(di_node_t node, struct libusb_device *dev)
 	uint8_t	*rdata;
 	struct libusb_device_descriptor	*descr;
 	sunos_dev_priv_t	*dpriv = (sunos_dev_priv_t *)dev->os_priv;
+	char	match_str[PATH_MAX];
 
 	/* Device descriptors */
 	proplen = di_prop_lookup_bytes(DDI_DEV_T_ANY, node,
@@ -137,7 +526,11 @@ sunos_fill_in_dev_info(di_node_t node, struct libusb_device *dev)
 	phypath = di_devfs_path(node);
 	if (phypath) {
 		dpriv->phypath = strdup(phypath);
+		snprintf(match_str, sizeof(match_str), "^usb/%x.%x", dpriv->dev_descr.idVendor, dpriv->dev_descr.idProduct);
+		usbi_dbg("match is %s", match_str);
+		sunos_physpath_to_devlink(dpriv->phypath, match_str,  &dpriv->ugenpath);
 		di_devfs_path_free(phypath);
+
 	} else {
 		free(dpriv->raw_cfgdescr);
 
@@ -170,111 +563,98 @@ sunos_fill_in_dev_info(di_node_t node, struct libusb_device *dev)
 	return (LIBUSB_SUCCESS);
 }
 
-
 static int
 sunos_add_devices(di_devlink_t link, void *arg)
 {
 	struct devlink_cbarg	*largs = (struct devlink_cbarg *)arg;
 	struct node_args	*nargs;
-	di_node_t		myself, pnode;
+	di_node_t		myself, dn;
 	uint64_t		session_id = 0;
-	uint16_t		bdf = 0;
+	uint64_t		sid = 0;
+	uint64_t		bdf = 0;
 	struct libusb_device	*dev;
 	sunos_dev_priv_t	*devpriv;
-	const char		*path, *newpath;
-	int			 n, i;
+	int			n;
+	int			i = 0;
 	int			*addr_prop;
 	uint8_t			bus_number = 0;
+	uint32_t * 		regbuf = NULL;
+	uint32_t		reg;
 
 	nargs = (struct node_args *)largs->nargs;
 	myself = largs->myself;
-	if (nargs->last_ugenpath) {
-		/* the same node's links */
-		return (DI_WALK_CONTINUE);
-	}
 
 	/*
 	 * Construct session ID.
-	 * session ID = ...parent hub addr|hub addr|dev addr.
+	 * session ID = dev_addr | hub addr |parent hub addr|...|root hub bdf
+	 * 		8 bits       8bits          8 bits               16bits
 	 */
-	pnode = myself;
-	i = 0;
-	while (pnode != DI_NODE_NIL) {
-		if (di_prop_exists(DDI_DEV_T_ANY, pnode, "root-hub") == 1) {
-			/* walk to root */
-			uint32_t *regbuf = NULL;
-			uint32_t reg;
-
-			n = di_prop_lookup_ints(DDI_DEV_T_ANY, pnode, "reg",
-			    (int **)&regbuf);
-			reg = regbuf[0];
-			bdf = (PCI_REG_BUS_G(reg) << 8) |
-			    (PCI_REG_DEV_G(reg) << 3) | PCI_REG_FUNC_G(reg);
-			session_id |= (bdf << i * 8);
-
-			/* same as 'unit-address' property */
-			bus_number =
-			    (PCI_REG_DEV_G(reg) << 3) | PCI_REG_FUNC_G(reg);
-
-			usbi_dbg("device bus address=%s:%x",
-			    di_bus_addr(pnode), bus_number);
+	if (myself == DI_NODE_NIL)
+		return (DI_WALK_CONTINUE);
 
-			break;
-		}
+	dn = myself;
+	/* find the root hub */
+	while (di_prop_exists(DDI_DEV_T_ANY, dn, "root-hub") != 1) {
+		usbi_dbg("find_root_hub:%s", di_devfs_path(dn));
+		n = di_prop_lookup_ints(DDI_DEV_T_ANY, dn,
+				"assigned-address", &addr_prop);
+		session_id |= ((addr_prop[0] & 0xff) << i++ * 8);
+		dn = di_parent_node(dn);
+	}
 
+	/* dn is the root hub node */
+	n = di_prop_lookup_ints(DDI_DEV_T_ANY, dn, "reg", (int **)&regbuf);
+	reg = regbuf[0];
+	bdf = (PCI_REG_BUS_G(reg) << 8) | (PCI_REG_DEV_G(reg) << 3) | PCI_REG_FUNC_G(reg);
+	/* bdf must larger than i*8 bits */
+	session_id |= (bdf << i * 8);
+	bus_number = (PCI_REG_DEV_G(reg) << 3) | PCI_REG_FUNC_G(reg);
+
+	usbi_dbg("device bus address=%s:%x, name:%s",
+	    di_bus_addr(myself), bus_number, di_node_name(dn));
+	usbi_dbg("session id org:%lx", session_id);
+
+	/* dn is the usb device */
+	for (dn = di_child_node(myself); dn != DI_NODE_NIL; dn = di_sibling_node(dn)) {
+		usbi_dbg("device path:%s", di_devfs_path(dn));
+		/* skip hub devices, because its driver can not been unload */
+		if (di_prop_lookup_ints(DDI_DEV_T_ANY, dn, "usb-port-count", &addr_prop) != -1)
+			continue;
 		/* usb_addr */
-		n = di_prop_lookup_ints(DDI_DEV_T_ANY, pnode,
+		n = di_prop_lookup_ints(DDI_DEV_T_ANY, dn,
 		    "assigned-address", &addr_prop);
 		if ((n != 1) || (addr_prop[0] == 0)) {
 			usbi_dbg("cannot get valid usb_addr");
-
-			return (DI_WALK_CONTINUE);
+			continue;
 		}
 
-		session_id |= ((addr_prop[0] & 0xff) << i * 8);
-		if (++i > 7)
-			break;
+		sid = (session_id << 8) | (addr_prop[0] & 0xff) ;
+		usbi_dbg("session id %lx", sid);
 
-		pnode = di_parent_node(pnode);
-	}
-
-	path = di_devlink_path(link);
-	dev = usbi_get_device_by_session_id(nargs->ctx, session_id);
-	if (dev == NULL) {
-		dev = usbi_alloc_device(nargs->ctx, session_id);
+		dev = usbi_get_device_by_session_id(nargs->ctx, sid);
 		if (dev == NULL) {
-			usbi_dbg("can't alloc device");
-
-			return (DI_WALK_TERMINATE);
-		}
-		devpriv = (sunos_dev_priv_t *)dev->os_priv;
-		if ((newpath = strrchr(path, '/')) == NULL) {
-			libusb_unref_device(dev);
-
-			return (DI_WALK_TERMINATE);
-		}
-		devpriv->ugenpath = strndup(path, strlen(path) -
-		    strlen(newpath));
-		dev->bus_number = bus_number;
-
-		if (sunos_fill_in_dev_info(myself, dev) != LIBUSB_SUCCESS) {
-			libusb_unref_device(dev);
+			dev = usbi_alloc_device(nargs->ctx, sid);
+			if (dev == NULL) {
+				usbi_dbg("can't alloc device");
+				continue;
+			}
+			devpriv = (sunos_dev_priv_t *)dev->os_priv;
+			dev->bus_number = bus_number;
 
-			return (DI_WALK_TERMINATE);
-		}
-		if (usbi_sanitize_device(dev) < 0) {
-			libusb_unref_device(dev);
-			usbi_dbg("sanatize failed: ");
-			return (DI_WALK_TERMINATE);
+			if (sunos_fill_in_dev_info(dn, dev) != LIBUSB_SUCCESS) {
+				libusb_unref_device(dev);
+				usbi_dbg("get infomation fail");
+				continue;
+			}
+			if (usbi_sanitize_device(dev) < 0) {
+				libusb_unref_device(dev);
+				usbi_dbg("sanatize failed: ");
+				return (DI_WALK_TERMINATE);
+			}
+		} else {
+			devpriv = (sunos_dev_priv_t *)dev->os_priv;
+			usbi_dbg("Dev %s exists", devpriv->ugenpath);
 		}
-	} else {
-		usbi_dbg("Dev %s exists", path);
-	}
-
-	devpriv = (sunos_dev_priv_t *)dev->os_priv;
-	if (nargs->last_ugenpath == NULL) {
-		/* first device */
-		nargs->last_ugenpath = devpriv->ugenpath;
 
 		if (discovered_devs_append(*(nargs->discdevs), dev) == NULL) {
 			usbi_dbg("cannot append device");
@@ -285,11 +665,11 @@ sunos_add_devices(di_devlink_t link, void *arg)
 		 * hereafter. Front end or app should take care of their ref.
 		 */
 		libusb_unref_device(dev);
-	}
 
-	usbi_dbg("Device %s %s id=0x%llx, devcount:%d, bdf=%x",
-	    devpriv->ugenpath, path, (uint64_t)session_id,
-	    (*nargs->discdevs)->len, bdf);
+		usbi_dbg("Device %s %s id=0x%llx, devcount:%d, bdf=%x",
+		    devpriv->ugenpath, di_devfs_path(dn), (uint64_t)sid,
+		    (*nargs->discdevs)->len, bdf);
+	}
 
 	return (DI_WALK_CONTINUE);
 }
@@ -303,14 +683,14 @@ sunos_walk_minor_node_link(di_node_t node, void *args)
 	struct node_args *nargs = (struct node_args *)args;
 	di_devlink_handle_t devlink_hdl = nargs->dlink_hdl;
 
-	/* walk each minor to find ugen devices */
+	/* walk each minor to find usb devices */
         while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
                 minor_path = di_devfs_minor_path(minor);
                 arg.nargs = args;
 		arg.myself = node;
                 arg.minor = minor;
                 (void) di_devlink_walk(devlink_hdl,
-		    "^usb/[0-9a-f]+[.][0-9a-f]+", minor_path,
+		    "^usb/hub[0-9]+", minor_path,
 		    DI_PRIMARY_LINK, (void *)&arg, sunos_add_devices);
                 di_devfs_path_free(minor_path);
         }
@@ -496,7 +876,7 @@ sunos_check_device_and_status_open(struct libusb_device_handle *hdl,
 		usbi_dbg("can't find interface for endpoint 0x%02x",
 		    ep_addr);
 
-		return (LIBUSB_ERROR_ACCESS);
+		return (EACCES);
 	}
 
 	/* create filename */
@@ -603,6 +983,11 @@ sunos_open(struct libusb_device_handle *handle)
 		hpriv->eps[i].statfd = -1;
 	}
 
+	if (sunos_kernel_driver_active(handle, 0)) {
+		/* pretend we can open the device */
+		return (LIBUSB_SUCCESS);
+	}
+
 	if ((ret = sunos_usb_open_ep0(hpriv, dpriv)) != LIBUSB_SUCCESS) {
 		usbi_dbg("fail: %d", ret);
 		return (ret);
@@ -1010,9 +1395,7 @@ void
 sunos_destroy_device(struct libusb_device *dev)
 {
 	sunos_dev_priv_t *dpriv = (sunos_dev_priv_t *)dev->os_priv;
-
-	usbi_dbg("");
-
+	usbi_dbg("destroy everyting");
 	free(dpriv->raw_cfgdescr);
 	free(dpriv->ugenpath);
 	free(dpriv->phypath);
@@ -1254,7 +1637,7 @@ sunos_usb_get_status(int fd)
 	return (status);
 }
 
-const struct usbi_os_backend sunos_backend = {
+const struct usbi_os_backend usbi_backend = {
         .name = "Solaris",
         .caps = 0,
         .init = sunos_init,
@@ -1276,9 +1659,9 @@ const struct usbi_os_backend sunos_backend = {
         .reset_device = sunos_reset_device, /* TODO */
         .alloc_streams = NULL,
         .free_streams = NULL,
-        .kernel_driver_active = NULL,
-        .detach_kernel_driver = NULL,
-        .attach_kernel_driver = NULL,
+        .kernel_driver_active = sunos_kernel_driver_active,
+        .detach_kernel_driver = sunos_detach_kernel_driver,
+        .attach_kernel_driver = sunos_attach_kernel_driver,
         .destroy_device = sunos_destroy_device,
         .submit_transfer = sunos_submit_transfer,
         .cancel_transfer = sunos_cancel_transfer,

+ 6 - 0
vendor/github.com/karalabe/hid/libusb/libusb/os/sunos_usb.h → vendor/github.com/karalabe/usb/libusb/libusb/os/sunos_usb.h

@@ -65,6 +65,12 @@ struct devlink_cbarg {
 	di_minor_t		minor;
 };
 
+typedef struct walk_link {
+	char *path;
+	int len;
+	char **linkpp;
+} walk_link_t;
+
 /* AIO callback args */
 struct aio_callback_args{
 	struct libusb_transfer *transfer;

+ 6 - 5
vendor/github.com/karalabe/hid/libusb/libusb/os/threads_posix.c → vendor/github.com/karalabe/usb/libusb/libusb/os/threads_posix.c

@@ -29,7 +29,7 @@
 # include <unistd.h>
 # include <sys/syscall.h>
 #elif defined(__APPLE__)
-# include <mach/mach.h>
+# include <pthread.h>
 #elif defined(__CYGWIN__)
 # include <windows.h>
 #endif
@@ -43,7 +43,7 @@ int usbi_cond_timedwait(pthread_cond_t *cond,
 	struct timespec timeout;
 	int r;
 
-	r = usbi_backend->clock_gettime(USBI_CLOCK_REALTIME, &timeout);
+	r = usbi_backend.clock_gettime(USBI_CLOCK_REALTIME, &timeout);
 	if (r < 0)
 		return r;
 
@@ -59,7 +59,7 @@ int usbi_cond_timedwait(pthread_cond_t *cond,
 
 int usbi_get_tid(void)
 {
-	int ret = -1;
+	int ret;
 #if defined(__ANDROID__)
 	ret = gettid();
 #elif defined(__linux__)
@@ -69,10 +69,11 @@ int usbi_get_tid(void)
 	   real thread support. For 5.1 and earlier, -1 is returned. */
 	ret = syscall(SYS_getthrid);
 #elif defined(__APPLE__)
-	ret = mach_thread_self();
-	mach_port_deallocate(mach_task_self(), ret);
+	ret = (int)pthread_mach_thread_np(pthread_self());
 #elif defined(__CYGWIN__)
 	ret = GetCurrentThreadId();
+#else
+	ret = -1;
 #endif
 /* TODO: NetBSD thread ID support */
 	return ret;

+ 102 - 0
vendor/github.com/karalabe/usb/libusb/libusb/os/threads_posix.h

@@ -0,0 +1,102 @@
+/*
+ * libusb synchronization using POSIX Threads
+ *
+ * Copyright © 2010 Peter Stuge <peter@stuge.se>
+ *
+ * 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_POSIX_H
+#define LIBUSB_THREADS_POSIX_H
+
+#include <pthread.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#define USBI_MUTEX_INITIALIZER	PTHREAD_MUTEX_INITIALIZER
+typedef pthread_mutex_t usbi_mutex_static_t;
+static inline void usbi_mutex_static_lock(usbi_mutex_static_t *mutex)
+{
+	(void)pthread_mutex_lock(mutex);
+}
+static inline void usbi_mutex_static_unlock(usbi_mutex_static_t *mutex)
+{
+	(void)pthread_mutex_unlock(mutex);
+}
+
+typedef pthread_mutex_t usbi_mutex_t;
+static inline int usbi_mutex_init(usbi_mutex_t *mutex)
+{
+	return pthread_mutex_init(mutex, NULL);
+}
+static inline void usbi_mutex_lock(usbi_mutex_t *mutex)
+{
+	(void)pthread_mutex_lock(mutex);
+}
+static inline void usbi_mutex_unlock(usbi_mutex_t *mutex)
+{
+	(void)pthread_mutex_unlock(mutex);
+}
+static inline int usbi_mutex_trylock(usbi_mutex_t *mutex)
+{
+	return pthread_mutex_trylock(mutex);
+}
+static inline void usbi_mutex_destroy(usbi_mutex_t *mutex)
+{
+	(void)pthread_mutex_destroy(mutex);
+}
+
+typedef pthread_cond_t usbi_cond_t;
+static inline void usbi_cond_init(pthread_cond_t *cond)
+{
+	(void)pthread_cond_init(cond, NULL);
+}
+static inline int usbi_cond_wait(usbi_cond_t *cond, usbi_mutex_t *mutex)
+{
+	return pthread_cond_wait(cond, mutex);
+}
+int usbi_cond_timedwait(usbi_cond_t *cond,
+	usbi_mutex_t *mutex, const struct timeval *tv);
+static inline void usbi_cond_broadcast(usbi_cond_t *cond)
+{
+	(void)pthread_cond_broadcast(cond);
+}
+static inline void usbi_cond_destroy(usbi_cond_t *cond)
+{
+	(void)pthread_cond_destroy(cond);
+}
+
+typedef pthread_key_t usbi_tls_key_t;
+static inline void usbi_tls_key_create(usbi_tls_key_t *key)
+{
+	(void)pthread_key_create(key, NULL);
+}
+static inline void *usbi_tls_key_get(usbi_tls_key_t key)
+{
+	return pthread_getspecific(key);
+}
+static inline void usbi_tls_key_set(usbi_tls_key_t key, void *ptr)
+{
+	(void)pthread_setspecific(key, ptr);
+}
+static inline void usbi_tls_key_delete(usbi_tls_key_t key)
+{
+	(void)pthread_key_delete(key);
+}
+
+int usbi_get_tid(void);
+
+#endif /* LIBUSB_THREADS_POSIX_H */

+ 126 - 0
vendor/github.com/karalabe/usb/libusb/libusb/os/threads_windows.c

@@ -0,0 +1,126 @@
+/*
+ * libusb synchronization on Microsoft Windows
+ *
+ * Copyright © 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 <errno.h>
+
+#include "libusbi.h"
+
+struct usbi_cond_perthread {
+	struct list_head list;
+	HANDLE event;
+};
+
+void usbi_mutex_static_lock(usbi_mutex_static_t *mutex)
+{
+	while (InterlockedExchange(mutex, 1L) == 1L)
+		SleepEx(0, TRUE);
+}
+
+void usbi_cond_init(usbi_cond_t *cond)
+{
+	list_init(&cond->waiters);
+	list_init(&cond->not_waiting);
+}
+
+static int usbi_cond_intwait(usbi_cond_t *cond,
+	usbi_mutex_t *mutex, DWORD timeout_ms)
+{
+	struct usbi_cond_perthread *pos;
+	DWORD r;
+
+	// Same assumption as usbi_cond_broadcast() holds
+	if (list_empty(&cond->not_waiting)) {
+		pos = malloc(sizeof(*pos));
+		if (pos == NULL)
+			return ENOMEM; // This errno is not POSIX-allowed.
+		pos->event = CreateEvent(NULL, FALSE, FALSE, NULL); // auto-reset.
+		if (pos->event == NULL) {
+			free(pos);
+			return ENOMEM;
+		}
+	} else {
+		pos = list_first_entry(&cond->not_waiting, struct usbi_cond_perthread, list);
+		list_del(&pos->list); // remove from not_waiting list.
+		// Ensure the event is clear before waiting
+		WaitForSingleObject(pos->event, 0);
+	}
+
+	list_add(&pos->list, &cond->waiters);
+
+	LeaveCriticalSection(mutex);
+	r = WaitForSingleObject(pos->event, timeout_ms);
+	EnterCriticalSection(mutex);
+
+	list_del(&pos->list);
+	list_add(&pos->list, &cond->not_waiting);
+
+	if (r == WAIT_OBJECT_0)
+		return 0;
+	else if (r == WAIT_TIMEOUT)
+		return ETIMEDOUT;
+	else
+		return EINVAL;
+}
+
+// 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 timeval *tv)
+{
+	DWORD millis;
+
+	millis = (DWORD)(tv->tv_sec * 1000) + (tv->tv_usec / 1000);
+	/* round up to next millisecond */
+	if (tv->tv_usec % 1000)
+		millis++;
+	return usbi_cond_intwait(cond, mutex, millis);
+}
+
+void 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!
+	struct usbi_cond_perthread *pos;
+
+	list_for_each_entry(pos, &cond->waiters, list, struct usbi_cond_perthread)
+		SetEvent(pos->event);
+	// The wait function will remove its respective item from the list.
+}
+
+void 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, *next;
+
+	if (!list_empty(&cond->waiters))
+		return; // (!see above!)
+	list_for_each_entry_safe(pos, next, &cond->not_waiting, list, struct usbi_cond_perthread) {
+		CloseHandle(pos->event);
+		list_del(&pos->list);
+		free(pos);
+	}
+}

+ 64 - 29
vendor/github.com/karalabe/hid/libusb/libusb/os/threads_windows.h → vendor/github.com/karalabe/usb/libusb/libusb/os/threads_windows.h

@@ -21,17 +21,40 @@
 #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
+#define USBI_MUTEX_INITIALIZER	0L
+#ifdef _WIN32_WCE
+typedef LONG usbi_mutex_static_t;
+#else
+typedef volatile LONG usbi_mutex_static_t;
+#endif
+void usbi_mutex_static_lock(usbi_mutex_static_t *mutex);
+static inline void usbi_mutex_static_unlock(usbi_mutex_static_t *mutex)
+{
+	InterlockedExchange(mutex, 0L);
+}
 
-typedef struct usbi_cond {
-	// 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;
-} usbi_cond_t;
+typedef CRITICAL_SECTION usbi_mutex_t;
+static inline int usbi_mutex_init(usbi_mutex_t *mutex)
+{
+	InitializeCriticalSection(mutex);
+	return 0;
+}
+static inline void usbi_mutex_lock(usbi_mutex_t *mutex)
+{
+	EnterCriticalSection(mutex);
+}
+static inline void usbi_mutex_unlock(usbi_mutex_t *mutex)
+{
+	LeaveCriticalSection(mutex);
+}
+static inline int usbi_mutex_trylock(usbi_mutex_t *mutex)
+{
+	return !TryEnterCriticalSection(mutex);
+}
+static inline void usbi_mutex_destroy(usbi_mutex_t *mutex)
+{
+	DeleteCriticalSection(mutex);
+}
 
 // We *were* getting timespec from pthread.h:
 #if (!defined(HAVE_STRUCT_TIMESPEC) && !defined(_TIMESPEC_DEFINED))
@@ -45,32 +68,44 @@ struct timespec {
 
 // We *were* getting ETIMEDOUT from pthread.h:
 #ifndef ETIMEDOUT
-#  define ETIMEDOUT 10060     /* This is the value in winsock.h. */
+#define ETIMEDOUT	10060	/* This is the value in winsock.h. */
 #endif
 
-#define usbi_tls_key_t		DWORD
-
-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);
-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);
+typedef struct usbi_cond {
+	// 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;
+} usbi_cond_t;
 
-int usbi_cond_init(usbi_cond_t *cond);
+void usbi_cond_init(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 timeval *tv);
-int usbi_cond_broadcast(usbi_cond_t *cond);
-int usbi_cond_destroy(usbi_cond_t *cond);
+void usbi_cond_broadcast(usbi_cond_t *cond);
+void usbi_cond_destroy(usbi_cond_t *cond);
 
-int usbi_tls_key_create(usbi_tls_key_t *key);
-void *usbi_tls_key_get(usbi_tls_key_t key);
-int usbi_tls_key_set(usbi_tls_key_t key, void *value);
-int usbi_tls_key_delete(usbi_tls_key_t key);
+typedef DWORD usbi_tls_key_t;
+static inline void usbi_tls_key_create(usbi_tls_key_t *key)
+{
+	*key = TlsAlloc();
+}
+static inline void *usbi_tls_key_get(usbi_tls_key_t key)
+{
+	return TlsGetValue(key);
+}
+static inline void usbi_tls_key_set(usbi_tls_key_t key, void *ptr)
+{
+	(void)TlsSetValue(key, ptr);
+}
+static inline void usbi_tls_key_delete(usbi_tls_key_t key)
+{
+	(void)TlsFree(key);
+}
 
-int usbi_get_tid(void);
+static inline int usbi_get_tid(void)
+{
+	return (int)GetCurrentThreadId();
+}
 
 #endif /* LIBUSB_THREADS_WINDOWS_H */

+ 28 - 39
vendor/github.com/karalabe/hid/libusb/libusb/os/wince_usb.c → vendor/github.com/karalabe/usb/libusb/libusb/os/wince_usb.c

@@ -31,7 +31,7 @@
 #include "wince_usb.h"
 
 // Global variables
-int windows_version = WINDOWS_CE;
+int errno = 0;
 static uint64_t hires_frequency, hires_ticks_to_ps;
 static HANDLE driver_handle = INVALID_HANDLE_VALUE;
 static int concurrent_usage = -1;
@@ -109,7 +109,7 @@ static int translate_driver_error(DWORD error)
 	}
 }
 
-static int init_dllimports(void)
+static BOOL init_dllimports(void)
 {
 	DLL_GET_HANDLE(ceusbkwrapper);
 	DLL_LOAD_FUNC(ceusbkwrapper, UkwOpenDriver, TRUE);
@@ -135,7 +135,7 @@ static int init_dllimports(void)
 	DLL_LOAD_FUNC(ceusbkwrapper, UkwIssueBulkTransfer, TRUE);
 	DLL_LOAD_FUNC(ceusbkwrapper, UkwIsPipeHalted, TRUE);
 
-	return LIBUSB_SUCCESS;
+	return TRUE;
 }
 
 static void exit_dllimports(void)
@@ -186,11 +186,8 @@ static int wince_init(struct libusb_context *ctx)
 	// NB: concurrent usage supposes that init calls are equally balanced with
 	// exit calls. If init is called more than exit, we will not exit properly
 	if ( ++concurrent_usage == 0 ) {	// First init?
-		// Initialize pollable file descriptors
-		init_polling();
-
 		// Load DLL imports
-		if (init_dllimports() != LIBUSB_SUCCESS) {
+		if (!init_dllimports()) {
 			usbi_err(ctx, "could not resolve DLL functions");
 			r = LIBUSB_ERROR_NOT_SUPPORTED;
 			goto init_exit;
@@ -223,7 +220,6 @@ static int wince_init(struct libusb_context *ctx)
 init_exit: // Holds semaphore here.
 	if (!concurrent_usage && r != LIBUSB_SUCCESS) { // First init failed?
 		exit_dllimports();
-		exit_polling();
 
 		if (driver_handle != INVALID_HANDLE_VALUE) {
 			UkwCloseDriver(driver_handle);
@@ -239,10 +235,11 @@ init_exit: // Holds semaphore here.
 	return r;
 }
 
-static void wince_exit(void)
+static void wince_exit(struct libusb_context *ctx)
 {
 	HANDLE semaphore;
 	TCHAR sem_name[11 + 8 + 1]; // strlen("libusb_init") + (32-bit hex PID) + '\0'
+	UNUSED(ctx);
 
 	_stprintf(sem_name, _T("libusb_init%08X"), (unsigned int)(GetCurrentProcessId() & 0xFFFFFFFF));
 	semaphore = CreateSemaphore(NULL, 1, 1, sem_name);
@@ -259,7 +256,6 @@ static void wince_exit(void)
 	// Only works if exits and inits are balanced exactly
 	if (--concurrent_usage < 0) {	// Last exit
 		exit_dllimports();
-		exit_polling();
 
 		if (driver_handle != INVALID_HANDLE_VALUE) {
 			UkwCloseDriver(driver_handle);
@@ -328,7 +324,7 @@ static int wince_get_device_list(
 		}
 
 		new_devices = discovered_devs_append(new_devices, dev);
-		if (!discdevs) {
+		if (!new_devices) {
 			r = LIBUSB_ERROR_NO_MEM;
 			goto err_out;
 		}
@@ -541,12 +537,9 @@ static void wince_destroy_device(struct libusb_device *dev)
 static void wince_clear_transfer_priv(struct usbi_transfer *itransfer)
 {
 	struct wince_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
-	struct winfd wfd = fd_to_winfd(transfer_priv->pollable_fd.fd);
 
-	// No need to cancel transfer as it is either complete or abandoned
-	wfd.itransfer = NULL;
-	CloseHandle(wfd.handle);
-	usbi_free_fd(&transfer_priv->pollable_fd);
+	usbi_close(transfer_priv->pollable_fd.fd);
+	transfer_priv->pollable_fd = INVALID_WINFD;
 }
 
 static int wince_cancel_transfer(struct usbi_transfer *itransfer)
@@ -570,11 +563,10 @@ static int wince_submit_control_or_bulk_transfer(struct usbi_transfer *itransfer
 	BOOL direction_in, ret;
 	struct winfd wfd;
 	DWORD flags;
-	HANDLE eventHandle;
 	PUKW_CONTROL_HEADER setup = NULL;
 	const BOOL control_transfer = transfer->type == LIBUSB_TRANSFER_TYPE_CONTROL;
+	int r;
 
-	transfer_priv->pollable_fd = INVALID_WINFD;
 	if (control_transfer) {
 		setup = (PUKW_CONTROL_HEADER) transfer->buffer;
 		direction_in = setup->bmRequestType & LIBUSB_ENDPOINT_IN;
@@ -584,19 +576,18 @@ static int wince_submit_control_or_bulk_transfer(struct usbi_transfer *itransfer
 	flags = direction_in ? UKW_TF_IN_TRANSFER : UKW_TF_OUT_TRANSFER;
 	flags |= UKW_TF_SHORT_TRANSFER_OK;
 
-	eventHandle = CreateEvent(NULL, FALSE, FALSE, NULL);
-	if (eventHandle == NULL) {
-		usbi_err(ctx, "Failed to create event for async transfer");
+	wfd = usbi_create_fd();
+	if (wfd.fd < 0)
 		return LIBUSB_ERROR_NO_MEM;
-	}
 
-	wfd = usbi_create_fd(eventHandle, direction_in ? RW_READ : RW_WRITE, itransfer, &wince_cancel_transfer);
-	if (wfd.fd < 0) {
-		CloseHandle(eventHandle);
-		return LIBUSB_ERROR_NO_MEM;
+	r = usbi_add_pollfd(ctx, wfd.fd, direction_in ? POLLIN : POLLOUT);
+	if (r) {
+		usbi_close(wfd.fd);
+		return r;
 	}
 
 	transfer_priv->pollable_fd = wfd;
+
 	if (control_transfer) {
 		// Split out control setup header and data buffer
 		DWORD bufLen = transfer->length - sizeof(UKW_CONTROL_HEADER);
@@ -612,17 +603,14 @@ static int wince_submit_control_or_bulk_transfer(struct usbi_transfer *itransfer
 		int libusbErr = translate_driver_error(GetLastError());
 		usbi_err(ctx, "UkwIssue%sTransfer failed: error %u",
 			control_transfer ? "Control" : "Bulk", (unsigned int)GetLastError());
-		wince_clear_transfer_priv(itransfer);
+		usbi_remove_pollfd(ctx, wfd.fd);
+		usbi_close(wfd.fd);
+		transfer_priv->pollable_fd = INVALID_WINFD;
 		return libusbErr;
 	}
-	usbi_add_pollfd(ctx, transfer_priv->pollable_fd.fd, direction_in ? POLLIN : POLLOUT);
 
-	return LIBUSB_SUCCESS;
-}
 
-static int wince_submit_iso_transfer(struct usbi_transfer *itransfer)
-{
-	return LIBUSB_ERROR_NOT_SUPPORTED;
+	return LIBUSB_SUCCESS;
 }
 
 static int wince_submit_transfer(struct usbi_transfer *itransfer)
@@ -635,7 +623,6 @@ static int wince_submit_transfer(struct usbi_transfer *itransfer)
 	case LIBUSB_TRANSFER_TYPE_INTERRUPT:
 		return wince_submit_control_or_bulk_transfer(itransfer);
 	case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
-		return wince_submit_iso_transfer(itransfer);
 	case LIBUSB_TRANSFER_TYPE_BULK_STREAM:
 		return LIBUSB_ERROR_NOT_SUPPORTED;
 	default:
@@ -763,7 +750,7 @@ static int wince_handle_events(
 	struct wince_transfer_priv* transfer_priv = NULL;
 	POLL_NFDS_TYPE i = 0;
 	BOOL found = FALSE;
-	struct usbi_transfer *transfer;
+	struct usbi_transfer *itransfer;
 	DWORD io_size, io_result;
 	int r = LIBUSB_SUCCESS;
 
@@ -780,8 +767,8 @@ static int wince_handle_events(
 		// 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);
+		list_for_each_entry(itransfer, &ctx->flying_transfers, list, struct usbi_transfer) {
+			transfer_priv = usbi_transfer_get_os_priv(itransfer);
 			if (transfer_priv->pollable_fd.fd == fds[i].fd) {
 				found = TRUE;
 				break;
@@ -796,7 +783,7 @@ static int wince_handle_events(
 			// 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.
-			wince_handle_callback(transfer, io_result, io_size);
+			wince_handle_callback(itransfer, io_result, io_size);
 		} else if (found) {
 			usbi_err(ctx, "matching transfer for fd %d has not completed", fds[i]);
 			r = LIBUSB_ERROR_OTHER;
@@ -848,11 +835,12 @@ static int wince_clock_gettime(int clk_id, struct timespec *tp)
 	}
 }
 
-const struct usbi_os_backend wince_backend = {
+const struct usbi_os_backend usbi_backend = {
 	"Windows CE",
 	0,
 	wince_init,
 	wince_exit,
+	NULL,				/* set_option() */
 
 	wince_get_device_list,
 	NULL,				/* hotplug_poll */
@@ -893,6 +881,7 @@ const struct usbi_os_backend wince_backend = {
 	NULL,				/* handle_transfer_completion() */
 
 	wince_clock_gettime,
+	0,
 	sizeof(struct wince_device_priv),
 	0,
 	sizeof(struct wince_transfer_priv),

+ 0 - 0
vendor/github.com/karalabe/hid/libusb/libusb/os/wince_usb.h → vendor/github.com/karalabe/usb/libusb/libusb/os/wince_usb.h


+ 20 - 16
vendor/github.com/karalabe/hid/libusb/libusb/os/windows_common.h → vendor/github.com/karalabe/usb/libusb/libusb/os/windows_common.h

@@ -68,31 +68,35 @@
 /*
  * Macros for handling DLL themselves
  */
+#define DLL_HANDLE_NAME(name) __dll_##name##_handle
+
 #define DLL_DECLARE_HANDLE(name)				\
-	static HMODULE __dll_##name##_handle = NULL
+	static HMODULE DLL_HANDLE_NAME(name) = NULL
 
 #define DLL_GET_HANDLE(name)					\
 	do {							\
-		__dll_##name##_handle = DLL_LOAD_LIBRARY(name);	\
-		if (!__dll_##name##_handle)			\
-			return LIBUSB_ERROR_OTHER;		\
+		DLL_HANDLE_NAME(name) = DLL_LOAD_LIBRARY(name);	\
+		if (!DLL_HANDLE_NAME(name))			\
+			return FALSE;				\
 	} while (0)
 
 #define DLL_FREE_HANDLE(name)					\
 	do {							\
-		if (__dll_##name##_handle) {			\
-			FreeLibrary(__dll_##name##_handle);	\
-			__dll_##name##_handle = NULL;		\
+		if (DLL_HANDLE_NAME(name)) {			\
+			FreeLibrary(DLL_HANDLE_NAME(name));	\
+			DLL_HANDLE_NAME(name) = NULL;		\
 		}						\
-	} while(0)
+	} while (0)
 
 
 /*
  * Macros for handling functions within a DLL
  */
+#define DLL_FUNC_NAME(name) __dll_##name##_func_t
+
 #define DLL_DECLARE_FUNC_PREFIXNAME(api, ret, prefixname, name, args)	\
-	typedef ret (api * __dll_##name##_func_t)args;			\
-	static __dll_##name##_func_t prefixname = NULL
+	typedef ret (api * DLL_FUNC_NAME(name))args;			\
+	static DLL_FUNC_NAME(name) prefixname = NULL
 
 #define DLL_DECLARE_FUNC(api, ret, name, args)				\
 	DLL_DECLARE_FUNC_PREFIXNAME(api, ret, name, name, args)
@@ -101,22 +105,22 @@
 
 #define DLL_LOAD_FUNC_PREFIXNAME(dll, prefixname, name, ret_on_failure)	\
 	do {								\
-		HMODULE h = __dll_##dll##_handle;			\
-		prefixname = (__dll_##name##_func_t)GetProcAddress(h,	\
+		HMODULE h = DLL_HANDLE_NAME(dll);			\
+		prefixname = (DLL_FUNC_NAME(name))GetProcAddress(h,	\
 				DLL_STRINGIFY(name));			\
 		if (prefixname)						\
 			break;						\
-		prefixname = (__dll_##name##_func_t)GetProcAddress(h,	\
+		prefixname = (DLL_FUNC_NAME(name))GetProcAddress(h,	\
 				DLL_STRINGIFY(name) DLL_STRINGIFY(A));	\
 		if (prefixname)						\
 			break;						\
-		prefixname = (__dll_##name##_func_t)GetProcAddress(h,	\
+		prefixname = (DLL_FUNC_NAME(name))GetProcAddress(h,	\
 				DLL_STRINGIFY(name) DLL_STRINGIFY(W));	\
 		if (prefixname)						\
 			break;						\
 		if (ret_on_failure)					\
-			return LIBUSB_ERROR_NOT_FOUND;			\
-	} while(0)
+			return FALSE;					\
+	} while (0)
 
 #define DLL_LOAD_FUNC(dll, name, ret_on_failure)			\
 	DLL_LOAD_FUNC_PREFIXNAME(dll, name, name, ret_on_failure)

+ 523 - 106
vendor/github.com/karalabe/hid/libusb/libusb/os/windows_nt_common.c → vendor/github.com/karalabe/usb/libusb/libusb/os/windows_nt_common.c

@@ -32,6 +32,14 @@
 #include "windows_common.h"
 #include "windows_nt_common.h"
 
+// Public
+BOOL (WINAPI *pCancelIoEx)(HANDLE, LPOVERLAPPED);
+enum windows_version windows_version = WINDOWS_UNDEFINED;
+
+ // Global variables for init/exit
+static unsigned int init_count = 0;
+static bool usbdk_available = false;
+
 // Global variables for clock_gettime mechanism
 static uint64_t hires_ticks_to_ps;
 static uint64_t hires_frequency;
@@ -50,6 +58,11 @@ struct timer_request {
 static HANDLE timer_thread = NULL;
 static DWORD timer_thread_id = 0;
 
+/* Kernel32 dependencies */
+DLL_DECLARE_HANDLE(Kernel32);
+/* This call is only available from XP SP2 */
+DLL_DECLARE_FUNC_PREFIXED(WINAPI, BOOL, p, IsWow64Process, (HANDLE, PBOOL));
+
 /* User32 dependencies */
 DLL_DECLARE_HANDLE(User32);
 DLL_DECLARE_FUNC_PREFIXED(WINAPI, BOOL, p, GetMessageA, (LPMSG, HWND, UINT, UINT));
@@ -111,6 +124,11 @@ const char *windows_error_str(DWORD error_code)
 }
 #endif
 
+static inline struct windows_context_priv *_context_priv(struct libusb_context *ctx)
+{
+	return (struct windows_context_priv *)ctx->os_priv;
+}
+
 /* Hash table functions - modified From glibc 2.3.2:
    [Aho,Sethi,Ullman] Compilers: Principles, Techniques and Tools, 1986
    [Knuth]            The Art of Computer Programming, part 3 (6.4)  */
@@ -123,7 +141,7 @@ typedef struct htab_entry {
 } htab_entry;
 
 static htab_entry *htab_table = NULL;
-static usbi_mutex_t htab_mutex = NULL;
+static usbi_mutex_t htab_mutex;
 static unsigned long htab_filled;
 
 /* Before using the hash table we must allocate memory for it.
@@ -256,35 +274,46 @@ out_unlock:
 	return idx;
 }
 
-static int windows_init_dlls(void)
+/*
+* Make a transfer complete synchronously
+*/
+void windows_force_sync_completion(OVERLAPPED *overlapped, ULONG size)
+{
+	overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY;
+	overlapped->InternalHigh = size;
+	SetEvent(overlapped->hEvent);
+}
+
+static BOOL windows_init_dlls(void)
 {
+	DLL_GET_HANDLE(Kernel32);
+	DLL_LOAD_FUNC_PREFIXED(Kernel32, p, IsWow64Process, FALSE);
+	pCancelIoEx = (BOOL (WINAPI *)(HANDLE, LPOVERLAPPED))
+		GetProcAddress(DLL_HANDLE_NAME(Kernel32), "CancelIoEx");
+	usbi_dbg("Will use CancelIo%s for I/O cancellation", pCancelIoEx ? "Ex" : "");
+
 	DLL_GET_HANDLE(User32);
 	DLL_LOAD_FUNC_PREFIXED(User32, p, GetMessageA, TRUE);
 	DLL_LOAD_FUNC_PREFIXED(User32, p, PeekMessageA, TRUE);
 	DLL_LOAD_FUNC_PREFIXED(User32, p, PostThreadMessageA, TRUE);
 
-	return LIBUSB_SUCCESS;
+	return TRUE;
 }
 
 static void windows_exit_dlls(void)
 {
+	DLL_FREE_HANDLE(Kernel32);
 	DLL_FREE_HANDLE(User32);
 }
 
 static bool windows_init_clock(struct libusb_context *ctx)
 {
 	DWORD_PTR affinity, dummy;
-	HANDLE event = NULL;
+	HANDLE event;
 	LARGE_INTEGER li_frequency;
 	int i;
 
 	if (QueryPerformanceFrequency(&li_frequency)) {
-		// Load DLL imports
-		if (windows_init_dlls() != LIBUSB_SUCCESS) {
-			usbi_err(ctx, "could not resolve DLL functions");
-			return false;
-		}
-
 		// 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_frequency = li_frequency.QuadPart;
@@ -340,7 +369,7 @@ static bool windows_init_clock(struct libusb_context *ctx)
 	return true;
 }
 
-void windows_destroy_clock(void)
+static void windows_destroy_clock(void)
 {
 	if (timer_thread) {
 		// actually the signal to quit the thread.
@@ -357,6 +386,110 @@ void windows_destroy_clock(void)
 	}
 }
 
+/* Windows version detection */
+static BOOL is_x64(void)
+{
+	BOOL ret = FALSE;
+
+	// Detect if we're running a 32 or 64 bit system
+	if (sizeof(uintptr_t) < 8) {
+		if (pIsWow64Process != NULL)
+			pIsWow64Process(GetCurrentProcess(), &ret);
+	} else {
+		ret = TRUE;
+	}
+
+	return ret;
+}
+
+static void get_windows_version(void)
+{
+	OSVERSIONINFOEXA vi, vi2;
+	const char *arch, *w = NULL;
+	unsigned major, minor, version;
+	ULONGLONG major_equal, minor_equal;
+	BOOL ws;
+
+	windows_version = WINDOWS_UNDEFINED;
+
+	memset(&vi, 0, sizeof(vi));
+	vi.dwOSVersionInfoSize = sizeof(vi);
+	if (!GetVersionExA((OSVERSIONINFOA *)&vi)) {
+		memset(&vi, 0, sizeof(vi));
+		vi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
+		if (!GetVersionExA((OSVERSIONINFOA *)&vi))
+			return;
+	}
+
+	if (vi.dwPlatformId != VER_PLATFORM_WIN32_NT)
+		return;
+
+	if ((vi.dwMajorVersion > 6) || ((vi.dwMajorVersion == 6) && (vi.dwMinorVersion >= 2))) {
+		// Starting with Windows 8.1 Preview, GetVersionEx() does no longer report the actual OS version
+		// See: http://msdn.microsoft.com/en-us/library/windows/desktop/dn302074.aspx
+
+		major_equal = VerSetConditionMask(0, VER_MAJORVERSION, VER_EQUAL);
+		for (major = vi.dwMajorVersion; major <= 9; major++) {
+			memset(&vi2, 0, sizeof(vi2));
+			vi2.dwOSVersionInfoSize = sizeof(vi2);
+			vi2.dwMajorVersion = major;
+			if (!VerifyVersionInfoA(&vi2, VER_MAJORVERSION, major_equal))
+				continue;
+
+			if (vi.dwMajorVersion < major) {
+				vi.dwMajorVersion = major;
+				vi.dwMinorVersion = 0;
+			}
+
+			minor_equal = VerSetConditionMask(0, VER_MINORVERSION, VER_EQUAL);
+			for (minor = vi.dwMinorVersion; minor <= 9; minor++) {
+				memset(&vi2, 0, sizeof(vi2));
+				vi2.dwOSVersionInfoSize = sizeof(vi2);
+				vi2.dwMinorVersion = minor;
+				if (!VerifyVersionInfoA(&vi2, VER_MINORVERSION, minor_equal))
+					continue;
+
+				vi.dwMinorVersion = minor;
+				break;
+			}
+
+			break;
+		}
+	}
+
+	if ((vi.dwMajorVersion > 0xf) || (vi.dwMinorVersion > 0xf))
+		return;
+
+	ws = (vi.wProductType <= VER_NT_WORKSTATION);
+	version = vi.dwMajorVersion << 4 | vi.dwMinorVersion;
+	switch (version) {
+	case 0x50: windows_version = WINDOWS_2000;  w = "2000";	break;
+	case 0x51: windows_version = WINDOWS_XP;    w = "XP";	break;
+	case 0x52: windows_version = WINDOWS_2003;  w = "2003";	break;
+	case 0x60: windows_version = WINDOWS_VISTA; w = (ws ? "Vista" : "2008");  break;
+	case 0x61: windows_version = WINDOWS_7;	    w = (ws ? "7" : "2008_R2");	  break;
+	case 0x62: windows_version = WINDOWS_8;	    w = (ws ? "8" : "2012");	  break;
+	case 0x63: windows_version = WINDOWS_8_1;   w = (ws ? "8.1" : "2012_R2"); break;
+	case 0x64: windows_version = WINDOWS_10;    w = (ws ? "10" : "2016");	  break;
+	default:
+		if (version < 0x50) {
+			return;
+		} else {
+			windows_version = WINDOWS_11_OR_LATER;
+			w = "11 or later";
+		}
+	}
+
+	arch = is_x64() ? "64-bit" : "32-bit";
+
+	if (vi.wServicePackMinor)
+		usbi_dbg("Windows %s SP%u.%u %s", w, vi.wServicePackMajor, vi.wServicePackMinor, arch);
+	else if (vi.wServicePackMajor)
+		usbi_dbg("Windows %s SP%u %s", w, vi.wServicePackMajor, arch);
+	else
+		usbi_dbg("Windows %s %s", w, arch);
+}
+
 /*
 * Monotonic and real time functions
 */
@@ -401,74 +534,16 @@ static unsigned __stdcall windows_clock_gettime_threaded(void *param)
 	}
 }
 
-int windows_clock_gettime(int clk_id, struct timespec *tp)
-{
-	struct timer_request request;
-#if !defined(_MSC_VER) || (_MSC_VER < 1900)
-	FILETIME filetime;
-	ULARGE_INTEGER rtime;
-#endif
-	DWORD r;
-
-	switch (clk_id) {
-	case USBI_CLOCK_MONOTONIC:
-		if (timer_thread) {
-			request.tp = tp;
-			request.event = CreateEvent(NULL, FALSE, FALSE, NULL);
-			if (request.event == NULL)
-				return LIBUSB_ERROR_NO_MEM;
-
-			if (!pPostThreadMessageA(timer_thread_id, WM_TIMER_REQUEST, 0, (LPARAM)&request)) {
-				usbi_err(NULL, "PostThreadMessage failed for timer thread: %s", windows_error_str(0));
-				CloseHandle(request.event);
-				return LIBUSB_ERROR_OTHER;
-			}
-
-			do {
-				r = WaitForSingleObject(request.event, TIMER_REQUEST_RETRY_MS);
-				if (r == WAIT_TIMEOUT)
-					usbi_dbg("could not obtain a timer value within reasonable timeframe - too much load?");
-				else if (r == WAIT_FAILED)
-					usbi_err(NULL, "WaitForSingleObject failed: %s", windows_error_str(0));
-			} while (r == WAIT_TIMEOUT);
-			CloseHandle(request.event);
-
-			if (r == WAIT_OBJECT_0)
-				return LIBUSB_SUCCESS;
-			else
-				return LIBUSB_ERROR_OTHER;
-		}
-		// Fall through and return real-time if monotonic was not detected @ timer init
-	case USBI_CLOCK_REALTIME:
-#if defined(_MSC_VER) && (_MSC_VER >= 1900)
-		timespec_get(tp, TIME_UTC);
-#else
-		// 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);
-#endif
-		return LIBUSB_SUCCESS;
-	default:
-		return LIBUSB_ERROR_INVALID_PARAM;
-	}
-}
-
-static void windows_transfer_callback(struct usbi_transfer *itransfer, uint32_t io_result, uint32_t io_size)
+static void windows_transfer_callback(const struct windows_backend *backend,
+	struct usbi_transfer *itransfer, DWORD io_result, DWORD io_size)
 {
 	int status, istatus;
 
-	usbi_dbg("handling I/O completion with errcode %u, size %u", io_result, io_size);
+	usbi_dbg("handling I/O completion with errcode %u, size %u", (unsigned int)io_result, (unsigned int)io_size);
 
 	switch (io_result) {
 	case NO_ERROR:
-		status = windows_copy_transfer_data(itransfer, io_size);
+		status = backend->copy_transfer_data(itransfer, (uint32_t)io_size);
 		break;
 	case ERROR_GEN_FAILURE:
 		usbi_dbg("detected endpoint stall");
@@ -479,26 +554,31 @@ static void windows_transfer_callback(struct usbi_transfer *itransfer, uint32_t
 		status = LIBUSB_TRANSFER_TIMED_OUT;
 		break;
 	case ERROR_OPERATION_ABORTED:
-		istatus = windows_copy_transfer_data(itransfer, io_size);
+		istatus = backend->copy_transfer_data(itransfer, (uint32_t)io_size);
 		if (istatus != LIBUSB_TRANSFER_COMPLETED)
 			usbi_dbg("Failed to copy partial data in aborted operation: %d", istatus);
 
 		usbi_dbg("detected operation aborted");
 		status = LIBUSB_TRANSFER_CANCELLED;
 		break;
+	case ERROR_FILE_NOT_FOUND:
+		usbi_dbg("detected device removed");
+		status = LIBUSB_TRANSFER_NO_DEVICE;
+		break;
 	default:
-		usbi_err(ITRANSFER_CTX(itransfer), "detected I/O error %u: %s", io_result, windows_error_str(io_result));
+		usbi_err(ITRANSFER_CTX(itransfer), "detected I/O error %u: %s", (unsigned int)io_result, windows_error_str(io_result));
 		status = LIBUSB_TRANSFER_ERROR;
 		break;
 	}
-	windows_clear_transfer_priv(itransfer);	// Cancel polling
+	backend->clear_transfer_priv(itransfer);	// Cancel polling
 	if (status == LIBUSB_TRANSFER_CANCELLED)
 		usbi_handle_transfer_cancellation(itransfer);
 	else
 		usbi_handle_transfer_completion(itransfer, (enum libusb_transfer_status)status);
 }
 
-void windows_handle_callback(struct usbi_transfer *itransfer, uint32_t io_result, uint32_t io_size)
+static void windows_handle_callback(const struct windows_backend *backend,
+	struct usbi_transfer *itransfer, DWORD io_result, DWORD io_size)
 {
 	struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
 
@@ -507,7 +587,7 @@ void windows_handle_callback(struct usbi_transfer *itransfer, uint32_t io_result
 	case LIBUSB_TRANSFER_TYPE_BULK:
 	case LIBUSB_TRANSFER_TYPE_INTERRUPT:
 	case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
-		windows_transfer_callback(itransfer, io_result, io_size);
+		windows_transfer_callback(backend, itransfer, io_result, io_size);
 		break;
 	case LIBUSB_TRANSFER_TYPE_BULK_STREAM:
 		usbi_warn(ITRANSFER_CTX(itransfer), "bulk stream transfers are not yet supported on this platform");
@@ -517,13 +597,269 @@ void windows_handle_callback(struct usbi_transfer *itransfer, uint32_t io_result
 	}
 }
 
-int windows_handle_events(struct libusb_context *ctx, struct pollfd *fds, POLL_NFDS_TYPE nfds, int num_ready)
+static int windows_init(struct libusb_context *ctx)
 {
-	POLL_NFDS_TYPE i;
-	bool found = false;
-	struct usbi_transfer *transfer;
-	struct winfd *pollable_fd = NULL;
+	struct windows_context_priv *priv = _context_priv(ctx);
+	HANDLE semaphore;
+	char sem_name[11 + 8 + 1]; // strlen("libusb_init") + (32-bit hex PID) + '\0'
+	int r = LIBUSB_ERROR_OTHER;
+	bool winusb_backend_init = false;
+
+	sprintf(sem_name, "libusb_init%08X", (unsigned int)(GetCurrentProcessId() & 0xFFFFFFFF));
+	semaphore = CreateSemaphoreA(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 (++init_count == 1) { // First init?
+		// Load DLL imports
+		if (!windows_init_dlls()) {
+			usbi_err(ctx, "could not resolve DLL functions");
+			goto init_exit;
+		}
+
+		get_windows_version();
+
+		if (windows_version == WINDOWS_UNDEFINED) {
+			usbi_err(ctx, "failed to detect Windows version");
+			r = LIBUSB_ERROR_NOT_SUPPORTED;
+			goto init_exit;
+		}
+
+		if (!windows_init_clock(ctx))
+			goto init_exit;
+
+		if (!htab_create(ctx))
+			goto init_exit;
+
+		r = winusb_backend.init(ctx);
+		if (r != LIBUSB_SUCCESS)
+			goto init_exit;
+		winusb_backend_init = true;
+
+		r = usbdk_backend.init(ctx);
+		if (r == LIBUSB_SUCCESS) {
+			usbi_dbg("UsbDk backend is available");
+			usbdk_available = true;
+		} else {
+			usbi_info(ctx, "UsbDk backend is not available");
+			// Do not report this as an error
+			r = LIBUSB_SUCCESS;
+		}
+	}
+
+	// By default, new contexts will use the WinUSB backend
+	priv->backend = &winusb_backend;
+
+	r = LIBUSB_SUCCESS;
+
+init_exit: // Holds semaphore here
+	if ((init_count == 1) && (r != LIBUSB_SUCCESS)) { // First init failed?
+		if (winusb_backend_init)
+			winusb_backend.exit(ctx);
+		htab_destroy();
+		windows_destroy_clock();
+		windows_exit_dlls();
+		--init_count;
+	}
+
+	ReleaseSemaphore(semaphore, 1, NULL); // increase count back to 1
+	CloseHandle(semaphore);
+	return r;
+}
+
+static void windows_exit(struct libusb_context *ctx)
+{
+	HANDLE semaphore;
+	char sem_name[11 + 8 + 1]; // strlen("libusb_init") + (32-bit hex PID) + '\0'
+	UNUSED(ctx);
+
+	sprintf(sem_name, "libusb_init%08X", (unsigned int)(GetCurrentProcessId() & 0xFFFFFFFF));
+	semaphore = CreateSemaphoreA(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 (--init_count == 0) { // Last exit
+		if (usbdk_available) {
+			usbdk_backend.exit(ctx);
+			usbdk_available = false;
+		}
+		winusb_backend.exit(ctx);
+		htab_destroy();
+		windows_destroy_clock();
+		windows_exit_dlls();
+	}
+
+	ReleaseSemaphore(semaphore, 1, NULL); // increase count back to 1
+	CloseHandle(semaphore);
+}
+
+static int windows_set_option(struct libusb_context *ctx, enum libusb_option option, va_list ap)
+{
+	struct windows_context_priv *priv = _context_priv(ctx);
+
+	UNUSED(ap);
+
+	switch (option) {
+	case LIBUSB_OPTION_USE_USBDK:
+		if (usbdk_available) {
+			usbi_dbg("switching context %p to use UsbDk backend", ctx);
+			priv->backend = &usbdk_backend;
+		} else {
+			usbi_err(ctx, "UsbDk backend not available");
+			return LIBUSB_ERROR_NOT_FOUND;
+		}
+		return LIBUSB_SUCCESS;
+	default:
+		return LIBUSB_ERROR_NOT_SUPPORTED;
+	}
+
+}
+
+static int windows_get_device_list(struct libusb_context *ctx, struct discovered_devs **discdevs)
+{
+	struct windows_context_priv *priv = _context_priv(ctx);
+	return priv->backend->get_device_list(ctx, discdevs);
+}
+
+static int windows_open(struct libusb_device_handle *dev_handle)
+{
+	struct windows_context_priv *priv = _context_priv(HANDLE_CTX(dev_handle));
+	return priv->backend->open(dev_handle);
+}
+
+static void windows_close(struct libusb_device_handle *dev_handle)
+{
+	struct windows_context_priv *priv = _context_priv(HANDLE_CTX(dev_handle));
+	priv->backend->close(dev_handle);
+}
+
+static int windows_get_device_descriptor(struct libusb_device *dev,
+	unsigned char *buffer, int *host_endian)
+{
+	struct windows_context_priv *priv = _context_priv(DEVICE_CTX(dev));
+	*host_endian = 0;
+	return priv->backend->get_device_descriptor(dev, buffer);
+}
+
+static int windows_get_active_config_descriptor(struct libusb_device *dev,
+	unsigned char *buffer, size_t len, int *host_endian)
+{
+	struct windows_context_priv *priv = _context_priv(DEVICE_CTX(dev));
+	*host_endian = 0;
+	return priv->backend->get_active_config_descriptor(dev, buffer, len);
+}
+
+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_context_priv *priv = _context_priv(DEVICE_CTX(dev));
+	*host_endian = 0;
+	return priv->backend->get_config_descriptor(dev, config_index, buffer, len);
+}
+
+static int windows_get_config_descriptor_by_value(struct libusb_device *dev,
+	uint8_t bConfigurationValue, unsigned char **buffer, int *host_endian)
+{
+	struct windows_context_priv *priv = _context_priv(DEVICE_CTX(dev));
+	*host_endian = 0;
+	return priv->backend->get_config_descriptor_by_value(dev, bConfigurationValue, buffer);
+}
+
+static int windows_get_configuration(struct libusb_device_handle *dev_handle, int *config)
+{
+	struct windows_context_priv *priv = _context_priv(HANDLE_CTX(dev_handle));
+	return priv->backend->get_configuration(dev_handle, config);
+}
+
+static int windows_set_configuration(struct libusb_device_handle *dev_handle, int config)
+{
+	struct windows_context_priv *priv = _context_priv(HANDLE_CTX(dev_handle));
+	return priv->backend->set_configuration(dev_handle, config);
+}
+
+static int windows_claim_interface(struct libusb_device_handle *dev_handle, int interface_number)
+{
+	struct windows_context_priv *priv = _context_priv(HANDLE_CTX(dev_handle));
+	return priv->backend->claim_interface(dev_handle, interface_number);
+}
+
+static int windows_release_interface(struct libusb_device_handle *dev_handle, int interface_number)
+{
+	struct windows_context_priv *priv = _context_priv(HANDLE_CTX(dev_handle));
+	return priv->backend->release_interface(dev_handle, interface_number);
+}
+
+static int windows_set_interface_altsetting(struct libusb_device_handle *dev_handle,
+	int interface_number, int altsetting)
+{
+	struct windows_context_priv *priv = _context_priv(HANDLE_CTX(dev_handle));
+	return priv->backend->set_interface_altsetting(dev_handle, interface_number, altsetting);
+}
+
+static int windows_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint)
+{
+	struct windows_context_priv *priv = _context_priv(HANDLE_CTX(dev_handle));
+	return priv->backend->clear_halt(dev_handle, endpoint);
+}
+
+static int windows_reset_device(struct libusb_device_handle *dev_handle)
+{
+	struct windows_context_priv *priv = _context_priv(HANDLE_CTX(dev_handle));
+	return priv->backend->reset_device(dev_handle);
+}
+
+static void windows_destroy_device(struct libusb_device *dev)
+{
+	struct windows_context_priv *priv = _context_priv(DEVICE_CTX(dev));
+	priv->backend->destroy_device(dev);
+}
+
+static int windows_submit_transfer(struct usbi_transfer *itransfer)
+{
+	struct windows_context_priv *priv = _context_priv(ITRANSFER_CTX(itransfer));
+	return priv->backend->submit_transfer(itransfer);
+}
+
+static int windows_cancel_transfer(struct usbi_transfer *itransfer)
+{
+	struct windows_context_priv *priv = _context_priv(ITRANSFER_CTX(itransfer));
+	return priv->backend->cancel_transfer(itransfer);
+}
+
+static void windows_clear_transfer_priv(struct usbi_transfer *itransfer)
+{
+	struct windows_context_priv *priv = _context_priv(ITRANSFER_CTX(itransfer));
+	priv->backend->clear_transfer_priv(itransfer);
+}
+
+static int windows_handle_events(struct libusb_context *ctx, struct pollfd *fds, POLL_NFDS_TYPE nfds, int num_ready)
+{
+	struct windows_context_priv *priv = _context_priv(ctx);
+	struct usbi_transfer *itransfer;
 	DWORD io_size, io_result;
+	POLL_NFDS_TYPE i;
+	bool found;
+	int transfer_fd;
 	int r = LIBUSB_SUCCESS;
 
 	usbi_mutex_lock(&ctx->open_devs_lock);
@@ -538,11 +874,12 @@ int windows_handle_events(struct libusb_context *ctx, struct pollfd *fds, POLL_N
 
 		// Because a Windows OVERLAPPED is used for poll emulation,
 		// a pollable fd is created and stored with each transfer
-		usbi_mutex_lock(&ctx->flying_transfers_lock);
 		found = false;
-		list_for_each_entry(transfer, &ctx->flying_transfers, list, struct usbi_transfer) {
-			pollable_fd = windows_get_fd(transfer);
-			if (pollable_fd->fd == fds[i].fd) {
+		transfer_fd = -1;
+		usbi_mutex_lock(&ctx->flying_transfers_lock);
+		list_for_each_entry(itransfer, &ctx->flying_transfers, list, struct usbi_transfer) {
+			transfer_fd = priv->backend->get_transfer_fd(itransfer);
+			if (transfer_fd == fds[i].fd) {
 				found = true;
 				break;
 			}
@@ -550,15 +887,16 @@ int windows_handle_events(struct libusb_context *ctx, struct pollfd *fds, POLL_N
 		usbi_mutex_unlock(&ctx->flying_transfers_lock);
 
 		if (found) {
-			windows_get_overlapped_result(transfer, pollable_fd, &io_result, &io_size);
+			priv->backend->get_overlapped_result(itransfer, &io_result, &io_size);
+
+			usbi_remove_pollfd(ctx, transfer_fd);
 
-			usbi_remove_pollfd(ctx, pollable_fd->fd);
 			// let handle_callback free the event using the transfer wfd
 			// If you don't use the transfer wfd, you run a risk of trying to free a
 			// newly allocated wfd that took the place of the one from the transfer.
-			windows_handle_callback(transfer, io_result, io_size);
+			windows_handle_callback(priv->backend, itransfer, io_result, io_size);
 		} else {
-			usbi_err(ctx, "could not find a matching transfer for fd %d", fds[i]);
+			usbi_err(ctx, "could not find a matching transfer for fd %d", fds[i].fd);
 			r = LIBUSB_ERROR_NOT_FOUND;
 			break;
 		}
@@ -568,24 +906,103 @@ int windows_handle_events(struct libusb_context *ctx, struct pollfd *fds, POLL_N
 	return r;
 }
 
-int windows_common_init(struct libusb_context *ctx)
+static int windows_clock_gettime(int clk_id, struct timespec *tp)
 {
-	if (!windows_init_clock(ctx))
-		goto error_roll_back;
+	struct timer_request request;
+#if !defined(_MSC_VER) || (_MSC_VER < 1900)
+	FILETIME filetime;
+	ULARGE_INTEGER rtime;
+#endif
+	DWORD r;
 
-	if (!htab_create(ctx))
-		goto error_roll_back;
+	switch (clk_id) {
+	case USBI_CLOCK_MONOTONIC:
+		if (timer_thread) {
+			request.tp = tp;
+			request.event = CreateEvent(NULL, FALSE, FALSE, NULL);
+			if (request.event == NULL)
+				return LIBUSB_ERROR_NO_MEM;
 
-	return LIBUSB_SUCCESS;
+			if (!pPostThreadMessageA(timer_thread_id, WM_TIMER_REQUEST, 0, (LPARAM)&request)) {
+				usbi_err(NULL, "PostThreadMessage failed for timer thread: %s", windows_error_str(0));
+				CloseHandle(request.event);
+				return LIBUSB_ERROR_OTHER;
+			}
 
-error_roll_back:
-	windows_common_exit();
-	return LIBUSB_ERROR_NO_MEM;
-}
+			do {
+				r = WaitForSingleObject(request.event, TIMER_REQUEST_RETRY_MS);
+				if (r == WAIT_TIMEOUT)
+					usbi_dbg("could not obtain a timer value within reasonable timeframe - too much load?");
+				else if (r == WAIT_FAILED)
+					usbi_err(NULL, "WaitForSingleObject failed: %s", windows_error_str(0));
+			} while (r == WAIT_TIMEOUT);
+			CloseHandle(request.event);
 
-void windows_common_exit(void)
-{
-	htab_destroy();
-	windows_destroy_clock();
-	windows_exit_dlls();
+			if (r == WAIT_OBJECT_0)
+				return LIBUSB_SUCCESS;
+			else
+				return LIBUSB_ERROR_OTHER;
+		}
+		// Fall through and return real-time if monotonic was not detected @ timer init
+	case USBI_CLOCK_REALTIME:
+#if defined(_MSC_VER) && (_MSC_VER >= 1900)
+		timespec_get(tp, TIME_UTC);
+#else
+		// 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);
+#endif
+		return LIBUSB_SUCCESS;
+	default:
+		return LIBUSB_ERROR_INVALID_PARAM;
+	}
 }
+
+// NB: MSVC6 does not support named initializers.
+const struct usbi_os_backend usbi_backend = {
+	"Windows",
+	USBI_CAP_HAS_HID_ACCESS,
+	windows_init,
+	windows_exit,
+	windows_set_option,
+	windows_get_device_list,
+	NULL,	/* hotplug_poll */
+	windows_open,
+	windows_close,
+	windows_get_device_descriptor,
+	windows_get_active_config_descriptor,
+	windows_get_config_descriptor,
+	windows_get_config_descriptor_by_value,
+	windows_get_configuration,
+	windows_set_configuration,
+	windows_claim_interface,
+	windows_release_interface,
+	windows_set_interface_altsetting,
+	windows_clear_halt,
+	windows_reset_device,
+	NULL,	/* alloc_streams */
+	NULL,	/* free_streams */
+	NULL,	/* dev_mem_alloc */
+	NULL,	/* dev_mem_free */
+	NULL,	/* kernel_driver_active */
+	NULL,	/* detach_kernel_driver */
+	NULL,	/* attach_kernel_driver */
+	windows_destroy_device,
+	windows_submit_transfer,
+	windows_cancel_transfer,
+	windows_clear_transfer_priv,
+	windows_handle_events,
+	NULL,	/* handle_transfer_completion */
+	windows_clock_gettime,
+	sizeof(struct windows_context_priv),
+	sizeof(union windows_device_priv),
+	sizeof(union windows_device_handle_priv),
+	sizeof(union windows_transfer_priv),
+};

+ 110 - 0
vendor/github.com/karalabe/usb/libusb/libusb/os/windows_nt_common.h

@@ -0,0 +1,110 @@
+/*
+ * Windows backend common header for libusb 1.0
+ *
+ * This file brings together header code common between
+ * the desktop Windows backends.
+ * Copyright © 2012-2013 RealVNC Ltd.
+ * Copyright © 2009-2012 Pete Batard <pete@akeo.ie>
+ * 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
+
+#include "windows_nt_shared_types.h"
+
+ /* Windows versions */
+enum windows_version {
+	WINDOWS_UNDEFINED,
+	WINDOWS_2000,
+	WINDOWS_XP,
+	WINDOWS_2003,	// Also XP x64
+	WINDOWS_VISTA,
+	WINDOWS_7,
+	WINDOWS_8,
+	WINDOWS_8_1,
+	WINDOWS_10,
+	WINDOWS_11_OR_LATER
+};
+
+extern enum windows_version windows_version;
+
+/* This call is only available from Vista */
+extern BOOL (WINAPI *pCancelIoEx)(HANDLE, LPOVERLAPPED);
+
+struct windows_backend {
+	int (*init)(struct libusb_context *ctx);
+	void (*exit)(struct libusb_context *ctx);
+	int (*get_device_list)(struct libusb_context *ctx,
+		struct discovered_devs **discdevs);
+	int (*open)(struct libusb_device_handle *dev_handle);
+	void (*close)(struct libusb_device_handle *dev_handle);
+	int (*get_device_descriptor)(struct libusb_device *device, unsigned char *buffer);
+	int (*get_active_config_descriptor)(struct libusb_device *device,
+		unsigned char *buffer, size_t len);
+	int (*get_config_descriptor)(struct libusb_device *device,
+		uint8_t config_index, unsigned char *buffer, size_t len);
+	int (*get_config_descriptor_by_value)(struct libusb_device *device,
+		uint8_t bConfigurationValue, unsigned char **buffer);
+	int (*get_configuration)(struct libusb_device_handle *dev_handle, int *config);
+	int (*set_configuration)(struct libusb_device_handle *dev_handle, int config);
+	int (*claim_interface)(struct libusb_device_handle *dev_handle, int interface_number);
+	int (*release_interface)(struct libusb_device_handle *dev_handle, int interface_number);
+	int (*set_interface_altsetting)(struct libusb_device_handle *dev_handle,
+		int interface_number, int altsetting);
+	int (*clear_halt)(struct libusb_device_handle *dev_handle,
+		unsigned char endpoint);
+	int (*reset_device)(struct libusb_device_handle *dev_handle);
+	void (*destroy_device)(struct libusb_device *dev);
+	int (*submit_transfer)(struct usbi_transfer *itransfer);
+	int (*cancel_transfer)(struct usbi_transfer *itransfer);
+	void (*clear_transfer_priv)(struct usbi_transfer *itransfer);
+	int (*copy_transfer_data)(struct usbi_transfer *itransfer, uint32_t io_size);
+	int (*get_transfer_fd)(struct usbi_transfer *itransfer);
+	void (*get_overlapped_result)(struct usbi_transfer *itransfer,
+		DWORD *io_result, DWORD *io_size);
+};
+
+struct windows_context_priv {
+	const struct windows_backend *backend;
+};
+
+union windows_device_priv {
+	struct usbdk_device_priv usbdk_priv;
+	struct winusb_device_priv winusb_priv;
+};
+
+union windows_device_handle_priv {
+	struct usbdk_device_handle_priv usbdk_priv;
+	struct winusb_device_handle_priv winusb_priv;
+};
+
+union windows_transfer_priv {
+	struct usbdk_transfer_priv usbdk_priv;
+	struct winusb_transfer_priv winusb_priv;
+};
+
+extern const struct windows_backend usbdk_backend;
+extern const struct windows_backend winusb_backend;
+
+unsigned long htab_hash(const char *str);
+void windows_force_sync_completion(OVERLAPPED *overlapped, ULONG size);
+
+#if defined(ENABLE_LOGGING)
+const char *windows_error_str(DWORD error_code);
+#endif

+ 138 - 0
vendor/github.com/karalabe/usb/libusb/libusb/os/windows_nt_shared_types.h

@@ -0,0 +1,138 @@
+#pragma once
+
+#include "windows_common.h"
+
+#include <pshpack1.h>
+
+typedef struct USB_DEVICE_DESCRIPTOR {
+	UCHAR  bLength;
+	UCHAR  bDescriptorType;
+	USHORT bcdUSB;
+	UCHAR  bDeviceClass;
+	UCHAR  bDeviceSubClass;
+	UCHAR  bDeviceProtocol;
+	UCHAR  bMaxPacketSize0;
+	USHORT idVendor;
+	USHORT idProduct;
+	USHORT bcdDevice;
+	UCHAR  iManufacturer;
+	UCHAR  iProduct;
+	UCHAR  iSerialNumber;
+	UCHAR  bNumConfigurations;
+} USB_DEVICE_DESCRIPTOR, *PUSB_DEVICE_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;
+
+#include <poppack.h>
+
+#define MAX_DEVICE_ID_LEN	200
+
+typedef struct USB_DK_DEVICE_ID {
+	WCHAR DeviceID[MAX_DEVICE_ID_LEN];
+	WCHAR InstanceID[MAX_DEVICE_ID_LEN];
+} USB_DK_DEVICE_ID, *PUSB_DK_DEVICE_ID;
+
+typedef struct USB_DK_DEVICE_INFO {
+	USB_DK_DEVICE_ID ID;
+	ULONG64 FilterID;
+	ULONG64 Port;
+	ULONG64 Speed;
+	USB_DEVICE_DESCRIPTOR DeviceDescriptor;
+} USB_DK_DEVICE_INFO, *PUSB_DK_DEVICE_INFO;
+
+typedef struct USB_DK_ISO_TRANSFER_RESULT {
+	ULONG64 ActualLength;
+	ULONG64 TransferResult;
+} USB_DK_ISO_TRANSFER_RESULT, *PUSB_DK_ISO_TRANSFER_RESULT;
+
+typedef struct USB_DK_GEN_TRANSFER_RESULT {
+	ULONG64 BytesTransferred;
+	ULONG64 UsbdStatus; // USBD_STATUS code
+} USB_DK_GEN_TRANSFER_RESULT, *PUSB_DK_GEN_TRANSFER_RESULT;
+
+typedef struct USB_DK_TRANSFER_RESULT {
+	USB_DK_GEN_TRANSFER_RESULT GenResult;
+	PVOID64 IsochronousResultsArray; // array of USB_DK_ISO_TRANSFER_RESULT
+} USB_DK_TRANSFER_RESULT, *PUSB_DK_TRANSFER_RESULT;
+
+typedef struct USB_DK_TRANSFER_REQUEST {
+	ULONG64 EndpointAddress;
+	PVOID64 Buffer;
+	ULONG64 BufferLength;
+	ULONG64 TransferType;
+	ULONG64 IsochronousPacketsArraySize;
+	PVOID64 IsochronousPacketsArray;
+	USB_DK_TRANSFER_RESULT Result;
+} USB_DK_TRANSFER_REQUEST, *PUSB_DK_TRANSFER_REQUEST;
+
+struct usbdk_device_priv {
+	USB_DK_DEVICE_INFO info;
+	PUSB_CONFIGURATION_DESCRIPTOR *config_descriptors;
+	HANDLE redirector_handle;
+	HANDLE system_handle;
+	uint8_t active_configuration;
+};
+
+struct winusb_device_priv {
+	bool initialized;
+	bool root_hub;
+	uint8_t active_config;
+	uint8_t depth; // distance to HCD
+	const struct windows_usb_api_backend *apib;
+	char *dev_id;
+	char *path;  // device interface path
+	int sub_api; // for WinUSB-like APIs
+	struct {
+		char *path; // each interface needs a device interface path,
+		const struct windows_usb_api_backend *apib; // an API backend (multiple drivers support),
+		int sub_api;
+		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];
+	struct hid_device_priv *hid;
+	USB_DEVICE_DESCRIPTOR dev_descriptor;
+	PUSB_CONFIGURATION_DESCRIPTOR *config_descriptor; // list of pointers to the cached config descriptors
+};
+
+struct usbdk_device_handle_priv {
+	// Not currently used
+	char dummy;
+};
+
+struct winusb_device_handle_priv {
+	int active_interface;
+	struct {
+		HANDLE dev_handle; // WinUSB needs an extra handle for the file
+		HANDLE api_handle; // used by the API to communicate with the device
+	} interface_handle[USB_MAXINTERFACES];
+	int autoclaim_count[USB_MAXINTERFACES]; // For auto-release
+};
+
+struct usbdk_transfer_priv {
+	USB_DK_TRANSFER_REQUEST request;
+	struct winfd pollable_fd;
+	HANDLE system_handle;
+	PULONG64 IsochronousPacketsArray;
+	PUSB_DK_ISO_TRANSFER_RESULT IsochronousResultsArray;
+};
+
+struct winusb_transfer_priv {
+	struct winfd pollable_fd;
+	HANDLE handle;
+	uint8_t interface_number;
+	uint8_t *hid_buffer; // 1 byte extended data buffer, required for HID
+	uint8_t *hid_dest;   // transfer buffer destination, required for HID
+	size_t hid_expected_size;
+	void *iso_context;
+};

+ 171 - 246
vendor/github.com/karalabe/hid/libusb/libusb/os/windows_usbdk.c → vendor/github.com/karalabe/usb/libusb/libusb/os/windows_usbdk.c

@@ -23,22 +23,12 @@
 
 #include <config.h>
 
-#if defined(USE_USBDK)
-
 #include <windows.h>
-#include <cfgmgr32.h>
 #include <stdio.h>
 
 #include "libusbi.h"
 #include "windows_common.h"
 #include "windows_nt_common.h"
-
-#define ULONG64 uint64_t
-#define PVOID64 uint64_t
-
-typedef CONST WCHAR *PCWCHAR;
-#define wcsncpy_s wcsncpy
-
 #include "windows_usbdk.h"
 
 #if !defined(STATUS_SUCCESS)
@@ -55,7 +45,7 @@ typedef LONG NTSTATUS;
 #endif
 
 #if !defined(USBD_SUCCESS)
-typedef int32_t USBD_STATUS;
+typedef LONG USBD_STATUS;
 #define USBD_SUCCESS(Status)		((USBD_STATUS) (Status) >= 0)
 #define USBD_PENDING(Status)		((ULONG) (Status) >> 30 == 1)
 #define USBD_ERROR(Status)		((USBD_STATUS) (Status) < 0)
@@ -66,22 +56,6 @@ typedef int32_t USBD_STATUS;
 #define USBD_STATUS_CANCELED		((USBD_STATUS) 0xc0010000)
 #endif
 
-static int concurrent_usage = -1;
-
-struct usbdk_device_priv {
-	USB_DK_DEVICE_INFO info;
-	PUSB_CONFIGURATION_DESCRIPTOR *config_descriptors;
-	HANDLE redirector_handle;
-	uint8_t active_configuration;
-};
-
-struct usbdk_transfer_priv {
-	USB_DK_TRANSFER_REQUEST request;
-	struct winfd pollable_fd;
-	PULONG64 IsochronousPacketsArray;
-	PUSB_DK_ISO_TRANSFER_RESULT IsochronousResultsArray;
-};
-
 static inline struct usbdk_device_priv *_usbdk_device_priv(struct libusb_device *dev)
 {
 	return (struct usbdk_device_priv *)dev->os_priv;
@@ -115,7 +89,7 @@ static FARPROC get_usbdk_proc_addr(struct libusb_context *ctx, LPCSTR api_name)
 	FARPROC api_ptr = GetProcAddress(usbdk_helper.module, api_name);
 
 	if (api_ptr == NULL)
-		usbi_err(ctx, "UsbDkHelper API %s not found, error %d", api_name, GetLastError());
+		usbi_err(ctx, "UsbDkHelper API %s not found: %s", api_name, windows_error_str(0));
 
 	return api_ptr;
 }
@@ -132,7 +106,7 @@ static int load_usbdk_helper_dll(struct libusb_context *ctx)
 {
 	usbdk_helper.module = LoadLibraryA("UsbDkHelper");
 	if (usbdk_helper.module == NULL) {
-		usbi_err(ctx, "Failed to load UsbDkHelper.dll, error %d", GetLastError());
+		usbi_err(ctx, "Failed to load UsbDkHelper.dll: %s", windows_error_str(0));
 		return LIBUSB_ERROR_NOT_FOUND;
 	}
 
@@ -198,41 +172,41 @@ error_unload:
 
 static int usbdk_init(struct libusb_context *ctx)
 {
-	int r;
+	SC_HANDLE managerHandle;
+	SC_HANDLE serviceHandle;
 
-	if (++concurrent_usage == 0) { // First init?
-		r = load_usbdk_helper_dll(ctx);
-		if (r)
-			goto init_exit;
+	managerHandle = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
+	if (managerHandle == NULL) {
+		usbi_warn(ctx, "failed to open service control manager: %s", windows_error_str(0));
+		return LIBUSB_ERROR_OTHER;
+	}
 
-		init_polling();
+	serviceHandle = OpenServiceA(managerHandle, "UsbDk", GENERIC_READ);
+	CloseServiceHandle(managerHandle);
 
-		r = windows_common_init(ctx);
-		if (r)
-			goto init_exit;
-	}
-	// At this stage, either we went through full init successfully, or didn't need to
-	r = LIBUSB_SUCCESS;
-
-init_exit:
-	if (!concurrent_usage && r != LIBUSB_SUCCESS) { // First init failed?
-		exit_polling();
-		windows_common_exit();
-		unload_usbdk_helper_dll();
+	if (serviceHandle == NULL) {
+		if (GetLastError() != ERROR_SERVICE_DOES_NOT_EXIST)
+			usbi_warn(ctx, "failed to open UsbDk service: %s", windows_error_str(0));
+		return LIBUSB_ERROR_NOT_FOUND;
 	}
 
-	if (r != LIBUSB_SUCCESS)
-		--concurrent_usage; // Not expected to call libusb_exit if we failed.
+	CloseServiceHandle(serviceHandle);
 
-	return r;
+	return load_usbdk_helper_dll(ctx);
+}
+
+static void usbdk_exit(struct libusb_context *ctx)
+{
+	UNUSED(ctx);
+	unload_usbdk_helper_dll();
 }
 
 static int usbdk_get_session_id_for_device(struct libusb_context *ctx,
 	PUSB_DK_DEVICE_ID id, unsigned long *session_id)
 {
-	char dev_identity[ARRAYSIZE(id->DeviceID) + ARRAYSIZE(id->InstanceID)];
+	char dev_identity[ARRAYSIZE(id->DeviceID) + ARRAYSIZE(id->InstanceID) + 1];
 
-	if (sprintf(dev_identity, "%S%S", id->DeviceID, id->InstanceID) == -1) {
+	if (snprintf(dev_identity, sizeof(dev_identity), "%S%S", id->DeviceID, id->InstanceID) == -1) {
 		usbi_warn(ctx, "cannot form device identity", id->DeviceID);
 		return LIBUSB_ERROR_NOT_SUPPORTED;
 	}
@@ -296,11 +270,11 @@ static void usbdk_device_init(libusb_device *dev, PUSB_DK_DEVICE_INFO info)
 	dev->port_number = (uint8_t)info->Port;
 	dev->parent_dev = NULL;
 
-	//Addresses in libusb are 1-based
+	// Addresses in libusb are 1-based
 	dev->device_address = (uint8_t)(info->Port + 1);
 
 	dev->num_configurations = info->DeviceDescriptor.bNumConfigurations;
-	dev->device_descriptor = info->DeviceDescriptor;
+	memcpy(&dev->device_descriptor, &info->DeviceDescriptor, LIBUSB_DT_DEVICE_SIZE);
 
 	switch (info->Speed) {
 	case LowSpeed:
@@ -330,7 +304,7 @@ static int usbdk_get_device_list(struct libusb_context *ctx, struct discovered_d
 	ULONG dev_number;
 	PUSB_DK_DEVICE_INFO devices;
 
-	if(!usbdk_helper.GetDevicesList(&devices, &dev_number))
+	if (!usbdk_helper.GetDevicesList(&devices, &dev_number))
 		return LIBUSB_ERROR_OTHER;
 
 	for (i = 0; i < dev_number; i++) {
@@ -371,26 +345,16 @@ func_exit:
 	return r;
 }
 
-static void usbdk_exit(void)
-{
-	if (--concurrent_usage < 0) {
-		windows_common_exit();
-		exit_polling();
-		unload_usbdk_helper_dll();
-	}
-}
-
-static int usbdk_get_device_descriptor(struct libusb_device *dev, unsigned char *buffer, int *host_endian)
+static int usbdk_get_device_descriptor(struct libusb_device *dev, unsigned char *buffer)
 {
 	struct usbdk_device_priv *priv = _usbdk_device_priv(dev);
 
 	memcpy(buffer, &priv->info.DeviceDescriptor, DEVICE_DESC_LENGTH);
-	*host_endian = 0;
 
 	return LIBUSB_SUCCESS;
 }
 
-static int usbdk_get_config_descriptor(struct libusb_device *dev, uint8_t config_index, unsigned char *buffer, size_t len, int *host_endian)
+static int usbdk_get_config_descriptor(struct libusb_device *dev, uint8_t config_index, unsigned char *buffer, size_t len)
 {
 	struct usbdk_device_priv *priv = _usbdk_device_priv(dev);
 	PUSB_CONFIGURATION_DESCRIPTOR config_header;
@@ -403,15 +367,31 @@ static int usbdk_get_config_descriptor(struct libusb_device *dev, uint8_t config
 
 	size = min(config_header->wTotalLength, len);
 	memcpy(buffer, config_header, size);
-	*host_endian = 0;
-
 	return (int)size;
 }
 
-static inline int usbdk_get_active_config_descriptor(struct libusb_device *dev, unsigned char *buffer, size_t len, int *host_endian)
+static int usbdk_get_config_descriptor_by_value(struct libusb_device *dev, uint8_t bConfigurationValue,
+	unsigned char **buffer)
+{
+	struct usbdk_device_priv *priv = _usbdk_device_priv(dev);
+	PUSB_CONFIGURATION_DESCRIPTOR config_header;
+	uint8_t index;
+
+	for (index = 0; index < dev->num_configurations; index++) {
+		config_header = priv->config_descriptors[index];
+		if (config_header->bConfigurationValue == bConfigurationValue) {
+			*buffer = (unsigned char *)priv->config_descriptors[index];
+			return (int)config_header->wTotalLength;
+		}
+	}
+
+	return LIBUSB_ERROR_NOT_FOUND;
+}
+
+static int usbdk_get_active_config_descriptor(struct libusb_device *dev, unsigned char *buffer, size_t len)
 {
 	return usbdk_get_config_descriptor(dev, _usbdk_device_priv(dev)->active_configuration,
-			buffer, len, host_endian);
+			buffer, len);
 }
 
 static int usbdk_open(struct libusb_device_handle *dev_handle)
@@ -424,6 +404,8 @@ static int usbdk_open(struct libusb_device_handle *dev_handle)
 		return LIBUSB_ERROR_OTHER;
 	}
 
+	priv->system_handle = usbdk_helper.GetRedirectorSystemHandle(priv->redirector_handle);
+
 	return LIBUSB_SUCCESS;
 }
 
@@ -431,10 +413,8 @@ static void usbdk_close(struct libusb_device_handle *dev_handle)
 {
 	struct usbdk_device_priv *priv = _usbdk_device_priv(dev_handle->dev);
 
-	if (!usbdk_helper.StopRedirect(priv->redirector_handle)) {
-		struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev);
-		usbi_err(ctx, "Redirector shutdown failed");
-	}
+	if (!usbdk_helper.StopRedirect(priv->redirector_handle))
+		usbi_err(HANDLE_CTX(dev_handle), "Redirector shutdown failed");
 }
 
 static int usbdk_get_configuration(struct libusb_device_handle *dev_handle, int *config)
@@ -460,7 +440,7 @@ static int usbdk_claim_interface(struct libusb_device_handle *dev_handle, int if
 
 static int usbdk_set_interface_altsetting(struct libusb_device_handle *dev_handle, int iface, int altsetting)
 {
-	struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev);
+	struct libusb_context *ctx = HANDLE_CTX(dev_handle);
 	struct usbdk_device_priv *priv = _usbdk_device_priv(dev_handle->dev);
 
 	if (!usbdk_helper.SetAltsetting(priv->redirector_handle, iface, altsetting)) {
@@ -480,7 +460,7 @@ static int usbdk_release_interface(struct libusb_device_handle *dev_handle, int
 
 static int usbdk_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint)
 {
-	struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev);
+	struct libusb_context *ctx = HANDLE_CTX(dev_handle);
 	struct usbdk_device_priv *priv = _usbdk_device_priv(dev_handle->dev);
 
 	if (!usbdk_helper.ResetPipe(priv->redirector_handle, endpoint)) {
@@ -493,7 +473,7 @@ static int usbdk_clear_halt(struct libusb_device_handle *dev_handle, unsigned ch
 
 static int usbdk_reset_device(struct libusb_device_handle *dev_handle)
 {
-	struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev);
+	struct libusb_context *ctx = HANDLE_CTX(dev_handle);
 	struct usbdk_device_priv *priv = _usbdk_device_priv(dev_handle->dev);
 
 	if (!usbdk_helper.ResetDevice(priv->redirector_handle)) {
@@ -504,27 +484,6 @@ static int usbdk_reset_device(struct libusb_device_handle *dev_handle)
 	return LIBUSB_SUCCESS;
 }
 
-static int usbdk_kernel_driver_active(struct libusb_device_handle *dev_handle, int iface)
-{
-	UNUSED(dev_handle);
-	UNUSED(iface);
-	return LIBUSB_ERROR_NOT_SUPPORTED;
-}
-
-static int usbdk_attach_kernel_driver(struct libusb_device_handle *dev_handle, int iface)
-{
-	UNUSED(dev_handle);
-	UNUSED(iface);
-	return LIBUSB_ERROR_NOT_SUPPORTED;
-}
-
-static int usbdk_detach_kernel_driver(struct libusb_device_handle *dev_handle, int iface)
-{
-	UNUSED(dev_handle);
-	UNUSED(iface);
-	return LIBUSB_ERROR_NOT_SUPPORTED;
-}
-
 static void usbdk_destroy_device(struct libusb_device *dev)
 {
 	struct usbdk_device_priv* p = _usbdk_device_priv(dev);
@@ -533,12 +492,14 @@ static void usbdk_destroy_device(struct libusb_device *dev)
 		usbdk_release_config_descriptors(p, p->info.DeviceDescriptor.bNumConfigurations);
 }
 
-void windows_clear_transfer_priv(struct usbi_transfer *itransfer)
+static void usbdk_clear_transfer_priv(struct usbi_transfer *itransfer)
 {
 	struct usbdk_transfer_priv *transfer_priv = _usbdk_transfer_priv(itransfer);
 	struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
 
-	usbi_free_fd(&transfer_priv->pollable_fd);
+	usbi_close(transfer_priv->pollable_fd.fd);
+	transfer_priv->pollable_fd = INVALID_WINFD;
+	transfer_priv->system_handle = NULL;
 
 	if (transfer->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS) {
 		safe_free(transfer_priv->IsochronousPacketsArray);
@@ -551,47 +512,30 @@ static int usbdk_do_control_transfer(struct usbi_transfer *itransfer)
 	struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
 	struct usbdk_device_priv *priv = _usbdk_device_priv(transfer->dev_handle->dev);
 	struct usbdk_transfer_priv *transfer_priv = _usbdk_transfer_priv(itransfer);
-	struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev);
-	struct winfd wfd;
-	ULONG Length;
+	struct libusb_context *ctx = TRANSFER_CTX(transfer);
+	OVERLAPPED *overlapped = transfer_priv->pollable_fd.overlapped;
 	TransferResult transResult;
-	HANDLE sysHandle;
-
-	sysHandle = usbdk_helper.GetRedirectorSystemHandle(priv->redirector_handle);
-
-	wfd = usbi_create_fd(sysHandle, RW_READ, NULL, NULL);
-	// Always use the handle returned from usbi_create_fd (wfd.handle)
-	if (wfd.fd < 0)
-		return LIBUSB_ERROR_NO_MEM;
 
-	transfer_priv->request.Buffer = (PVOID64)(uintptr_t)transfer->buffer;
+	transfer_priv->request.Buffer = (PVOID64)transfer->buffer;
 	transfer_priv->request.BufferLength = transfer->length;
 	transfer_priv->request.TransferType = ControlTransferType;
-	transfer_priv->pollable_fd = INVALID_WINFD;
-	Length = (ULONG)transfer->length;
 
-	if (IS_XFERIN(transfer))
-		transResult = usbdk_helper.ReadPipe(priv->redirector_handle, &transfer_priv->request, wfd.overlapped);
+	if (transfer->buffer[0] & LIBUSB_ENDPOINT_IN)
+		transResult = usbdk_helper.ReadPipe(priv->redirector_handle, &transfer_priv->request, overlapped);
 	else
-		transResult = usbdk_helper.WritePipe(priv->redirector_handle, &transfer_priv->request, wfd.overlapped);
+		transResult = usbdk_helper.WritePipe(priv->redirector_handle, &transfer_priv->request, overlapped);
 
 	switch (transResult) {
 	case TransferSuccess:
-		wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY;
-		wfd.overlapped->InternalHigh = (DWORD)Length;
+		windows_force_sync_completion(overlapped, (ULONG)transfer_priv->request.Result.GenResult.BytesTransferred);
 		break;
 	case TransferSuccessAsync:
 		break;
 	case TransferFailure:
 		usbi_err(ctx, "ControlTransfer failed: %s", windows_error_str(0));
-		usbi_free_fd(&wfd);
 		return LIBUSB_ERROR_IO;
 	}
 
-	// Use priv_transfer to store data needed for async polling
-	transfer_priv->pollable_fd = wfd;
-	usbi_add_pollfd(ctx, transfer_priv->pollable_fd.fd, POLLIN);
-
 	return LIBUSB_SUCCESS;
 }
 
@@ -600,12 +544,11 @@ static int usbdk_do_bulk_transfer(struct usbi_transfer *itransfer)
 	struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
 	struct usbdk_device_priv *priv = _usbdk_device_priv(transfer->dev_handle->dev);
 	struct usbdk_transfer_priv *transfer_priv = _usbdk_transfer_priv(itransfer);
-	struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev);
-	struct winfd wfd;
+	struct libusb_context *ctx = TRANSFER_CTX(transfer);
+	OVERLAPPED *overlapped = transfer_priv->pollable_fd.overlapped;
 	TransferResult transferRes;
-	HANDLE sysHandle;
 
-	transfer_priv->request.Buffer = (PVOID64)(uintptr_t)transfer->buffer;
+	transfer_priv->request.Buffer = (PVOID64)transfer->buffer;
 	transfer_priv->request.BufferLength = transfer->length;
 	transfer_priv->request.EndpointAddress = transfer->endpoint;
 
@@ -614,42 +557,29 @@ static int usbdk_do_bulk_transfer(struct usbi_transfer *itransfer)
 		transfer_priv->request.TransferType = BulkTransferType;
 		break;
 	case LIBUSB_TRANSFER_TYPE_INTERRUPT:
-		transfer_priv->request.TransferType = IntertuptTransferType;
+		transfer_priv->request.TransferType = InterruptTransferType;
 		break;
 	default:
-		usbi_err(ctx, "Wrong transfer type (%d) in usbdk_do_bulk_transfer. %s", transfer->type, windows_error_str(0));
+		usbi_err(ctx, "Wrong transfer type (%d) in usbdk_do_bulk_transfer", transfer->type);
 		return LIBUSB_ERROR_INVALID_PARAM;
 	}
 
-	transfer_priv->pollable_fd = INVALID_WINFD;
-
-	sysHandle = usbdk_helper.GetRedirectorSystemHandle(priv->redirector_handle);
-
-	wfd = usbi_create_fd(sysHandle, IS_XFERIN(transfer) ? RW_READ : RW_WRITE, NULL, NULL);
-	// Always use the handle returned from usbi_create_fd (wfd.handle)
-	if (wfd.fd < 0)
-		return LIBUSB_ERROR_NO_MEM;
-
 	if (IS_XFERIN(transfer))
-		transferRes = usbdk_helper.ReadPipe(priv->redirector_handle, &transfer_priv->request, wfd.overlapped);
+		transferRes = usbdk_helper.ReadPipe(priv->redirector_handle, &transfer_priv->request, overlapped);
 	else
-		transferRes = usbdk_helper.WritePipe(priv->redirector_handle, &transfer_priv->request, wfd.overlapped);
+		transferRes = usbdk_helper.WritePipe(priv->redirector_handle, &transfer_priv->request, overlapped);
 
 	switch (transferRes) {
 	case TransferSuccess:
-		wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY;
+		windows_force_sync_completion(overlapped, (ULONG)transfer_priv->request.Result.GenResult.BytesTransferred);
 		break;
 	case TransferSuccessAsync:
 		break;
 	case TransferFailure:
 		usbi_err(ctx, "ReadPipe/WritePipe failed: %s", windows_error_str(0));
-		usbi_free_fd(&wfd);
 		return LIBUSB_ERROR_IO;
 	}
 
-	transfer_priv->pollable_fd = wfd;
-	usbi_add_pollfd(ctx, transfer_priv->pollable_fd.fd, IS_XFERIN(transfer) ? POLLIN : POLLOUT);
-
 	return LIBUSB_SUCCESS;
 }
 
@@ -658,68 +588,81 @@ static int usbdk_do_iso_transfer(struct usbi_transfer *itransfer)
 	struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
 	struct usbdk_device_priv *priv = _usbdk_device_priv(transfer->dev_handle->dev);
 	struct usbdk_transfer_priv *transfer_priv = _usbdk_transfer_priv(itransfer);
-	struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev);
-	struct winfd wfd;
+	struct libusb_context *ctx = TRANSFER_CTX(transfer);
+	OVERLAPPED *overlapped = transfer_priv->pollable_fd.overlapped;
 	TransferResult transferRes;
 	int i;
-	HANDLE sysHandle;
 
-	transfer_priv->request.Buffer = (PVOID64)(uintptr_t)transfer->buffer;
+	transfer_priv->request.Buffer = (PVOID64)transfer->buffer;
 	transfer_priv->request.BufferLength = transfer->length;
 	transfer_priv->request.EndpointAddress = transfer->endpoint;
 	transfer_priv->request.TransferType = IsochronousTransferType;
 	transfer_priv->request.IsochronousPacketsArraySize = transfer->num_iso_packets;
 	transfer_priv->IsochronousPacketsArray = malloc(transfer->num_iso_packets * sizeof(ULONG64));
-	transfer_priv->request.IsochronousPacketsArray = (PVOID64)(uintptr_t)transfer_priv->IsochronousPacketsArray;
+	transfer_priv->request.IsochronousPacketsArray = (PVOID64)transfer_priv->IsochronousPacketsArray;
 	if (!transfer_priv->IsochronousPacketsArray) {
-		usbi_err(ctx, "Allocation of IsochronousPacketsArray is failed, %s", windows_error_str(0));
-		return LIBUSB_ERROR_IO;
+		usbi_err(ctx, "Allocation of IsochronousPacketsArray failed");
+		return LIBUSB_ERROR_NO_MEM;
 	}
 
 	transfer_priv->IsochronousResultsArray = malloc(transfer->num_iso_packets * sizeof(USB_DK_ISO_TRANSFER_RESULT));
-	transfer_priv->request.Result.IsochronousResultsArray = (PVOID64)(uintptr_t)transfer_priv->IsochronousResultsArray;
+	transfer_priv->request.Result.IsochronousResultsArray = (PVOID64)transfer_priv->IsochronousResultsArray;
 	if (!transfer_priv->IsochronousResultsArray) {
-		usbi_err(ctx, "Allocation of isochronousResultsArray is failed, %s", windows_error_str(0));
-		free(transfer_priv->IsochronousPacketsArray);
-		return LIBUSB_ERROR_IO;
+		usbi_err(ctx, "Allocation of isochronousResultsArray failed");
+		return LIBUSB_ERROR_NO_MEM;
 	}
 
 	for (i = 0; i < transfer->num_iso_packets; i++)
 		transfer_priv->IsochronousPacketsArray[i] = transfer->iso_packet_desc[i].length;
 
-	transfer_priv->pollable_fd = INVALID_WINFD;
-
-	sysHandle = usbdk_helper.GetRedirectorSystemHandle(priv->redirector_handle);
-
-	wfd = usbi_create_fd(sysHandle, IS_XFERIN(transfer) ? RW_READ : RW_WRITE, NULL, NULL);
-	// Always use the handle returned from usbi_create_fd (wfd.handle)
-	if (wfd.fd < 0) {
-		free(transfer_priv->IsochronousPacketsArray);
-		free(transfer_priv->IsochronousResultsArray);
-		return LIBUSB_ERROR_NO_MEM;
-	}
-
 	if (IS_XFERIN(transfer))
-		transferRes = usbdk_helper.ReadPipe(priv->redirector_handle, &transfer_priv->request, wfd.overlapped);
+		transferRes = usbdk_helper.ReadPipe(priv->redirector_handle, &transfer_priv->request, overlapped);
 	else
-		transferRes = usbdk_helper.WritePipe(priv->redirector_handle, &transfer_priv->request, wfd.overlapped);
+		transferRes = usbdk_helper.WritePipe(priv->redirector_handle, &transfer_priv->request, overlapped);
 
 	switch (transferRes) {
 	case TransferSuccess:
-		wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY;
+		windows_force_sync_completion(overlapped, (ULONG)transfer_priv->request.Result.GenResult.BytesTransferred);
 		break;
 	case TransferSuccessAsync:
 		break;
 	case TransferFailure:
-		usbi_err(ctx, "ReadPipe/WritePipe failed: %s", windows_error_str(0));
-		usbi_free_fd(&wfd);
-		free(transfer_priv->IsochronousPacketsArray);
-		free(transfer_priv->IsochronousResultsArray);
 		return LIBUSB_ERROR_IO;
 	}
 
+	return LIBUSB_SUCCESS;
+}
+
+static int usbdk_do_submit_transfer(struct usbi_transfer *itransfer,
+	short events, int (*transfer_fn)(struct usbi_transfer *))
+{
+	struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+	struct libusb_context *ctx = TRANSFER_CTX(transfer);
+	struct usbdk_device_priv *priv = _usbdk_device_priv(transfer->dev_handle->dev);
+	struct usbdk_transfer_priv *transfer_priv = _usbdk_transfer_priv(itransfer);
+	struct winfd wfd;
+	int r;
+
+	wfd = usbi_create_fd();
+	if (wfd.fd < 0)
+		return LIBUSB_ERROR_NO_MEM;
+
+	r = usbi_add_pollfd(ctx, wfd.fd, events);
+	if (r) {
+		usbi_close(wfd.fd);
+		return r;
+	}
+
+	// Use transfer_priv to store data needed for async polling
 	transfer_priv->pollable_fd = wfd;
-	usbi_add_pollfd(ctx, transfer_priv->pollable_fd.fd, IS_XFERIN(transfer) ? POLLIN : POLLOUT);
+	transfer_priv->system_handle = priv->system_handle;
+
+	r = transfer_fn(itransfer);
+	if (r != LIBUSB_SUCCESS) {
+		usbi_remove_pollfd(ctx, wfd.fd);
+		usbdk_clear_transfer_priv(itransfer);
+		return r;
+	}
 
 	return LIBUSB_SUCCESS;
 }
@@ -727,33 +670,52 @@ static int usbdk_do_iso_transfer(struct usbi_transfer *itransfer)
 static int usbdk_submit_transfer(struct usbi_transfer *itransfer)
 {
 	struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+	int (*transfer_fn)(struct usbi_transfer *);
+	short events;
 
 	switch (transfer->type) {
 	case LIBUSB_TRANSFER_TYPE_CONTROL:
-		return usbdk_do_control_transfer(itransfer);
+		events = (transfer->buffer[0] & LIBUSB_ENDPOINT_IN) ? POLLIN : POLLOUT;
+		transfer_fn = usbdk_do_control_transfer;
+		break;
 	case LIBUSB_TRANSFER_TYPE_BULK:
 	case LIBUSB_TRANSFER_TYPE_INTERRUPT:
 		if (IS_XFEROUT(transfer) && (transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET))
 			return LIBUSB_ERROR_NOT_SUPPORTED; //TODO: Check whether we can support this in UsbDk
-		else
-			return usbdk_do_bulk_transfer(itransfer);
+		events = IS_XFERIN(transfer) ? POLLIN : POLLOUT;
+		transfer_fn = usbdk_do_bulk_transfer;
+		break;
 	case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
-		return usbdk_do_iso_transfer(itransfer);
+		events = IS_XFERIN(transfer) ? POLLIN : POLLOUT;
+		transfer_fn = usbdk_do_iso_transfer;
+		break;
 	default:
 		usbi_err(TRANSFER_CTX(transfer), "unknown endpoint type %d", transfer->type);
 		return LIBUSB_ERROR_INVALID_PARAM;
 	}
+
+	return usbdk_do_submit_transfer(itransfer, events, transfer_fn);
 }
 
 static int usbdk_abort_transfers(struct usbi_transfer *itransfer)
 {
 	struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
-	struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev);
+	struct libusb_context *ctx = TRANSFER_CTX(transfer);
 	struct usbdk_device_priv *priv = _usbdk_device_priv(transfer->dev_handle->dev);
+	struct usbdk_transfer_priv *transfer_priv = _usbdk_transfer_priv(itransfer);
+	struct winfd *pollable_fd = &transfer_priv->pollable_fd;
 
-	if (!usbdk_helper.AbortPipe(priv->redirector_handle, transfer->endpoint)) {
-		usbi_err(ctx, "AbortPipe failed: %s", windows_error_str(0));
-		return LIBUSB_ERROR_NO_DEVICE;
+	if (pCancelIoEx != NULL) {
+		// Use CancelIoEx if available to cancel just a single transfer
+		if (!pCancelIoEx(priv->system_handle, pollable_fd->overlapped)) {
+			usbi_err(ctx, "CancelIoEx failed: %s", windows_error_str(0));
+			return LIBUSB_ERROR_NO_DEVICE;
+		}
+	} else {
+		if (!usbdk_helper.AbortPipe(priv->redirector_handle, transfer->endpoint)) {
+			usbi_err(ctx, "AbortPipe failed: %s", windows_error_str(0));
+			return LIBUSB_ERROR_NO_DEVICE;
+		}
 	}
 
 	return LIBUSB_SUCCESS;
@@ -778,16 +740,16 @@ static int usbdk_cancel_transfer(struct usbi_transfer *itransfer)
 	}
 }
 
-int windows_copy_transfer_data(struct usbi_transfer *itransfer, uint32_t io_size)
+static int usbdk_copy_transfer_data(struct usbi_transfer *itransfer, uint32_t io_size)
 {
 	itransfer->transferred += io_size;
 	return LIBUSB_TRANSFER_COMPLETED;
 }
 
-struct winfd *windows_get_fd(struct usbi_transfer *transfer)
+static int usbdk_get_transfer_fd(struct usbi_transfer *itransfer)
 {
-	struct usbdk_transfer_priv *transfer_priv = _usbdk_transfer_priv(transfer);
-	return &transfer_priv->pollable_fd;
+	struct usbdk_transfer_priv *transfer_priv = _usbdk_transfer_priv(itransfer);
+	return transfer_priv->pollable_fd.fd;
 }
 
 static DWORD usbdk_translate_usbd_status(USBD_STATUS UsbdStatus)
@@ -796,30 +758,28 @@ static DWORD usbdk_translate_usbd_status(USBD_STATUS UsbdStatus)
 		return NO_ERROR;
 
 	switch (UsbdStatus) {
-	case USBD_STATUS_STALL_PID:
-	case USBD_STATUS_ENDPOINT_HALTED:
-	case USBD_STATUS_BAD_START_FRAME:
-		return ERROR_GEN_FAILURE;
 	case USBD_STATUS_TIMEOUT:
 		return ERROR_SEM_TIMEOUT;
 	case USBD_STATUS_CANCELED:
 		return ERROR_OPERATION_ABORTED;
 	default:
-		return ERROR_FUNCTION_FAILED;
+		return ERROR_GEN_FAILURE;
 	}
 }
 
-void windows_get_overlapped_result(struct usbi_transfer *transfer, struct winfd *pollable_fd, DWORD *io_result, DWORD *io_size)
+static void usbdk_get_overlapped_result(struct usbi_transfer *itransfer, DWORD *io_result, DWORD *io_size)
 {
+	struct usbdk_transfer_priv *transfer_priv = _usbdk_transfer_priv(itransfer);
+	struct winfd *pollable_fd = &transfer_priv->pollable_fd;
+
 	if (HasOverlappedIoCompletedSync(pollable_fd->overlapped) // Handle async requests that completed synchronously first
-			|| GetOverlappedResult(pollable_fd->handle, pollable_fd->overlapped, io_size, false)) { // Regular async overlapped
-		struct libusb_transfer *ltransfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer);
-		struct usbdk_transfer_priv *transfer_priv = _usbdk_transfer_priv(transfer);
+			|| GetOverlappedResult(transfer_priv->system_handle, pollable_fd->overlapped, io_size, FALSE)) { // Regular async overlapped
+		struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
 
-		if (ltransfer->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS) {
-			int i;
+		if (transfer->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS) {
+			ULONG64 i;
 			for (i = 0; i < transfer_priv->request.IsochronousPacketsArraySize; i++) {
-				struct libusb_iso_packet_descriptor *lib_desc = &ltransfer->iso_packet_desc[i];
+				struct libusb_iso_packet_descriptor *lib_desc = &transfer->iso_packet_desc[i];
 
 				switch (transfer_priv->IsochronousResultsArray[i].TransferResult) {
 				case STATUS_SUCCESS:
@@ -836,70 +796,35 @@ void windows_get_overlapped_result(struct usbi_transfer *transfer, struct winfd
 			}
 		}
 
-		*io_size = (DWORD) transfer_priv->request.Result.GenResult.BytesTransferred;
-		*io_result = usbdk_translate_usbd_status((USBD_STATUS) transfer_priv->request.Result.GenResult.UsbdStatus);
-	}
-	else {
+		*io_size = (DWORD)transfer_priv->request.Result.GenResult.BytesTransferred;
+		*io_result = usbdk_translate_usbd_status((USBD_STATUS)transfer_priv->request.Result.GenResult.UsbdStatus);
+	} else {
 		*io_result = GetLastError();
 	}
 }
 
-static int usbdk_clock_gettime(int clk_id, struct timespec *tp)
-{
-	return windows_clock_gettime(clk_id, tp);
-}
-
-const struct usbi_os_backend usbdk_backend = {
-	"Windows",
-	USBI_CAP_HAS_HID_ACCESS,
+const struct windows_backend usbdk_backend = {
 	usbdk_init,
 	usbdk_exit,
-
 	usbdk_get_device_list,
-	NULL,
 	usbdk_open,
 	usbdk_close,
-
 	usbdk_get_device_descriptor,
 	usbdk_get_active_config_descriptor,
 	usbdk_get_config_descriptor,
-	NULL,
-
+	usbdk_get_config_descriptor_by_value,
 	usbdk_get_configuration,
 	usbdk_set_configuration,
 	usbdk_claim_interface,
 	usbdk_release_interface,
-
 	usbdk_set_interface_altsetting,
 	usbdk_clear_halt,
 	usbdk_reset_device,
-
-	NULL,
-	NULL,
-
-	NULL,	// dev_mem_alloc()
-	NULL,	// dev_mem_free()
-
-	usbdk_kernel_driver_active,
-	usbdk_detach_kernel_driver,
-	usbdk_attach_kernel_driver,
-
 	usbdk_destroy_device,
-
 	usbdk_submit_transfer,
 	usbdk_cancel_transfer,
-	windows_clear_transfer_priv,
-
-	windows_handle_events,
-	NULL,
-
-	usbdk_clock_gettime,
-#if defined(USBI_TIMERFD_AVAILABLE)
-	NULL,
-#endif
-	sizeof(struct usbdk_device_priv),
-	0,
-	sizeof(struct usbdk_transfer_priv),
+	usbdk_clear_transfer_priv,
+	usbdk_copy_transfer_data,
+	usbdk_get_transfer_fd,
+	usbdk_get_overlapped_result,
 };
-
-#endif /* USE_USBDK */

+ 3 - 46
vendor/github.com/karalabe/hid/libusb/libusb/os/windows_usbdk.h → vendor/github.com/karalabe/usb/libusb/libusb/os/windows_usbdk.h

@@ -23,56 +23,13 @@
 
 #pragma once
 
-typedef struct tag_USB_DK_DEVICE_ID {
-	WCHAR DeviceID[MAX_DEVICE_ID_LEN];
-	WCHAR InstanceID[MAX_DEVICE_ID_LEN];
-} USB_DK_DEVICE_ID, *PUSB_DK_DEVICE_ID;
+#include "windows_nt_common.h"
 
-static inline void UsbDkFillIDStruct(USB_DK_DEVICE_ID *ID, PCWCHAR DeviceID, PCWCHAR InstanceID)
-{
-	wcsncpy_s(ID->DeviceID, DeviceID, MAX_DEVICE_ID_LEN);
-	wcsncpy_s(ID->InstanceID, InstanceID, MAX_DEVICE_ID_LEN);
-}
-
-typedef struct tag_USB_DK_DEVICE_INFO {
-	USB_DK_DEVICE_ID ID;
-	ULONG64 FilterID;
-	ULONG64 Port;
-	ULONG64 Speed;
-	USB_DEVICE_DESCRIPTOR DeviceDescriptor;
-} USB_DK_DEVICE_INFO, *PUSB_DK_DEVICE_INFO;
-
-typedef struct tag_USB_DK_CONFIG_DESCRIPTOR_REQUEST {
+typedef struct USB_DK_CONFIG_DESCRIPTOR_REQUEST {
 	USB_DK_DEVICE_ID ID;
 	ULONG64 Index;
 } USB_DK_CONFIG_DESCRIPTOR_REQUEST, *PUSB_DK_CONFIG_DESCRIPTOR_REQUEST;
 
-typedef struct tag_USB_DK_ISO_TRANSFER_RESULT {
-	ULONG64 ActualLength;
-	ULONG64 TransferResult;
-} USB_DK_ISO_TRANSFER_RESULT, *PUSB_DK_ISO_TRANSFER_RESULT;
-
-typedef struct tag_USB_DK_GEN_TRANSFER_RESULT {
-	ULONG64 BytesTransferred;
-	ULONG64 UsbdStatus; // USBD_STATUS code
-} USB_DK_GEN_TRANSFER_RESULT, *PUSB_DK_GEN_TRANSFER_RESULT;
-
-typedef struct tag_USB_DK_TRANSFER_RESULT {
-	USB_DK_GEN_TRANSFER_RESULT GenResult;
-	PVOID64 IsochronousResultsArray; // array of USB_DK_ISO_TRANSFER_RESULT
-} USB_DK_TRANSFER_RESULT, *PUSB_DK_TRANSFER_RESULT;
-
-typedef struct tag_USB_DK_TRANSFER_REQUEST {
-	ULONG64 EndpointAddress;
-	PVOID64 Buffer;
-	ULONG64 BufferLength;
-	ULONG64 TransferType;
-	ULONG64 IsochronousPacketsArraySize;
-	PVOID64 IsochronousPacketsArray;
-
-	USB_DK_TRANSFER_RESULT Result;
-} USB_DK_TRANSFER_REQUEST, *PUSB_DK_TRANSFER_REQUEST;
-
 typedef enum {
 	TransferFailure = 0,
 	TransferSuccess,
@@ -90,7 +47,7 @@ typedef enum {
 typedef enum {
 	ControlTransferType,
 	BulkTransferType,
-	IntertuptTransferType,
+	InterruptTransferType,
 	IsochronousTransferType
 } USB_DK_TRANSFER_TYPE;
 

+ 3009 - 0
vendor/github.com/karalabe/usb/libusb/libusb/os/windows_winusb.c

@@ -0,0 +1,3009 @@
+/*
+ * windows backend for libusb 1.0
+ * Copyright © 2009-2012 Pete Batard <pete@akeo.ie>
+ * Copyright © 2016-2018 Chris Dickens <christopher.a.dickens@gmail.com>
+ * With contributions from Michael Plante, Orin Eman et al.
+ * Parts of this code adapted from libusb-win32-v1 by Stephan Meyer
+ * HID Reports IOCTLs inspired from HIDAPI by Alan Ott, Signal 11 Software
+ * Hash table functions adapted from glibc, by Ulrich Drepper et al.
+ * 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
+ */
+
+#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>
+#include <winioctl.h>
+
+#include "libusbi.h"
+#include "windows_common.h"
+#include "windows_nt_common.h"
+#include "windows_winusb.h"
+
+// Unfuckup the 'inferface' keyword
+#undef interface
+
+#define HANDLE_VALID(h) (((h) != NULL) && ((h) != INVALID_HANDLE_VALUE))
+
+// 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;			\
+	}
+
+// WinUSB-like API prototypes
+static int winusbx_init(struct libusb_context *ctx);
+static void winusbx_exit(void);
+static int winusbx_open(int sub_api, struct libusb_device_handle *dev_handle);
+static void winusbx_close(int sub_api, struct libusb_device_handle *dev_handle);
+static int winusbx_configure_endpoints(int sub_api, struct libusb_device_handle *dev_handle, int iface);
+static int winusbx_claim_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface);
+static int winusbx_release_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface);
+static int winusbx_submit_control_transfer(int sub_api, struct usbi_transfer *itransfer);
+static int winusbx_set_interface_altsetting(int sub_api, struct libusb_device_handle *dev_handle, int iface, int altsetting);
+static int winusbx_submit_iso_transfer(int sub_api, struct usbi_transfer *itransfer);
+static int winusbx_submit_bulk_transfer(int sub_api, struct usbi_transfer *itransfer);
+static int winusbx_clear_halt(int sub_api, struct libusb_device_handle *dev_handle, unsigned char endpoint);
+static int winusbx_abort_transfers(int sub_api, struct usbi_transfer *itransfer);
+static int winusbx_abort_control(int sub_api, struct usbi_transfer *itransfer);
+static int winusbx_reset_device(int sub_api, struct libusb_device_handle *dev_handle);
+static int winusbx_copy_transfer_data(int sub_api, struct usbi_transfer *itransfer, uint32_t io_size);
+// Composite API prototypes
+static int composite_open(int sub_api, struct libusb_device_handle *dev_handle);
+static void composite_close(int sub_api, struct libusb_device_handle *dev_handle);
+static int composite_claim_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface);
+static int composite_set_interface_altsetting(int sub_api, struct libusb_device_handle *dev_handle, int iface, int altsetting);
+static int composite_release_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface);
+static int composite_submit_control_transfer(int sub_api, struct usbi_transfer *itransfer);
+static int composite_submit_bulk_transfer(int sub_api, struct usbi_transfer *itransfer);
+static int composite_submit_iso_transfer(int sub_api, struct usbi_transfer *itransfer);
+static int composite_clear_halt(int sub_api, struct libusb_device_handle *dev_handle, unsigned char endpoint);
+static int composite_abort_transfers(int sub_api, struct usbi_transfer *itransfer);
+static int composite_abort_control(int sub_api, struct usbi_transfer *itransfer);
+static int composite_reset_device(int sub_api, struct libusb_device_handle *dev_handle);
+static int composite_copy_transfer_data(int sub_api, struct usbi_transfer *itransfer, uint32_t io_size);
+
+static usbi_mutex_t autoclaim_lock;
+
+// API globals
+static HMODULE WinUSBX_handle = NULL;
+static struct winusb_interface WinUSBX[SUB_API_MAX];
+#define CHECK_WINUSBX_AVAILABLE(sub_api)		\
+	do {						\
+		if (sub_api == SUB_API_NOTSET)		\
+			sub_api = priv->sub_api;	\
+		if (!WinUSBX[sub_api].initialized) 	\
+			return LIBUSB_ERROR_ACCESS;	\
+	} while (0)
+
+static bool api_hid_available = false;
+#define CHECK_HID_AVAILABLE				\
+	do {						\
+		if (!api_hid_available)			\
+			return LIBUSB_ERROR_ACCESS;	\
+	} while (0)
+
+#if defined(ENABLE_LOGGING)
+static const char *guid_to_string(const GUID *guid)
+{
+	static char guid_string[MAX_GUID_STRING_LENGTH];
+
+	if (guid == NULL)
+		return "";
+
+	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
+
+/*
+ * 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;
+	char *ret_path;
+	size_t add_root = 0;
+
+	if (path == NULL)
+		return NULL;
+
+	size = strlen(path) + 1;
+
+	// Microsoft indiscriminately uses '\\?\', '\\.\', '##?#" or "##.#" for root prefixes.
+	if (!((size > 3) && (((path[0] == '\\') && (path[1] == '\\') && (path[3] == '\\'))
+			|| ((path[0] == '#') && (path[1] == '#') && (path[3] == '#'))))) {
+		add_root = sizeof(root_prefix);
+		size += add_root;
+	}
+
+	ret_path = malloc(size);
+	if (ret_path == NULL)
+		return NULL;
+
+	strcpy(&ret_path[add_root], path);
+
+	// Ensure consistency with root prefix
+	memcpy(ret_path, root_prefix, sizeof(root_prefix));
+
+	// Same goes for '\' and '#' after the root prefix. Ensure '#' is used
+	for (j = sizeof(root_prefix); 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, AdvAPI32, OLE32 and SetupAPI DLL functions
+ */
+static BOOL init_dlls(void)
+{
+	DLL_GET_HANDLE(Cfgmgr32);
+	DLL_LOAD_FUNC(Cfgmgr32, CM_Get_Parent, TRUE);
+	DLL_LOAD_FUNC(Cfgmgr32, CM_Get_Child, TRUE);
+
+	// Prefixed to avoid conflict with header files
+	DLL_GET_HANDLE(AdvAPI32);
+	DLL_LOAD_FUNC_PREFIXED(AdvAPI32, p, RegQueryValueExW, TRUE);
+	DLL_LOAD_FUNC_PREFIXED(AdvAPI32, p, RegCloseKey, TRUE);
+
+	DLL_GET_HANDLE(OLE32);
+	DLL_LOAD_FUNC_PREFIXED(OLE32, p, IIDFromString, TRUE);
+
+	DLL_GET_HANDLE(SetupAPI);
+	DLL_LOAD_FUNC_PREFIXED(SetupAPI, p, SetupDiGetClassDevsA, TRUE);
+	DLL_LOAD_FUNC_PREFIXED(SetupAPI, p, SetupDiEnumDeviceInfo, TRUE);
+	DLL_LOAD_FUNC_PREFIXED(SetupAPI, p, SetupDiEnumDeviceInterfaces, TRUE);
+	DLL_LOAD_FUNC_PREFIXED(SetupAPI, p, SetupDiGetDeviceInstanceIdA, TRUE);
+	DLL_LOAD_FUNC_PREFIXED(SetupAPI, p, SetupDiGetDeviceInterfaceDetailA, TRUE);
+	DLL_LOAD_FUNC_PREFIXED(SetupAPI, p, SetupDiGetDeviceRegistryPropertyA, TRUE);
+	DLL_LOAD_FUNC_PREFIXED(SetupAPI, p, SetupDiDestroyDeviceInfoList, TRUE);
+	DLL_LOAD_FUNC_PREFIXED(SetupAPI, p, SetupDiOpenDevRegKey, TRUE);
+	DLL_LOAD_FUNC_PREFIXED(SetupAPI, p, SetupDiOpenDeviceInterfaceRegKey, TRUE);
+
+	return TRUE;
+}
+
+static void exit_dlls(void)
+{
+	DLL_FREE_HANDLE(Cfgmgr32);
+	DLL_FREE_HANDLE(AdvAPI32);
+	DLL_FREE_HANDLE(OLE32);
+	DLL_FREE_HANDLE(SetupAPI);
+}
+
+/*
+ * enumerate interfaces for the whole USB class
+ *
+ * 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)
+ * enumerator: the generic USB class 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.
+ */
+static bool get_devinfo_data(struct libusb_context *ctx,
+	HDEVINFO *dev_info, SP_DEVINFO_DATA *dev_info_data, const char *enumerator, unsigned _index)
+{
+	if (_index == 0) {
+		*dev_info = pSetupDiGetClassDevsA(NULL, enumerator, NULL, DIGCF_PRESENT|DIGCF_ALLCLASSES);
+		if (*dev_info == INVALID_HANDLE_VALUE) {
+			usbi_err(ctx, "could not obtain device info set for PnP enumerator '%s': %s",
+				enumerator, windows_error_str(0));
+			return false;
+		}
+	}
+
+	dev_info_data->cbSize = sizeof(SP_DEVINFO_DATA);
+	if (!pSetupDiEnumDeviceInfo(*dev_info, _index, dev_info_data)) {
+		if (GetLastError() != ERROR_NO_MORE_ITEMS)
+			usbi_err(ctx, "could not obtain device info data for PnP enumerator '%s' index %u: %s",
+				enumerator, _index, windows_error_str(0));
+
+		pSetupDiDestroyDeviceInfoList(*dev_info);
+		*dev_info = INVALID_HANDLE_VALUE;
+		return false;
+	}
+	return true;
+}
+
+/*
+ * 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.
+ */
+static int get_interface_details(struct libusb_context *ctx, HDEVINFO dev_info,
+	PSP_DEVINFO_DATA dev_info_data, LPCGUID guid, DWORD *_index, char **dev_interface_path)
+{
+	SP_DEVICE_INTERFACE_DATA dev_interface_data;
+	PSP_DEVICE_INTERFACE_DETAIL_DATA_A dev_interface_details;
+	DWORD size;
+
+	dev_info_data->cbSize = sizeof(SP_DEVINFO_DATA);
+	dev_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
+	for (;;) {
+		if (!pSetupDiEnumDeviceInfo(dev_info, *_index, dev_info_data)) {
+			if (GetLastError() != ERROR_NO_MORE_ITEMS) {
+				usbi_err(ctx, "Could not obtain device info data for %s index %u: %s",
+					guid_to_string(guid), *_index, windows_error_str(0));
+				return LIBUSB_ERROR_OTHER;
+			}
+
+			// No more devices
+			return LIBUSB_SUCCESS;
+		}
+
+		// Always advance the index for the next iteration
+		(*_index)++;
+
+		if (pSetupDiEnumDeviceInterfaces(dev_info, dev_info_data, guid, 0, &dev_interface_data))
+			break;
+
+		if (GetLastError() != ERROR_NO_MORE_ITEMS) {
+			usbi_err(ctx, "Could not obtain interface data for %s devInst %X: %s",
+				guid_to_string(guid), dev_info_data->DevInst, windows_error_str(0));
+			return LIBUSB_ERROR_OTHER;
+		}
+
+		// Device does not have an interface matching this GUID, skip
+	}
+
+	// Read interface data (dummy + actual) to access the device path
+	if (!pSetupDiGetDeviceInterfaceDetailA(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 %s devInst %X: %s",
+				guid_to_string(guid), dev_info_data->DevInst, windows_error_str(0));
+			return LIBUSB_ERROR_OTHER;
+		}
+	} else {
+		usbi_err(ctx, "program assertion failed - http://msdn.microsoft.com/en-us/library/ms792901.aspx is wrong");
+		return LIBUSB_ERROR_OTHER;
+	}
+
+	dev_interface_details = malloc(size);
+	if (dev_interface_details == NULL) {
+		usbi_err(ctx, "could not allocate interface data for %s devInst %X",
+			guid_to_string(guid), dev_info_data->DevInst);
+		return LIBUSB_ERROR_NO_MEM;
+	}
+
+	dev_interface_details->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A);
+	if (!pSetupDiGetDeviceInterfaceDetailA(dev_info, &dev_interface_data,
+		dev_interface_details, size, NULL, NULL)) {
+		usbi_err(ctx, "could not access interface data (actual) for %s devInst %X: %s",
+			guid_to_string(guid), dev_info_data->DevInst, windows_error_str(0));
+		free(dev_interface_details);
+		return LIBUSB_ERROR_OTHER;
+	}
+
+	*dev_interface_path = sanitize_path(dev_interface_details->DevicePath);
+	free(dev_interface_details);
+
+	if (*dev_interface_path == NULL) {
+		usbi_err(ctx, "could not allocate interface path for %s devInst %X",
+			guid_to_string(guid), dev_info_data->DevInst);
+		return LIBUSB_ERROR_NO_MEM;
+	}
+
+	return LIBUSB_SUCCESS;
+}
+
+/* For libusb0 filter */
+static SP_DEVICE_INTERFACE_DETAIL_DATA_A *get_interface_details_filter(struct libusb_context *ctx,
+	HDEVINFO *dev_info, SP_DEVINFO_DATA *dev_info_data, const GUID *guid, unsigned _index, char *filter_path)
+{
+	SP_DEVICE_INTERFACE_DATA dev_interface_data;
+	SP_DEVICE_INTERFACE_DETAIL_DATA_A *dev_interface_details;
+	DWORD size;
+
+	if (_index == 0)
+		*dev_info = pSetupDiGetClassDevsA(guid, NULL, NULL, DIGCF_PRESENT|DIGCF_DEVICEINTERFACE);
+
+	if (dev_info_data != NULL) {
+		dev_info_data->cbSize = sizeof(SP_DEVINFO_DATA);
+		if (!pSetupDiEnumDeviceInfo(*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));
+
+			pSetupDiDestroyDeviceInfoList(*dev_info);
+			*dev_info = INVALID_HANDLE_VALUE;
+			return NULL;
+		}
+	}
+
+	dev_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
+	if (!pSetupDiEnumDeviceInterfaces(*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));
+
+		pSetupDiDestroyDeviceInfoList(*dev_info);
+		*dev_info = INVALID_HANDLE_VALUE;
+		return NULL;
+	}
+
+	// Read interface data (dummy + actual) to access the device path
+	if (!pSetupDiGetDeviceInterfaceDetailA(*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;
+	}
+
+	dev_interface_details = calloc(1, size);
+	if (dev_interface_details == 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_A);
+	if (!pSetupDiGetDeviceInterfaceDetailA(*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));
+
+	// [trobinso] lookup the libusb0 symbolic index.
+	if (dev_interface_details) {
+		HKEY hkey_device_interface = pSetupDiOpenDeviceInterfaceRegKey(*dev_info, &dev_interface_data, 0, KEY_READ);
+		if (hkey_device_interface != INVALID_HANDLE_VALUE) {
+			DWORD libusb0_symboliclink_index = 0;
+			DWORD value_length = sizeof(DWORD);
+			DWORD value_type = 0;
+			LONG status;
+
+			status = pRegQueryValueExW(hkey_device_interface, L"LUsb0", NULL, &value_type,
+				(LPBYTE)&libusb0_symboliclink_index, &value_length);
+			if (status == ERROR_SUCCESS) {
+				if (libusb0_symboliclink_index < 256) {
+					// libusb0.sys is connected to this device instance.
+					// If the the device interface guid is {F9F3FF14-AE21-48A0-8A25-8011A7A931D9} then it's a filter.
+					sprintf(filter_path, "\\\\.\\libusb0-%04u", (unsigned int)libusb0_symboliclink_index);
+					usbi_dbg("assigned libusb0 symbolic link %s", filter_path);
+				} else {
+					// libusb0.sys was connected to this device instance at one time; but not anymore.
+				}
+			}
+			pRegCloseKey(hkey_device_interface);
+		}
+	}
+
+	return dev_interface_details;
+
+err_exit:
+	pSetupDiDestroyDeviceInfoList(*dev_info);
+	*dev_info = INVALID_HANDLE_VALUE;
+	return NULL;
+}
+
+/*
+ * Returns the first known ancestor of a device
+ */
+static struct libusb_device *get_ancestor(struct libusb_context *ctx,
+	DEVINST devinst, PDEVINST _parent_devinst)
+{
+	struct libusb_device *dev = NULL;
+	DEVINST parent_devinst;
+
+	while (dev == NULL) {
+		if (CM_Get_Parent(&parent_devinst, devinst, 0) != CR_SUCCESS)
+			break;
+		devinst = parent_devinst;
+		dev = usbi_get_device_by_session_id(ctx, (unsigned long)devinst);
+	}
+
+	if ((dev != NULL) && (_parent_devinst != NULL))
+		*_parent_devinst = devinst;
+
+	return dev;
+}
+
+/*
+ * Determine which interface the given endpoint address belongs to
+ */
+static int get_interface_by_endpoint(struct libusb_config_descriptor *conf_desc, uint8_t ep)
+{
+	const struct libusb_interface *intf;
+	const struct libusb_interface_descriptor *intf_desc;
+	int i, j, k;
+
+	for (i = 0; i < conf_desc->bNumInterfaces; i++) {
+		intf = &conf_desc->interface[i];
+		for (j = 0; j < intf->num_altsetting; j++) {
+			intf_desc = &intf->altsetting[j];
+			for (k = 0; k < intf_desc->bNumEndpoints; k++) {
+				if (intf_desc->endpoint[k].bEndpointAddress == ep) {
+					usbi_dbg("found endpoint %02X on interface %d", intf_desc->bInterfaceNumber, i);
+					return intf_desc->bInterfaceNumber;
+				}
+			}
+		}
+	}
+
+	usbi_dbg("endpoint %02X not found on any interface", ep);
+	return LIBUSB_ERROR_NOT_FOUND;
+}
+
+/*
+ * Populate the endpoints addresses of the device_priv interface helper structs
+ */
+static int windows_assign_endpoints(struct libusb_device_handle *dev_handle, int iface, int altsetting)
+{
+	int i, r;
+	struct winusb_device_priv *priv = _device_priv(dev_handle->dev);
+	struct libusb_config_descriptor *conf_desc;
+	const struct libusb_interface_descriptor *if_desc;
+	struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev);
+
+	r = libusb_get_active_config_descriptor(dev_handle->dev, &conf_desc);
+	if (r != LIBUSB_SUCCESS) {
+		usbi_warn(ctx, "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);
+		libusb_free_config_descriptor(conf_desc);
+		return LIBUSB_SUCCESS;
+	}
+
+	priv->usb_interface[iface].endpoint = malloc(if_desc->bNumEndpoints);
+	if (priv->usb_interface[iface].endpoint == NULL) {
+		libusb_free_config_descriptor(conf_desc);
+		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);
+
+	// Extra init may be required to configure endpoints
+	if (priv->apib->configure_endpoints)
+		r = priv->apib->configure_endpoints(SUB_API_NOTSET, dev_handle, iface);
+
+	return r;
+}
+
+// Lookup for a match in the list of API driver names
+// return -1 if not found, driver match number otherwise
+static int get_sub_api(char *driver, int api)
+{
+	int i;
+	const char sep_str[2] = {LIST_SEPARATOR, 0};
+	char *tok, *tmp_str;
+	size_t len = strlen(driver);
+
+	if (len == 0)
+		return SUB_API_NOTSET;
+
+	tmp_str = _strdup(driver);
+	if (tmp_str == NULL)
+		return SUB_API_NOTSET;
+
+	tok = strtok(tmp_str, sep_str);
+	while (tok != NULL) {
+		for (i = 0; i < usb_api_backend[api].nb_driver_names; i++) {
+			if (_stricmp(tok, usb_api_backend[api].driver_name_list[i]) == 0) {
+				free(tmp_str);
+				return i;
+			}
+		}
+		tok = strtok(NULL, sep_str);
+	}
+
+	free(tmp_str);
+	return SUB_API_NOTSET;
+}
+
+/*
+ * auto-claiming and auto-release helper functions
+ */
+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 winusb_device_handle_priv *handle_priv = _device_handle_priv(
+		transfer->dev_handle);
+	struct winusb_device_priv *priv = _device_priv(transfer->dev_handle->dev);
+	int current_interface = *interface_number;
+	int r = LIBUSB_SUCCESS;
+
+	switch (api_type) {
+	case USB_API_WINUSBX:
+	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->id == 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 winusb_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 winusb_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_error_name((enum libusb_error)r));
+		}
+	}
+	usbi_mutex_unlock(&autoclaim_lock);
+}
+
+/*
+ * init: libusb backend init function
+ */
+static int winusb_init(struct libusb_context *ctx)
+{
+	int i;
+
+	// We need a lock for proper auto-release
+	usbi_mutex_init(&autoclaim_lock);
+
+	// Load DLL imports
+	if (!init_dlls()) {
+		usbi_err(ctx, "could not resolve DLL functions");
+		return LIBUSB_ERROR_OTHER;
+	}
+
+	// Initialize the low level APIs (we don't care about errors at this stage)
+	for (i = 0; i < USB_API_MAX; i++) {
+		if (usb_api_backend[i].init && usb_api_backend[i].init(ctx))
+			usbi_warn(ctx, "error initializing %s backend",
+				usb_api_backend[i].designation);
+	}
+
+	return LIBUSB_SUCCESS;
+}
+
+/*
+* exit: libusb backend deinitialization function
+*/
+static void winusb_exit(struct libusb_context *ctx)
+{
+	int i;
+
+	for (i = 0; i < USB_API_MAX; i++) {
+		if (usb_api_backend[i].exit)
+			usb_api_backend[i].exit();
+	}
+
+	exit_dlls();
+	usbi_mutex_destroy(&autoclaim_lock);
+}
+
+/*
+ * fetch and cache all the config descriptors through I/O
+ */
+static void cache_config_descriptors(struct libusb_device *dev, HANDLE hub_handle)
+{
+	struct libusb_context *ctx = DEVICE_CTX(dev);
+	struct winusb_device_priv *priv = _device_priv(dev);
+	DWORD size, ret_size;
+	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;
+
+	if (dev->num_configurations == 0)
+		return;
+
+	priv->config_descriptor = calloc(dev->num_configurations, sizeof(PUSB_CONFIGURATION_DESCRIPTOR));
+	if (priv->config_descriptor == NULL) {
+		usbi_err(ctx, "could not allocate configuration descriptor array for '%s'", priv->dev_id);
+		return;
+	}
+
+	for (i = 0; i <= dev->num_configurations; i++) {
+		safe_free(cd_buf_actual);
+
+		if (i == dev->num_configurations)
+			break;
+
+		size = sizeof(cd_buf_short);
+		memset(&cd_buf_short, 0, size);
+
+		cd_buf_short.req.ConnectionIndex = (ULONG)dev->port_number;
+		cd_buf_short.req.SetupPacket.bmRequest = LIBUSB_ENDPOINT_IN;
+		cd_buf_short.req.SetupPacket.bRequest = LIBUSB_REQUEST_GET_DESCRIPTOR;
+		cd_buf_short.req.SetupPacket.wValue = (LIBUSB_DT_CONFIG << 8) | i;
+		cd_buf_short.req.SetupPacket.wIndex = 0;
+		cd_buf_short.req.SetupPacket.wLength = (USHORT)sizeof(USB_CONFIGURATION_DESCRIPTOR);
+
+		// Dummy call to get the required data size. Initial failures are reported as info rather
+		// than error as they can occur for non-penalizing situations, such as with some hubs.
+		// coverity[tainted_data_argument]
+		if (!DeviceIoControl(hub_handle, IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, &cd_buf_short, size,
+			&cd_buf_short, size, &ret_size, NULL)) {
+			usbi_info(ctx, "could not access configuration descriptor %u (dummy) for '%s': %s", i, priv->dev_id, windows_error_str(0));
+			continue;
+		}
+
+		if ((ret_size != size) || (cd_buf_short.desc.wTotalLength < sizeof(USB_CONFIGURATION_DESCRIPTOR))) {
+			usbi_info(ctx, "unexpected configuration descriptor %u size (dummy) for '%s'", i, priv->dev_id);
+			continue;
+		}
+
+		size = sizeof(USB_DESCRIPTOR_REQUEST) + cd_buf_short.desc.wTotalLength;
+		cd_buf_actual = malloc(size);
+		if (cd_buf_actual == NULL) {
+			usbi_err(ctx, "could not allocate configuration descriptor %u buffer for '%s'", i, priv->dev_id);
+			continue;
+		}
+
+		// Actual call
+		cd_buf_actual->ConnectionIndex = (ULONG)dev->port_number;
+		cd_buf_actual->SetupPacket.bmRequest = LIBUSB_ENDPOINT_IN;
+		cd_buf_actual->SetupPacket.bRequest = LIBUSB_REQUEST_GET_DESCRIPTOR;
+		cd_buf_actual->SetupPacket.wValue = (LIBUSB_DT_CONFIG << 8) | i;
+		cd_buf_actual->SetupPacket.wIndex = 0;
+		cd_buf_actual->SetupPacket.wLength = cd_buf_short.desc.wTotalLength;
+
+		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 %u (actual) for '%s': %s", i, priv->dev_id, windows_error_str(0));
+			continue;
+		}
+
+		cd_data = (PUSB_CONFIGURATION_DESCRIPTOR)((UCHAR *)cd_buf_actual + sizeof(USB_DESCRIPTOR_REQUEST));
+
+		if ((size != ret_size) || (cd_data->wTotalLength != cd_buf_short.desc.wTotalLength)) {
+			usbi_err(ctx, "unexpected configuration descriptor %u size (actual) for '%s'", i, priv->dev_id);
+			continue;
+		}
+
+		if (cd_data->bDescriptorType != LIBUSB_DT_CONFIG) {
+			usbi_err(ctx, "descriptor %u not a configuration descriptor for '%s'", i, priv->dev_id);
+			continue;
+		}
+
+		usbi_dbg("cached config descriptor %u (bConfigurationValue=%u, %u bytes)",
+			i, cd_data->bConfigurationValue, cd_data->wTotalLength);
+
+		// Cache the descriptor
+		priv->config_descriptor[i] = malloc(cd_data->wTotalLength);
+		if (priv->config_descriptor[i] != NULL) {
+			memcpy(priv->config_descriptor[i], cd_data, cd_data->wTotalLength);
+		} else {
+			usbi_err(ctx, "could not allocate configuration descriptor %u buffer for '%s'", i, priv->dev_id);
+		}
+	}
+}
+
+/*
+ * Populate a libusb device structure
+ */
+static int init_device(struct libusb_device *dev, struct libusb_device *parent_dev,
+	uint8_t port_number, DEVINST devinst)
+{
+	struct libusb_context *ctx;
+	struct libusb_device *tmp_dev;
+	struct winusb_device_priv *priv, *parent_priv;
+	USB_NODE_CONNECTION_INFORMATION_EX conn_info;
+	USB_NODE_CONNECTION_INFORMATION_EX_V2 conn_info_v2;
+	HANDLE hub_handle;
+	DWORD size;
+	uint8_t bus_number, depth;
+	int r;
+
+	priv = _device_priv(dev);
+
+	// If the device is already initialized, we can stop here
+	if (priv->initialized)
+		return LIBUSB_SUCCESS;
+
+	if (parent_dev != NULL) { // Not a HCD root hub
+		ctx = DEVICE_CTX(dev);
+		parent_priv = _device_priv(parent_dev);
+		if (parent_priv->apib->id != USB_API_HUB) {
+			usbi_warn(ctx, "parent for device '%s' is not a hub", priv->dev_id);
+			return LIBUSB_ERROR_NOT_FOUND;
+		}
+
+		// Calculate depth and fetch bus number
+		bus_number = parent_dev->bus_number;
+		if (bus_number == 0) {
+			tmp_dev = get_ancestor(ctx, devinst, &devinst);
+			if (tmp_dev != parent_dev) {
+				usbi_err(ctx, "program assertion failed - first ancestor is not parent");
+				return LIBUSB_ERROR_NOT_FOUND;
+			}
+			libusb_unref_device(tmp_dev);
+
+			for (depth = 1; bus_number == 0; depth++) {
+				tmp_dev = get_ancestor(ctx, devinst, &devinst);
+				if (tmp_dev->bus_number != 0) {
+					bus_number = tmp_dev->bus_number;
+					depth += _device_priv(tmp_dev)->depth;
+				}
+				libusb_unref_device(tmp_dev);
+			}
+		} else {
+			depth = parent_priv->depth + 1;
+		}
+
+		if (bus_number == 0) {
+			usbi_err(ctx, "program assertion failed - bus number not found for '%s'", priv->dev_id);
+			return LIBUSB_ERROR_NOT_FOUND;
+		}
+
+		dev->bus_number = bus_number;
+		dev->port_number = port_number;
+		dev->parent_dev = parent_dev;
+		priv->depth = depth;
+
+		hub_handle = CreateFileA(parent_priv->path, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
+				     0, NULL);
+		if (hub_handle == INVALID_HANDLE_VALUE) {
+			usbi_warn(ctx, "could not open hub %s: %s", parent_priv->path, windows_error_str(0));
+			return LIBUSB_ERROR_ACCESS;
+		}
+
+		memset(&conn_info, 0, sizeof(conn_info));
+		conn_info.ConnectionIndex = (ULONG)port_number;
+		// coverity[tainted_data_argument]
+		if (!DeviceIoControl(hub_handle, IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX, &conn_info, sizeof(conn_info),
+			&conn_info, sizeof(conn_info), &size, NULL)) {
+			usbi_warn(ctx, "could not get node connection information for device '%s': %s",
+				  priv->dev_id, windows_error_str(0));
+			CloseHandle(hub_handle);
+			return LIBUSB_ERROR_NO_DEVICE;
+		}
+
+		if (conn_info.ConnectionStatus == NoDeviceConnected) {
+			usbi_err(ctx, "device '%s' is no longer connected!", priv->dev_id);
+			CloseHandle(hub_handle);
+			return LIBUSB_ERROR_NO_DEVICE;
+		}
+
+		memcpy(&priv->dev_descriptor, &(conn_info.DeviceDescriptor), sizeof(USB_DEVICE_DESCRIPTOR));
+		dev->num_configurations = priv->dev_descriptor.bNumConfigurations;
+		priv->active_config = conn_info.CurrentConfigurationValue;
+		usbi_dbg("found %u configurations (active conf: %u)", dev->num_configurations, priv->active_config);
+
+		// Cache as many config descriptors as we can
+		cache_config_descriptors(dev, hub_handle);
+
+		// In their great wisdom, Microsoft decided to BREAK the USB speed report between Windows 7 and Windows 8
+		if (windows_version >= WINDOWS_8) {
+			conn_info_v2.ConnectionIndex = (ULONG)port_number;
+			conn_info_v2.Length = sizeof(USB_NODE_CONNECTION_INFORMATION_EX_V2);
+			conn_info_v2.SupportedUsbProtocols.Usb300 = 1;
+			if (!DeviceIoControl(hub_handle, IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX_V2,
+				&conn_info_v2, sizeof(conn_info_v2), &conn_info_v2, sizeof(conn_info_v2), &size, NULL)) {
+				usbi_warn(ctx, "could not get node connection information (V2) for device '%s': %s",
+					  priv->dev_id,  windows_error_str(0));
+			} else if (conn_info_v2.Flags.DeviceIsOperatingAtSuperSpeedOrHigher) {
+				conn_info.Speed = 3;
+			}
+		}
+
+		CloseHandle(hub_handle);
+
+		if (conn_info.DeviceAddress > UINT8_MAX)
+			usbi_err(ctx, "program assertion failed - device address overflow");
+
+		dev->device_address = (uint8_t)conn_info.DeviceAddress;
+
+		switch (conn_info.Speed) {
+		case 0: dev->speed = LIBUSB_SPEED_LOW; break;
+		case 1: dev->speed = LIBUSB_SPEED_FULL; break;
+		case 2: dev->speed = LIBUSB_SPEED_HIGH; break;
+		case 3: dev->speed = LIBUSB_SPEED_SUPER; break;
+		default:
+			usbi_warn(ctx, "unknown device speed %u", conn_info.Speed);
+			break;
+		}
+	}
+
+	r = usbi_sanitize_device(dev);
+	if (r)
+		return r;
+
+	priv->initialized = true;
+
+	usbi_dbg("(bus: %u, addr: %u, depth: %u, port: %u): '%s'",
+		dev->bus_number, dev->device_address, priv->depth, dev->port_number, priv->dev_id);
+
+	return LIBUSB_SUCCESS;
+}
+
+static int enumerate_hcd_root_hub(struct libusb_context *ctx, const char *dev_id,
+	uint8_t bus_number, DEVINST devinst)
+{
+	struct libusb_device *dev;
+	struct winusb_device_priv *priv;
+	unsigned long session_id;
+	DEVINST child_devinst;
+
+	if (CM_Get_Child(&child_devinst, devinst, 0) != CR_SUCCESS) {
+		usbi_err(ctx, "could not get child devinst for '%s'", dev_id);
+		return LIBUSB_ERROR_OTHER;
+	}
+
+	session_id = (unsigned long)child_devinst;
+	dev = usbi_get_device_by_session_id(ctx, session_id);
+	if (dev == NULL) {
+		usbi_err(ctx, "program assertion failed - HCD '%s' child not found", dev_id);
+		return LIBUSB_ERROR_NO_DEVICE;
+	}
+
+	if (dev->bus_number == 0) {
+		// Only do this once
+		usbi_dbg("assigning HCD '%s' bus number %u", dev_id, bus_number);
+		priv = _device_priv(dev);
+		dev->bus_number = bus_number;
+		dev->num_configurations = 1;
+		priv->dev_descriptor.bLength = LIBUSB_DT_DEVICE_SIZE;
+		priv->dev_descriptor.bDescriptorType = LIBUSB_DT_DEVICE;
+		priv->dev_descriptor.bDeviceClass = LIBUSB_CLASS_HUB;
+		priv->dev_descriptor.bNumConfigurations = 1;
+		priv->active_config = 1;
+		priv->root_hub = true;
+		if (sscanf(dev_id, "PCI\\VEN_%04hx&DEV_%04hx%*s", &priv->dev_descriptor.idVendor, &priv->dev_descriptor.idProduct) != 2) {
+			usbi_warn(ctx, "could not infer VID/PID of HCD root hub from '%s'", dev_id);
+			priv->dev_descriptor.idVendor = 0x1d6b; // Linux Foundation root hub
+			priv->dev_descriptor.idProduct = 1;
+		}
+	}
+
+	libusb_unref_device(dev);
+	return LIBUSB_SUCCESS;
+}
+
+// Returns the api type, or 0 if not found/unsupported
+static void get_api_type(struct libusb_context *ctx, HDEVINFO *dev_info,
+	SP_DEVINFO_DATA *dev_info_data, int *api, int *sub_api)
+{
+	// Precedence for filter drivers vs driver is in the order of this array
+	struct driver_lookup lookup[3] = {
+		{"\0\0", SPDRP_SERVICE, "driver"},
+		{"\0\0", SPDRP_UPPERFILTERS, "upper filter driver"},
+		{"\0\0", SPDRP_LOWERFILTERS, "lower filter driver"}
+	};
+	DWORD size, reg_type;
+	unsigned k, l;
+	int i, j;
+
+	// Check the service & filter names to know the API we should use
+	for (k = 0; k < 3; k++) {
+		if (pSetupDiGetDeviceRegistryPropertyA(*dev_info, dev_info_data, lookup[k].reg_prop,
+			&reg_type, (PBYTE)lookup[k].list, MAX_KEY_LENGTH, &size)) {
+			// Turn the REG_SZ SPDRP_SERVICE into REG_MULTI_SZ
+			if (lookup[k].reg_prop == SPDRP_SERVICE)
+				// our buffers are MAX_KEY_LENGTH + 1 so we can overflow if needed
+				lookup[k].list[strlen(lookup[k].list) + 1] = 0;
+
+			// MULTI_SZ is a pain to work with. Turn it into something much more manageable
+			// NB: none of the driver names we check against contain LIST_SEPARATOR,
+			// (currently ';'), so even if an unsuported one does, it's not an issue
+			for (l = 0; (lookup[k].list[l] != 0) || (lookup[k].list[l + 1] != 0); l++) {
+				if (lookup[k].list[l] == 0)
+					lookup[k].list[l] = LIST_SEPARATOR;
+			}
+			usbi_dbg("%s(s): %s", lookup[k].designation, lookup[k].list);
+		} else {
+			if (GetLastError() != ERROR_INVALID_DATA)
+				usbi_dbg("could not access %s: %s", lookup[k].designation, windows_error_str(0));
+			lookup[k].list[0] = 0;
+		}
+	}
+
+	for (i = 2; i < USB_API_MAX; i++) {
+		for (k = 0; k < 3; k++) {
+			j = get_sub_api(lookup[k].list, i);
+			if (j >= 0) {
+				usbi_dbg("matched %s name against %s", lookup[k].designation,
+					(i != USB_API_WINUSBX) ? usb_api_backend[i].designation : usb_api_backend[i].driver_name_list[j]);
+				*api = i;
+				*sub_api = j;
+				return;
+			}
+		}
+	}
+}
+
+static int set_composite_interface(struct libusb_context *ctx, struct libusb_device *dev,
+	char *dev_interface_path, char *device_id, int api, int sub_api)
+{
+	struct winusb_device_priv *priv = _device_priv(dev);
+	int interface_number;
+	const char *mi_str;
+
+	// Because MI_## are not necessarily in sequential order (some composite
+	// devices will have only MI_00 & MI_03 for instance), we retrieve the actual
+	// interface number from the path's MI value
+	mi_str = strstr(device_id, "MI_");
+	if ((mi_str != NULL) && isdigit(mi_str[3]) && isdigit(mi_str[4])) {
+		interface_number = ((mi_str[3] - '0') * 10) + (mi_str[4] - '0');
+	} else {
+		usbi_warn(ctx, "failure to read interface number for %s, using default value", device_id);
+		interface_number = 0;
+	}
+
+	if (interface_number >= USB_MAXINTERFACES) {
+		usbi_warn(ctx, "interface %d too large - ignoring interface path %s", interface_number, dev_interface_path);
+		return LIBUSB_ERROR_ACCESS;
+	}
+
+	if (priv->usb_interface[interface_number].path != NULL) {
+		if (api == USB_API_HID) {
+			// HID devices can have multiple collections (COL##) for each MI_## interface
+			usbi_dbg("interface[%d] already set - ignoring HID collection: %s",
+				interface_number, device_id);
+			return LIBUSB_ERROR_ACCESS;
+		}
+		// In other cases, just use the latest data
+		safe_free(priv->usb_interface[interface_number].path);
+	}
+
+	usbi_dbg("interface[%d] = %s", interface_number, dev_interface_path);
+	priv->usb_interface[interface_number].path = dev_interface_path;
+	priv->usb_interface[interface_number].apib = &usb_api_backend[api];
+	priv->usb_interface[interface_number].sub_api = sub_api;
+	if ((api == USB_API_HID) && (priv->hid == NULL)) {
+		priv->hid = calloc(1, sizeof(struct hid_device_priv));
+		if (priv->hid == NULL)
+			return LIBUSB_ERROR_NO_MEM;
+	}
+
+	return LIBUSB_SUCCESS;
+}
+
+static int set_hid_interface(struct libusb_context *ctx, struct libusb_device *dev,
+	char *dev_interface_path)
+{
+	int i;
+	struct winusb_device_priv *priv = _device_priv(dev);
+
+	if (priv->hid == NULL) {
+		usbi_err(ctx, "program assertion failed: parent is not HID");
+		return LIBUSB_ERROR_NO_DEVICE;
+	} else if (priv->hid->nb_interfaces == USB_MAXINTERFACES) {
+		usbi_err(ctx, "program assertion failed: max USB interfaces reached for HID device");
+		return LIBUSB_ERROR_NO_DEVICE;
+	}
+
+	for (i = 0; i < priv->hid->nb_interfaces; i++) {
+		if ((priv->usb_interface[i].path != NULL) && strcmp(priv->usb_interface[i].path, dev_interface_path) == 0) {
+			usbi_dbg("interface[%d] already set to %s", i, dev_interface_path);
+			return LIBUSB_ERROR_ACCESS;
+		}
+	}
+
+	priv->usb_interface[priv->hid->nb_interfaces].path = dev_interface_path;
+	priv->usb_interface[priv->hid->nb_interfaces].apib = &usb_api_backend[USB_API_HID];
+	usbi_dbg("interface[%u] = %s", priv->hid->nb_interfaces, dev_interface_path);
+	priv->hid->nb_interfaces++;
+	return LIBUSB_SUCCESS;
+}
+
+/*
+ * get_device_list: libusb backend device enumeration function
+ */
+static int winusb_get_device_list(struct libusb_context *ctx, struct discovered_devs **_discdevs)
+{
+	struct discovered_devs *discdevs;
+	HDEVINFO *dev_info, dev_info_intf, dev_info_enum;
+	SP_DEVINFO_DATA dev_info_data;
+	DWORD _index = 0;
+	GUID hid_guid;
+	int r = LIBUSB_SUCCESS;
+	int api, sub_api;
+	unsigned int pass, i, j;
+	char enumerator[16];
+	char dev_id[MAX_PATH_LENGTH];
+	struct libusb_device *dev, *parent_dev;
+	struct winusb_device_priv *priv, *parent_priv;
+	char *dev_interface_path = NULL;
+	unsigned long session_id;
+	DWORD size, port_nr, reg_type, install_state;
+	HKEY key;
+	WCHAR guid_string_w[MAX_GUID_STRING_LENGTH];
+	GUID *if_guid;
+	LONG s;
+#define HUB_PASS 0
+#define DEV_PASS 1
+#define HCD_PASS 2
+#define GEN_PASS 3
+#define HID_PASS 4
+#define EXT_PASS 5
+	// Keep a list of guids that will be enumerated
+#define GUID_SIZE_STEP 8
+	const GUID **guid_list, **new_guid_list;
+	unsigned int guid_size = GUID_SIZE_STEP;
+	unsigned int nb_guids;
+	// Keep a list of PnP enumerator strings that are found
+	char *usb_enumerator[8] = { "USB" };
+	unsigned int nb_usb_enumerators = 1;
+	unsigned int usb_enum_index = 0;
+	// Keep a list of newly allocated devs to unref
+#define UNREF_SIZE_STEP 16
+	libusb_device **unref_list, **new_unref_list;
+	unsigned int unref_size = UNREF_SIZE_STEP;
+	unsigned int unref_cur = 0;
+
+	// PASS 1 : (re)enumerate HCDs (allows for HCD hotplug)
+	// PASS 2 : (re)enumerate HUBS
+	// PASS 3 : (re)enumerate generic USB devices (including driverless)
+	//           and list additional USB device interface GUIDs to explore
+	// PASS 4 : (re)enumerate master USB devices that have a device interface
+	// PASS 5+: (re)enumerate device interfaced GUIDs (including HID) and
+	//           set the device interfaces.
+
+	// Init the GUID table
+	guid_list = malloc(guid_size * sizeof(void *));
+	if (guid_list == NULL) {
+		usbi_err(ctx, "failed to alloc guid list");
+		return LIBUSB_ERROR_NO_MEM;
+	}
+
+	guid_list[HUB_PASS] = &GUID_DEVINTERFACE_USB_HUB;
+	guid_list[DEV_PASS] = &GUID_DEVINTERFACE_USB_DEVICE;
+	guid_list[HCD_PASS] = &GUID_DEVINTERFACE_USB_HOST_CONTROLLER;
+	guid_list[GEN_PASS] = NULL;
+	if (api_hid_available) {
+		HidD_GetHidGuid(&hid_guid);
+		guid_list[HID_PASS] = &hid_guid;
+	} else {
+		guid_list[HID_PASS] = NULL;
+	}
+	nb_guids = EXT_PASS;
+
+	unref_list = malloc(unref_size * sizeof(void *));
+	if (unref_list == NULL) {
+		usbi_err(ctx, "failed to alloc unref list");
+		free((void *)guid_list);
+		return LIBUSB_ERROR_NO_MEM;
+	}
+
+	dev_info_intf = pSetupDiGetClassDevsA(NULL, NULL, NULL, DIGCF_ALLCLASSES | DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
+	if (dev_info_intf == INVALID_HANDLE_VALUE) {
+		usbi_err(ctx, "failed to obtain device info list: %s", windows_error_str(0));
+		free(unref_list);
+		free((void *)guid_list);
+		return LIBUSB_ERROR_OTHER;
+	}
+
+	for (pass = 0; ((pass < nb_guids) && (r == LIBUSB_SUCCESS)); pass++) {
+//#define ENUM_DEBUG
+#if defined(ENABLE_LOGGING) && defined(ENUM_DEBUG)
+		const char * const passname[] = {"HUB", "DEV", "HCD", "GEN", "HID", "EXT"};
+		usbi_dbg("#### PROCESSING %ss %s", passname[MIN(pass, EXT_PASS)], guid_to_string(guid_list[pass]));
+#endif
+		if ((pass == HID_PASS) && (guid_list[HID_PASS] == NULL))
+			continue;
+
+		dev_info = (pass != GEN_PASS) ? &dev_info_intf : &dev_info_enum;
+
+		for (i = 0; ; i++) {
+			// safe loop: free up any (unprotected) dynamic resource
+			// NB: this is always executed before breaking the loop
+			safe_free(dev_interface_path);
+			priv = parent_priv = NULL;
+			dev = parent_dev = NULL;
+
+			// Safe loop: end of loop conditions
+			if (r != LIBUSB_SUCCESS)
+				break;
+
+			if ((pass == HCD_PASS) && (i == UINT8_MAX)) {
+				usbi_warn(ctx, "program assertion failed - found more than %u buses, skipping the rest.", UINT8_MAX);
+				break;
+			}
+
+			if (pass != GEN_PASS) {
+				// Except for GEN, all passes deal with device interfaces
+				r = get_interface_details(ctx, *dev_info, &dev_info_data, guid_list[pass], &_index, &dev_interface_path);
+				if ((r != LIBUSB_SUCCESS) || (dev_interface_path == NULL)) {
+					_index = 0;
+					break;
+				}
+			} else {
+				// Workaround for a Nec/Renesas USB 3.0 driver bug where root hubs are
+				// being listed under the "NUSB3" PnP Symbolic Name rather than "USB".
+				// The Intel USB 3.0 driver behaves similar, but uses "IUSB3"
+				// The Intel Alpine Ridge USB 3.1 driver uses "IARUSB3"
+				for (; usb_enum_index < nb_usb_enumerators; usb_enum_index++) {
+					if (get_devinfo_data(ctx, dev_info, &dev_info_data, usb_enumerator[usb_enum_index], i))
+						break;
+					i = 0;
+				}
+				if (usb_enum_index == nb_usb_enumerators)
+					break;
+			}
+
+			// Read the Device ID path
+			if (!pSetupDiGetDeviceInstanceIdA(*dev_info, &dev_info_data, dev_id, sizeof(dev_id), NULL)) {
+				usbi_warn(ctx, "could not read the device instance ID for devInst %X, skipping",
+					  dev_info_data.DevInst);
+				continue;
+			}
+
+#ifdef ENUM_DEBUG
+			usbi_dbg("PRO: %s", dev_id);
+#endif
+
+			// Set API to use or get additional data from generic pass
+			api = USB_API_UNSUPPORTED;
+			sub_api = SUB_API_NOTSET;
+			switch (pass) {
+			case HCD_PASS:
+				break;
+			case HUB_PASS:
+				api = USB_API_HUB;
+				// Fetch the PnP enumerator class for this hub
+				// This will allow us to enumerate all classes during the GEN pass
+				if (!pSetupDiGetDeviceRegistryPropertyA(*dev_info, &dev_info_data, SPDRP_ENUMERATOR_NAME,
+					NULL, (PBYTE)enumerator, sizeof(enumerator), NULL)) {
+					usbi_err(ctx, "could not read enumerator string for device '%s': %s", dev_id, windows_error_str(0));
+					LOOP_BREAK(LIBUSB_ERROR_OTHER);
+				}
+				for (j = 0; j < nb_usb_enumerators; j++) {
+					if (strcmp(usb_enumerator[j], enumerator) == 0)
+						break;
+				}
+				if (j == nb_usb_enumerators) {
+					usbi_dbg("found new PnP enumerator string '%s'", enumerator);
+					if (nb_usb_enumerators < ARRAYSIZE(usb_enumerator)) {
+						usb_enumerator[nb_usb_enumerators] = _strdup(enumerator);
+						if (usb_enumerator[nb_usb_enumerators] != NULL) {
+							nb_usb_enumerators++;
+						} else {
+							usbi_err(ctx, "could not allocate enumerator string '%s'", enumerator);
+							LOOP_BREAK(LIBUSB_ERROR_NO_MEM);
+						}
+					} else {
+						usbi_warn(ctx, "too many enumerator strings, some devices may not be accessible");
+					}
+				}
+				break;
+			case GEN_PASS:
+				// We use the GEN pass to detect driverless devices...
+				if (!pSetupDiGetDeviceRegistryPropertyA(*dev_info, &dev_info_data, SPDRP_DRIVER,
+					NULL, NULL, 0, NULL) && (GetLastError() != ERROR_INSUFFICIENT_BUFFER)) {
+					usbi_info(ctx, "The following device has no driver: '%s'", dev_id);
+					usbi_info(ctx, "libusb will not be able to access it");
+				}
+				// ...and to add the additional device interface GUIDs
+				key = pSetupDiOpenDevRegKey(*dev_info, &dev_info_data, DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ);
+				if (key == INVALID_HANDLE_VALUE)
+					break;
+				// Look for both DeviceInterfaceGUIDs *and* DeviceInterfaceGUID, in that order
+				size = sizeof(guid_string_w);
+				s = pRegQueryValueExW(key, L"DeviceInterfaceGUIDs", NULL, &reg_type,
+					(LPBYTE)guid_string_w, &size);
+				if (s == ERROR_FILE_NOT_FOUND)
+					s = pRegQueryValueExW(key, L"DeviceInterfaceGUID", NULL, &reg_type,
+						(LPBYTE)guid_string_w, &size);
+				pRegCloseKey(key);
+				if ((s == ERROR_SUCCESS) &&
+				    (((reg_type == REG_SZ) && (size == (sizeof(guid_string_w) - sizeof(WCHAR)))) ||
+				     ((reg_type == REG_MULTI_SZ) && (size == sizeof(guid_string_w))))) {
+					if (nb_guids == guid_size) {
+						new_guid_list = realloc((void *)guid_list, (guid_size + GUID_SIZE_STEP) * sizeof(void *));
+						if (new_guid_list == NULL) {
+							usbi_err(ctx, "failed to realloc guid list");
+							LOOP_BREAK(LIBUSB_ERROR_NO_MEM);
+						}
+						guid_list = new_guid_list;
+						guid_size += GUID_SIZE_STEP;
+					}
+					if_guid = malloc(sizeof(*if_guid));
+					if (if_guid == NULL) {
+						usbi_err(ctx, "failed to alloc if_guid");
+						LOOP_BREAK(LIBUSB_ERROR_NO_MEM);
+					}
+					if (pIIDFromString(guid_string_w, if_guid) != 0) {
+						usbi_warn(ctx, "device '%s' has malformed DeviceInterfaceGUID string, skipping", dev_id);
+						free(if_guid);
+					} else {
+						// Check if we've already seen this GUID
+						for (j = EXT_PASS; j < nb_guids; j++) {
+							if (memcmp(guid_list[j], if_guid, sizeof(*if_guid)) == 0)
+								break;
+						}
+						if (j == nb_guids) {
+							usbi_dbg("extra GUID: %s", guid_to_string(if_guid));
+							guid_list[nb_guids++] = if_guid;
+						} else {
+							// Duplicate, ignore
+							free(if_guid);
+						}
+					}
+				} else if (s == ERROR_SUCCESS) {
+					usbi_warn(ctx, "unexpected type/size of DeviceInterfaceGUID for '%s'", dev_id);
+				}
+				break;
+			case HID_PASS:
+				api = USB_API_HID;
+				break;
+			default:
+				// Get the API type (after checking that the driver installation is OK)
+				if ((!pSetupDiGetDeviceRegistryPropertyA(*dev_info, &dev_info_data, SPDRP_INSTALL_STATE,
+					NULL, (PBYTE)&install_state, sizeof(install_state), &size)) || (size != sizeof(install_state))) {
+					usbi_warn(ctx, "could not detect installation state of driver for '%s': %s",
+						dev_id, windows_error_str(0));
+				} else if (install_state != 0) {
+					usbi_warn(ctx, "driver for device '%s' is reporting an issue (code: %u) - skipping",
+						dev_id, (unsigned int)install_state);
+					continue;
+				}
+				get_api_type(ctx, dev_info, &dev_info_data, &api, &sub_api);
+				break;
+			}
+
+			// Find parent device (for the passes that need it)
+			if (pass >= GEN_PASS) {
+				parent_dev = get_ancestor(ctx, dev_info_data.DevInst, NULL);
+				if (parent_dev == NULL) {
+					// Root hubs will not have a parent
+					dev = usbi_get_device_by_session_id(ctx, (unsigned long)dev_info_data.DevInst);
+					if (dev != NULL) {
+						priv = _device_priv(dev);
+						if (priv->root_hub)
+							goto track_unref;
+						libusb_unref_device(dev);
+					}
+
+					usbi_dbg("unlisted ancestor for '%s' (non USB HID, newly connected, etc.) - ignoring", dev_id);
+					continue;
+				}
+
+				parent_priv = _device_priv(parent_dev);
+				// virtual USB devices are also listed during GEN - don't process these yet
+				if ((pass == GEN_PASS) && (parent_priv->apib->id != USB_API_HUB)) {
+					libusb_unref_device(parent_dev);
+					continue;
+				}
+			}
+
+			// Create new or match existing device, using the devInst as session id
+			if ((pass <= GEN_PASS) && (pass != HCD_PASS)) {	// For subsequent passes, we'll lookup the parent
+				// These are the passes that create "new" devices
+				session_id = (unsigned long)dev_info_data.DevInst;
+				dev = usbi_get_device_by_session_id(ctx, session_id);
+				if (dev == NULL) {
+				alloc_device:
+					usbi_dbg("allocating new device for session [%lX]", session_id);
+					dev = usbi_alloc_device(ctx, session_id);
+					if (dev == NULL)
+						LOOP_BREAK(LIBUSB_ERROR_NO_MEM);
+
+					priv = winusb_device_priv_init(dev);
+					priv->dev_id = _strdup(dev_id);
+					if (priv->dev_id == NULL) {
+						libusb_unref_device(dev);
+						LOOP_BREAK(LIBUSB_ERROR_NO_MEM);
+					}
+				} else {
+					usbi_dbg("found existing device for session [%lX]", session_id);
+
+					priv = _device_priv(dev);
+					if (strcmp(priv->dev_id, dev_id) != 0) {
+						usbi_dbg("device instance ID for session [%lX] changed", session_id);
+						usbi_disconnect_device(dev);
+						libusb_unref_device(dev);
+						goto alloc_device;
+					}
+				}
+
+			track_unref:
+				// Keep track of devices that need unref
+				if (unref_cur == unref_size) {
+					new_unref_list = realloc(unref_list, (unref_size + UNREF_SIZE_STEP) * sizeof(void *));
+					if (new_unref_list == NULL) {
+						usbi_err(ctx, "could not realloc list for unref - aborting");
+						LOOP_BREAK(LIBUSB_ERROR_NO_MEM);
+					}
+					unref_list = new_unref_list;
+					unref_size += UNREF_SIZE_STEP;
+				}
+				unref_list[unref_cur++] = dev;
+			}
+
+			// Setup device
+			switch (pass) {
+			case HUB_PASS:
+			case DEV_PASS:
+				// If the device has already been setup, don't do it again
+				if (priv->path != NULL)
+					break;
+				// Take care of API initialization
+				priv->path = dev_interface_path;
+				dev_interface_path = NULL;
+				priv->apib = &usb_api_backend[api];
+				priv->sub_api = sub_api;
+				switch (api) {
+				case USB_API_COMPOSITE:
+				case USB_API_HUB:
+					break;
+				case USB_API_HID:
+					priv->hid = calloc(1, sizeof(struct hid_device_priv));
+					if (priv->hid == NULL)
+						LOOP_BREAK(LIBUSB_ERROR_NO_MEM);
+					break;
+				default:
+					// For other devices, the first interface is the same as the device
+					priv->usb_interface[0].path = _strdup(priv->path);
+					if (priv->usb_interface[0].path == NULL)
+						LOOP_BREAK(LIBUSB_ERROR_NO_MEM);
+					// The following is needed if we want API calls to work for both simple
+					// and composite devices.
+					for (j = 0; j < USB_MAXINTERFACES; j++)
+						priv->usb_interface[j].apib = &usb_api_backend[api];
+					break;
+				}
+				break;
+			case HCD_PASS:
+				r = enumerate_hcd_root_hub(ctx, dev_id, (uint8_t)(i + 1), dev_info_data.DevInst);
+				break;
+			case GEN_PASS:
+				// The SPDRP_ADDRESS for USB devices is the device port number on the hub
+				port_nr = 0;
+				if (!pSetupDiGetDeviceRegistryPropertyA(*dev_info, &dev_info_data, SPDRP_ADDRESS,
+						NULL, (PBYTE)&port_nr, sizeof(port_nr), &size) || (size != sizeof(port_nr)))
+					usbi_warn(ctx, "could not retrieve port number for device '%s': %s", dev_id, windows_error_str(0));
+				r = init_device(dev, parent_dev, (uint8_t)port_nr, dev_info_data.DevInst);
+				if (r == LIBUSB_SUCCESS) {
+					// Append device to the list of discovered devices
+					discdevs = discovered_devs_append(*_discdevs, dev);
+					if (!discdevs)
+						LOOP_BREAK(LIBUSB_ERROR_NO_MEM);
+
+					*_discdevs = discdevs;
+				} else if (r == LIBUSB_ERROR_NO_DEVICE) {
+					// This can occur if the device was disconnected but Windows hasn't
+					// refreshed its enumeration yet - in that case, we ignore the device
+					r = LIBUSB_SUCCESS;
+				}
+				break;
+			default: // HID_PASS and later
+				if (parent_priv->apib->id == USB_API_HID || parent_priv->apib->id == USB_API_COMPOSITE) {
+					if (parent_priv->apib->id == USB_API_HID) {
+						usbi_dbg("setting HID interface for [%lX]:", parent_dev->session_data);
+						r = set_hid_interface(ctx, parent_dev, dev_interface_path);
+					} else {
+						usbi_dbg("setting composite interface for [%lX]:", parent_dev->session_data);
+						r = set_composite_interface(ctx, parent_dev, dev_interface_path, dev_id, api, sub_api);
+					}
+					switch (r) {
+					case LIBUSB_SUCCESS:
+						dev_interface_path = NULL;
+						break;
+					case LIBUSB_ERROR_ACCESS:
+						// interface has already been set => make sure dev_interface_path is freed then
+						r = LIBUSB_SUCCESS;
+						break;
+					default:
+						LOOP_BREAK(r);
+						break;
+					}
+				}
+				libusb_unref_device(parent_dev);
+				break;
+			}
+		}
+	}
+
+	pSetupDiDestroyDeviceInfoList(dev_info_intf);
+
+	// Free any additional GUIDs
+	for (pass = EXT_PASS; pass < nb_guids; pass++)
+		free((void *)guid_list[pass]);
+	free((void *)guid_list);
+
+	// Free any PnP enumerator strings
+	for (i = 1; i < nb_usb_enumerators; i++)
+		free(usb_enumerator[i]);
+
+	// Unref newly allocated devs
+	for (i = 0; i < unref_cur; i++)
+		libusb_unref_device(unref_list[i]);
+	free(unref_list);
+
+	return r;
+}
+
+static int winusb_get_device_descriptor(struct libusb_device *dev, unsigned char *buffer)
+{
+	struct winusb_device_priv *priv = _device_priv(dev);
+
+	memcpy(buffer, &priv->dev_descriptor, DEVICE_DESC_LENGTH);
+	return LIBUSB_SUCCESS;
+}
+
+static int winusb_get_config_descriptor(struct libusb_device *dev, uint8_t config_index, unsigned char *buffer, size_t len)
+{
+	struct winusb_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 = priv->config_descriptor[config_index];
+
+	size = MIN(config_header->wTotalLength, len);
+	memcpy(buffer, priv->config_descriptor[config_index], size);
+	return (int)size;
+}
+
+static int winusb_get_config_descriptor_by_value(struct libusb_device *dev, uint8_t bConfigurationValue,
+	unsigned char **buffer)
+{
+	struct winusb_device_priv *priv = _device_priv(dev);
+	PUSB_CONFIGURATION_DESCRIPTOR config_header;
+	uint8_t index;
+
+	if (priv->config_descriptor == NULL)
+		return LIBUSB_ERROR_NOT_FOUND;
+
+	for (index = 0; index < dev->num_configurations; index++) {
+		config_header = priv->config_descriptor[index];
+		if (config_header == NULL)
+			continue;
+		if (config_header->bConfigurationValue == bConfigurationValue) {
+			*buffer = (unsigned char *)priv->config_descriptor[index];
+			return (int)config_header->wTotalLength;
+		}
+	}
+
+	return LIBUSB_ERROR_NOT_FOUND;
+}
+
+/*
+ * return the cached copy of the active config descriptor
+ */
+static int winusb_get_active_config_descriptor(struct libusb_device *dev, unsigned char *buffer, size_t len)
+{
+	struct winusb_device_priv *priv = _device_priv(dev);
+	unsigned char *config_desc;
+	int r;
+
+	if (priv->active_config == 0)
+		return LIBUSB_ERROR_NOT_FOUND;
+
+	r = winusb_get_config_descriptor_by_value(dev, priv->active_config, &config_desc);
+	if (r < 0)
+		return r;
+
+	len = MIN((size_t)r, len);
+	memcpy(buffer, config_desc, len);
+	return (int)len;
+}
+
+static int winusb_open(struct libusb_device_handle *dev_handle)
+{
+	struct winusb_device_priv *priv = _device_priv(dev_handle->dev);
+
+	CHECK_SUPPORTED_API(priv->apib, open);
+
+	return priv->apib->open(SUB_API_NOTSET, dev_handle);
+}
+
+static void winusb_close(struct libusb_device_handle *dev_handle)
+{
+	struct winusb_device_priv *priv = _device_priv(dev_handle->dev);
+
+	if (priv->apib->close)
+		priv->apib->close(SUB_API_NOTSET, dev_handle);
+}
+
+static int winusb_get_configuration(struct libusb_device_handle *dev_handle, int *config)
+{
+	struct winusb_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 winusb_set_configuration(struct libusb_device_handle *dev_handle, int config)
+{
+	struct winusb_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 winusb_claim_interface(struct libusb_device_handle *dev_handle, int iface)
+{
+	struct winusb_device_priv *priv = _device_priv(dev_handle->dev);
+	int r;
+
+	CHECK_SUPPORTED_API(priv->apib, claim_interface);
+
+	safe_free(priv->usb_interface[iface].endpoint);
+	priv->usb_interface[iface].nb_endpoints = 0;
+
+	r = priv->apib->claim_interface(SUB_API_NOTSET, dev_handle, iface);
+
+	if (r == LIBUSB_SUCCESS)
+		r = windows_assign_endpoints(dev_handle, iface, 0);
+
+	return r;
+}
+
+static int winusb_set_interface_altsetting(struct libusb_device_handle *dev_handle, int iface, int altsetting)
+{
+	struct winusb_device_priv *priv = _device_priv(dev_handle->dev);
+	int r;
+
+	CHECK_SUPPORTED_API(priv->apib, set_interface_altsetting);
+
+	safe_free(priv->usb_interface[iface].endpoint);
+	priv->usb_interface[iface].nb_endpoints = 0;
+
+	r = priv->apib->set_interface_altsetting(SUB_API_NOTSET, dev_handle, iface, altsetting);
+
+	if (r == LIBUSB_SUCCESS)
+		r = windows_assign_endpoints(dev_handle, iface, altsetting);
+
+	return r;
+}
+
+static int winusb_release_interface(struct libusb_device_handle *dev_handle, int iface)
+{
+	struct winusb_device_priv *priv = _device_priv(dev_handle->dev);
+
+	CHECK_SUPPORTED_API(priv->apib, release_interface);
+
+	return priv->apib->release_interface(SUB_API_NOTSET, dev_handle, iface);
+}
+
+static int winusb_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint)
+{
+	struct winusb_device_priv *priv = _device_priv(dev_handle->dev);
+
+	CHECK_SUPPORTED_API(priv->apib, clear_halt);
+
+	return priv->apib->clear_halt(SUB_API_NOTSET, dev_handle, endpoint);
+}
+
+static int winusb_reset_device(struct libusb_device_handle *dev_handle)
+{
+	struct winusb_device_priv *priv = _device_priv(dev_handle->dev);
+
+	CHECK_SUPPORTED_API(priv->apib, reset_device);
+
+	return priv->apib->reset_device(SUB_API_NOTSET, dev_handle);
+}
+
+static void winusb_destroy_device(struct libusb_device *dev)
+{
+	winusb_device_priv_release(dev);
+}
+
+static void winusb_clear_transfer_priv(struct usbi_transfer *itransfer)
+{
+	struct winusb_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
+
+	usbi_close(transfer_priv->pollable_fd.fd);
+	transfer_priv->pollable_fd = INVALID_WINFD;
+	transfer_priv->handle = NULL;
+	safe_free(transfer_priv->hid_buffer);
+	safe_free(transfer_priv->iso_context);
+
+	// When auto claim is in use, attempt to release the auto-claimed interface
+	auto_release(itransfer);
+}
+
+static int do_submit_transfer(struct usbi_transfer *itransfer, short events,
+	int (*transfer_fn)(int, struct usbi_transfer *))
+{
+	struct libusb_context *ctx = ITRANSFER_CTX(itransfer);
+	struct winusb_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
+	struct winfd wfd;
+	int r;
+
+	wfd = usbi_create_fd();
+	if (wfd.fd < 0)
+		return LIBUSB_ERROR_NO_MEM;
+
+	r = usbi_add_pollfd(ctx, wfd.fd, events);
+	if (r) {
+		usbi_close(wfd.fd);
+		return r;
+	}
+
+	// Use transfer_priv to store data needed for async polling
+	transfer_priv->pollable_fd = wfd;
+
+	r = transfer_fn(SUB_API_NOTSET, itransfer);
+
+	if ((r != LIBUSB_SUCCESS) && (r != LIBUSB_ERROR_OVERFLOW)) {
+		usbi_remove_pollfd(ctx, wfd.fd);
+		usbi_close(wfd.fd);
+		transfer_priv->pollable_fd = INVALID_WINFD;
+	}
+
+	return r;
+}
+
+static int winusb_submit_transfer(struct usbi_transfer *itransfer)
+{
+	struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+	struct winusb_device_priv *priv = _device_priv(transfer->dev_handle->dev);
+	int (*transfer_fn)(int, struct usbi_transfer *);
+	short events;
+
+	switch (transfer->type) {
+	case LIBUSB_TRANSFER_TYPE_CONTROL:
+		events = (transfer->buffer[0] & LIBUSB_ENDPOINT_IN) ? POLLIN : POLLOUT;
+		transfer_fn = priv->apib->submit_control_transfer;
+		break;
+	case LIBUSB_TRANSFER_TYPE_BULK:
+	case LIBUSB_TRANSFER_TYPE_INTERRUPT:
+		if (IS_XFEROUT(transfer) && (transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET))
+			return LIBUSB_ERROR_NOT_SUPPORTED;
+		events = IS_XFERIN(transfer) ? POLLIN : POLLOUT;
+		transfer_fn = priv->apib->submit_bulk_transfer;
+		break;
+	case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
+		events = IS_XFERIN(transfer) ? POLLIN : POLLOUT;
+		transfer_fn = priv->apib->submit_iso_transfer;
+		break;
+	case LIBUSB_TRANSFER_TYPE_BULK_STREAM:
+		return LIBUSB_ERROR_NOT_SUPPORTED;
+	default:
+		usbi_err(TRANSFER_CTX(transfer), "unknown endpoint type %d", transfer->type);
+		return LIBUSB_ERROR_INVALID_PARAM;
+	}
+
+	if (transfer_fn == NULL) {
+		usbi_warn(TRANSFER_CTX(transfer),
+			"unsupported transfer type %d (unrecognized device driver)",
+			transfer->type);
+		return LIBUSB_ERROR_NOT_SUPPORTED;
+	}
+
+	return do_submit_transfer(itransfer, events, transfer_fn);
+}
+
+static int windows_abort_control(struct usbi_transfer *itransfer)
+{
+	struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+	struct winusb_device_priv *priv = _device_priv(transfer->dev_handle->dev);
+
+	CHECK_SUPPORTED_API(priv->apib, abort_control);
+
+	return priv->apib->abort_control(SUB_API_NOTSET, itransfer);
+}
+
+static int windows_abort_transfers(struct usbi_transfer *itransfer)
+{
+	struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+	struct winusb_device_priv *priv = _device_priv(transfer->dev_handle->dev);
+
+	CHECK_SUPPORTED_API(priv->apib, abort_transfers);
+
+	return priv->apib->abort_transfers(SUB_API_NOTSET, itransfer);
+}
+
+static int winusb_cancel_transfer(struct usbi_transfer *itransfer)
+{
+	struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+
+	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);
+	case LIBUSB_TRANSFER_TYPE_BULK_STREAM:
+		return LIBUSB_ERROR_NOT_SUPPORTED;
+	default:
+		usbi_err(ITRANSFER_CTX(itransfer), "unknown endpoint type %d", transfer->type);
+		return LIBUSB_ERROR_INVALID_PARAM;
+	}
+}
+
+static int winusb_copy_transfer_data(struct usbi_transfer *itransfer, uint32_t io_size)
+{
+	struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+	struct winusb_device_priv *priv = _device_priv(transfer->dev_handle->dev);
+	return priv->apib->copy_transfer_data(SUB_API_NOTSET, itransfer, io_size);
+}
+
+static int winusb_get_transfer_fd(struct usbi_transfer *itransfer)
+{
+	struct winusb_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
+	return transfer_priv->pollable_fd.fd;
+}
+
+static void winusb_get_overlapped_result(struct usbi_transfer *itransfer,
+	DWORD *io_result, DWORD *io_size)
+{
+	struct winusb_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
+	struct winfd *pollable_fd = &transfer_priv->pollable_fd;
+
+	if (HasOverlappedIoCompletedSync(pollable_fd->overlapped)) {
+		*io_result = NO_ERROR;
+		*io_size = (DWORD)pollable_fd->overlapped->InternalHigh;
+	} else if (GetOverlappedResult(transfer_priv->handle, pollable_fd->overlapped, io_size, FALSE)) {
+		// Regular async overlapped
+		*io_result = NO_ERROR;
+	} else {
+		*io_result = GetLastError();
+	}
+}
+
+// NB: MSVC6 does not support named initializers.
+const struct windows_backend winusb_backend = {
+	winusb_init,
+	winusb_exit,
+	winusb_get_device_list,
+	winusb_open,
+	winusb_close,
+	winusb_get_device_descriptor,
+	winusb_get_active_config_descriptor,
+	winusb_get_config_descriptor,
+	winusb_get_config_descriptor_by_value,
+	winusb_get_configuration,
+	winusb_set_configuration,
+	winusb_claim_interface,
+	winusb_release_interface,
+	winusb_set_interface_altsetting,
+	winusb_clear_halt,
+	winusb_reset_device,
+	winusb_destroy_device,
+	winusb_submit_transfer,
+	winusb_cancel_transfer,
+	winusb_clear_transfer_priv,
+	winusb_copy_transfer_data,
+	winusb_get_transfer_fd,
+	winusb_get_overlapped_result,
+};
+
+/*
+ * USB API backends
+ */
+
+static const char * const composite_driver_names[] = {"USBCCGP"};
+static const char * const winusbx_driver_names[] = {"libusbK", "libusb0", "WinUSB"};
+static const char * const hid_driver_names[] = {"HIDUSB", "MOUHID", "KBDHID"};
+const struct windows_usb_api_backend usb_api_backend[USB_API_MAX] = {
+	{
+		USB_API_UNSUPPORTED,
+		"Unsupported API",
+		// No supported operations
+	},
+	{
+		USB_API_HUB,
+		"HUB API",
+		// No supported operations
+	},
+	{
+		USB_API_COMPOSITE,
+		"Composite API",
+		composite_driver_names,
+		ARRAYSIZE(composite_driver_names),
+		NULL,	/* init */
+		NULL,	/* exit */
+		composite_open,
+		composite_close,
+		NULL,	/* configure_endpoints */
+		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_WINUSBX,
+		"WinUSB-like APIs",
+		winusbx_driver_names,
+		ARRAYSIZE(winusbx_driver_names),
+		winusbx_init,
+		winusbx_exit,
+		winusbx_open,
+		winusbx_close,
+		winusbx_configure_endpoints,
+		winusbx_claim_interface,
+		winusbx_set_interface_altsetting,
+		winusbx_release_interface,
+		winusbx_clear_halt,
+		winusbx_reset_device,
+		winusbx_submit_bulk_transfer,
+		winusbx_submit_iso_transfer,
+		winusbx_submit_control_transfer,
+		winusbx_abort_control,
+		winusbx_abort_transfers,
+		winusbx_copy_transfer_data,
+	},
+	{
+		USB_API_HID,
+		"HID API",
+		// No supported operations
+	},
+};
+
+
+/*
+ * WinUSB-like (WinUSB, libusb0/libusbK through libusbk DLL) API functions
+ */
+#define WinUSBX_Set(fn)										\
+	do {											\
+		if (native_winusb)								\
+			WinUSBX[i].fn = (WinUsb_##fn##_t)GetProcAddress(h, "WinUsb_" #fn);	\
+		else										\
+			pLibK_GetProcAddress((PVOID *)&WinUSBX[i].fn, i, KUSB_FNID_##fn);	\
+	} while (0)
+
+static int winusbx_init(struct libusb_context *ctx)
+{
+	HMODULE h;
+	bool native_winusb;
+	int i;
+	KLIB_VERSION LibK_Version;
+	LibK_GetProcAddress_t pLibK_GetProcAddress = NULL;
+	LibK_GetVersion_t pLibK_GetVersion;
+
+	h = LoadLibraryA("libusbK");
+
+	if (h == NULL) {
+		usbi_info(ctx, "libusbK DLL is not available, will use native WinUSB");
+		h = LoadLibraryA("WinUSB");
+
+		if (h == NULL) {
+			usbi_warn(ctx, "WinUSB DLL is not available either, "
+				"you will not be able to access devices outside of enumeration");
+			return LIBUSB_ERROR_NOT_FOUND;
+		}
+	} else {
+		usbi_dbg("using libusbK DLL for universal access");
+		pLibK_GetVersion = (LibK_GetVersion_t)GetProcAddress(h, "LibK_GetVersion");
+		if (pLibK_GetVersion != NULL) {
+			pLibK_GetVersion(&LibK_Version);
+			usbi_dbg("libusbK version: %d.%d.%d.%d", LibK_Version.Major, LibK_Version.Minor,
+				LibK_Version.Micro, LibK_Version.Nano);
+		}
+		pLibK_GetProcAddress = (LibK_GetProcAddress_t)GetProcAddress(h, "LibK_GetProcAddress");
+		if (pLibK_GetProcAddress == NULL) {
+			usbi_err(ctx, "LibK_GetProcAddress() not found in libusbK DLL");
+			FreeLibrary(h);
+			return LIBUSB_ERROR_NOT_FOUND;
+		}
+	}
+
+	native_winusb = (pLibK_GetProcAddress == NULL);
+	for (i = 0; i < SUB_API_MAX; i++) {
+		WinUSBX_Set(AbortPipe);
+		WinUSBX_Set(ControlTransfer);
+		WinUSBX_Set(FlushPipe);
+		WinUSBX_Set(Free);
+		WinUSBX_Set(GetAssociatedInterface);
+		WinUSBX_Set(Initialize);
+		WinUSBX_Set(ReadPipe);
+		if (!native_winusb)
+			WinUSBX_Set(ResetDevice);
+		WinUSBX_Set(ResetPipe);
+		WinUSBX_Set(SetCurrentAlternateSetting);
+		WinUSBX_Set(SetPipePolicy);
+		WinUSBX_Set(WritePipe);
+		WinUSBX_Set(IsoReadPipe);
+		WinUSBX_Set(IsoWritePipe);
+
+		if (WinUSBX[i].Initialize != NULL) {
+			WinUSBX[i].initialized = true;
+			// Assume driver supports CancelIoEx() if it is available
+			WinUSBX[i].CancelIoEx_supported = (pCancelIoEx != NULL);
+			usbi_dbg("initalized sub API %s", winusbx_driver_names[i]);
+		} else {
+			usbi_warn(ctx, "Failed to initalize sub API %s", winusbx_driver_names[i]);
+			WinUSBX[i].initialized = false;
+		}
+	}
+
+	WinUSBX_handle = h;
+	return LIBUSB_SUCCESS;
+}
+
+static void winusbx_exit(void)
+{
+	if (WinUSBX_handle != NULL) {
+		FreeLibrary(WinUSBX_handle);
+		WinUSBX_handle = NULL;
+
+		/* Reset the WinUSBX API structures */
+		memset(&WinUSBX, 0, sizeof(WinUSBX));
+	}
+}
+
+// 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 winusbx_open(int sub_api, struct libusb_device_handle *dev_handle)
+{
+	struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev);
+	struct winusb_device_priv *priv = _device_priv(dev_handle->dev);
+	struct winusb_device_handle_priv *handle_priv = _device_handle_priv(dev_handle);
+	HANDLE file_handle;
+	int i;
+
+	CHECK_WINUSBX_AVAILABLE(sub_api);
+
+	// WinUSB requires a separate 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_WINUSBX)) {
+			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->usb_interface[i].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 winusbx_close(int sub_api, struct libusb_device_handle *dev_handle)
+{
+	struct winusb_device_handle_priv *handle_priv = _device_handle_priv(dev_handle);
+	struct winusb_device_priv *priv = _device_priv(dev_handle->dev);
+	HANDLE handle;
+	int i;
+
+	if (sub_api == SUB_API_NOTSET)
+		sub_api = priv->sub_api;
+
+	if (!WinUSBX[sub_api].initialized)
+		return;
+
+	if (priv->apib->id == USB_API_COMPOSITE) {
+		// If this is a composite device, just free and close all WinUSB-like
+		// interfaces directly (each is independent and not associated with another)
+		for (i = 0; i < USB_MAXINTERFACES; i++) {
+			if (priv->usb_interface[i].apib->id == USB_API_WINUSBX) {
+				handle = handle_priv->interface_handle[i].api_handle;
+				if (HANDLE_VALID(handle))
+					WinUSBX[sub_api].Free(handle);
+
+				handle = handle_priv->interface_handle[i].dev_handle;
+				if (HANDLE_VALID(handle))
+					CloseHandle(handle);
+			}
+		}
+	} else {
+		// If this is a WinUSB device, free all interfaces above interface 0,
+		// then free and close interface 0 last
+		for (i = 1; i < USB_MAXINTERFACES; i++) {
+			handle = handle_priv->interface_handle[i].api_handle;
+			if (HANDLE_VALID(handle))
+				WinUSBX[sub_api].Free(handle);
+		}
+		handle = handle_priv->interface_handle[0].api_handle;
+		if (HANDLE_VALID(handle))
+			WinUSBX[sub_api].Free(handle);
+
+		handle = handle_priv->interface_handle[0].dev_handle;
+		if (HANDLE_VALID(handle))
+			CloseHandle(handle);
+	}
+}
+
+static int winusbx_configure_endpoints(int sub_api, struct libusb_device_handle *dev_handle, int iface)
+{
+	struct winusb_device_handle_priv *handle_priv = _device_handle_priv(dev_handle);
+	struct winusb_device_priv *priv = _device_priv(dev_handle->dev);
+	HANDLE winusb_handle = handle_priv->interface_handle[iface].api_handle;
+	UCHAR policy;
+	ULONG timeout = 0;
+	uint8_t endpoint_address;
+	int i;
+
+	CHECK_WINUSBX_AVAILABLE(sub_api);
+
+	// With handle and enpoints set (in parent), we can setup the default pipe properties
+	// see http://download.microsoft.com/download/D/1/D/D1DD7745-426B-4CC3-A269-ABBBE427C0EF/DVC-T705_DDC08.pptx
+	for (i = -1; i < priv->usb_interface[iface].nb_endpoints; i++) {
+		endpoint_address = (i == -1) ? 0 : priv->usb_interface[iface].endpoint[i];
+		if (!WinUSBX[sub_api].SetPipePolicy(winusb_handle, endpoint_address,
+			PIPE_TRANSFER_TIMEOUT, sizeof(ULONG), &timeout))
+			usbi_dbg("failed to set PIPE_TRANSFER_TIMEOUT for control endpoint %02X", endpoint_address);
+
+		if ((i == -1) || (sub_api == SUB_API_LIBUSB0))
+			continue; // Other policies don't apply to control endpoint or libusb0
+
+		policy = false;
+		if (!WinUSBX[sub_api].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 (!WinUSBX[sub_api].SetPipePolicy(winusb_handle, endpoint_address,
+			IGNORE_SHORT_PACKETS, sizeof(UCHAR), &policy))
+			usbi_dbg("failed to disable IGNORE_SHORT_PACKETS for endpoint %02X", endpoint_address);
+
+		policy = true;
+		/* ALLOW_PARTIAL_READS must be enabled due to likely libusbK bug. See:
+		   https://sourceforge.net/mailarchive/message.php?msg_id=29736015 */
+		if (!WinUSBX[sub_api].SetPipePolicy(winusb_handle, endpoint_address,
+			ALLOW_PARTIAL_READS, sizeof(UCHAR), &policy))
+			usbi_dbg("failed to enable ALLOW_PARTIAL_READS for endpoint %02X", endpoint_address);
+
+		if (!WinUSBX[sub_api].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 winusbx_claim_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface)
+{
+	struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev);
+	struct winusb_device_handle_priv *handle_priv = _device_handle_priv(dev_handle);
+	struct winusb_device_priv *priv = _device_priv(dev_handle->dev);
+	bool is_using_usbccgp = (priv->apib->id == USB_API_COMPOSITE);
+	SP_DEVICE_INTERFACE_DETAIL_DATA_A *dev_interface_details = NULL;
+	HDEVINFO dev_info = INVALID_HANDLE_VALUE;
+	SP_DEVINFO_DATA dev_info_data;
+	char *dev_path_no_guid = NULL;
+	char filter_path[] = "\\\\.\\libusb0-0000";
+	bool found_filter = false;
+	HANDLE file_handle, winusb_handle;
+	DWORD err;
+	int i;
+
+	CHECK_WINUSBX_AVAILABLE(sub_api);
+
+	// If the device is composite, but using the default Windows composite parent driver (usbccgp)
+	// or if it's the first WinUSB-like interface, we get a handle through Initialize().
+	if ((is_using_usbccgp) || (iface == 0)) {
+		// composite device (independent interfaces) or interface 0
+		file_handle = handle_priv->interface_handle[iface].dev_handle;
+		if (!HANDLE_VALID(file_handle))
+			return LIBUSB_ERROR_NOT_FOUND;
+
+		if (!WinUSBX[sub_api].Initialize(file_handle, &winusb_handle)) {
+			handle_priv->interface_handle[iface].api_handle = INVALID_HANDLE_VALUE;
+			err = GetLastError();
+			switch (err) {
+			case ERROR_BAD_COMMAND:
+				// The device was disconnected
+				usbi_err(ctx, "could not access interface %d: %s", iface, windows_error_str(0));
+				return LIBUSB_ERROR_NO_DEVICE;
+			default:
+				// it may be that we're using the libusb0 filter driver.
+				// TODO: can we move this whole business into the K/0 DLL?
+				for (i = 0; ; i++) {
+					safe_free(dev_interface_details);
+					safe_free(dev_path_no_guid);
+
+					dev_interface_details = get_interface_details_filter(ctx, &dev_info, &dev_info_data, &GUID_DEVINTERFACE_LIBUSB0_FILTER, i, filter_path);
+					if ((found_filter) || (dev_interface_details == NULL))
+						break;
+
+					// ignore GUID part
+					dev_path_no_guid = sanitize_path(strtok(dev_interface_details->DevicePath, "{"));
+					if (dev_path_no_guid == NULL)
+						continue;
+
+					if (strncmp(dev_path_no_guid, priv->usb_interface[iface].path, strlen(dev_path_no_guid)) == 0) {
+						file_handle = CreateFileA(filter_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) {
+							if (WinUSBX[sub_api].Initialize(file_handle, &winusb_handle)) {
+								// Replace the existing file handle with the working one
+								CloseHandle(handle_priv->interface_handle[iface].dev_handle);
+								handle_priv->interface_handle[iface].dev_handle = file_handle;
+								found_filter = true;
+							} else {
+								usbi_err(ctx, "could not initialize filter driver for %s", filter_path);
+								CloseHandle(file_handle);
+							}
+						} else {
+							usbi_err(ctx, "could not open device %s: %s", filter_path, windows_error_str(0));
+						}
+					}
+				}
+				free(dev_interface_details);
+				if (!found_filter) {
+					usbi_err(ctx, "could not access interface %d: %s", iface, windows_error_str(err));
+					return LIBUSB_ERROR_ACCESS;
+				}
+			}
+		}
+		handle_priv->interface_handle[iface].api_handle = winusb_handle;
+	} else {
+		// For all other interfaces, use GetAssociatedInterface()
+		winusb_handle = handle_priv->interface_handle[0].api_handle;
+		// It is a requirement for multiple interface devices on Windows that, to you
+		// must first claim the first interface before you claim the others
+		if (!HANDLE_VALID(winusb_handle)) {
+			file_handle = handle_priv->interface_handle[0].dev_handle;
+			if (WinUSBX[sub_api].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): %s", iface, windows_error_str(0));
+				return LIBUSB_ERROR_ACCESS;
+			}
+		}
+		if (!WinUSBX[sub_api].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;
+			}
+		}
+	}
+	usbi_dbg("claimed interface %d", iface);
+	handle_priv->active_interface = iface;
+
+	return LIBUSB_SUCCESS;
+}
+
+static int winusbx_release_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface)
+{
+	struct winusb_device_handle_priv *handle_priv = _device_handle_priv(dev_handle);
+	struct winusb_device_priv *priv = _device_priv(dev_handle->dev);
+	HANDLE winusb_handle;
+
+	CHECK_WINUSBX_AVAILABLE(sub_api);
+
+	winusb_handle = handle_priv->interface_handle[iface].api_handle;
+	if (!HANDLE_VALID(winusb_handle))
+		return LIBUSB_ERROR_NOT_FOUND;
+
+	WinUSBX[sub_api].Free(winusb_handle);
+	handle_priv->interface_handle[iface].api_handle = INVALID_HANDLE_VALUE;
+
+	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 winusb_device_handle_priv *handle_priv = _device_handle_priv(dev_handle);
+	struct winusb_device_priv *priv = _device_priv(dev_handle->dev);
+	int i;
+
+	if ((api_id < USB_API_WINUSBX) || (api_id > USB_API_HID)) {
+		usbi_dbg("unsupported API ID");
+		return -1;
+	}
+
+	for (i = 0; i < USB_MAXINTERFACES; i++) {
+		if (HANDLE_VALID(handle_priv->interface_handle[i].dev_handle)
+				&& HANDLE_VALID(handle_priv->interface_handle[i].api_handle)
+				&& (priv->usb_interface[i].apib->id == api_id))
+			return i;
+	}
+
+	return -1;
+}
+
+/*
+ * Lookup interface by endpoint address. -1 if not found
+ */
+static int interface_by_endpoint(struct winusb_device_priv *priv,
+	struct winusb_device_handle_priv *handle_priv, uint8_t endpoint_address)
+{
+	int i, j;
+
+	for (i = 0; i < USB_MAXINTERFACES; i++) {
+		if (!HANDLE_VALID(handle_priv->interface_handle[i].api_handle))
+			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 winusbx_submit_control_transfer(int sub_api, 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 winusb_device_priv *priv = _device_priv(transfer->dev_handle->dev);
+	struct winusb_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
+	struct winusb_device_handle_priv *handle_priv = _device_handle_priv(transfer->dev_handle);
+	PWINUSB_SETUP_PACKET setup = (PWINUSB_SETUP_PACKET)transfer->buffer;
+	ULONG size;
+	HANDLE winusb_handle;
+	OVERLAPPED *overlapped;
+	int current_interface;
+
+	CHECK_WINUSBX_AVAILABLE(sub_api);
+
+	size = transfer->length - LIBUSB_CONTROL_SETUP_SIZE;
+
+	// Windows places upper limits on the control transfer size
+	// See: https://msdn.microsoft.com/en-us/library/windows/hardware/ff538112.aspx
+	if (size > MAX_CTRL_BUFFER_LENGTH)
+		return LIBUSB_ERROR_INVALID_PARAM;
+
+	current_interface = get_valid_interface(transfer->dev_handle, USB_API_WINUSBX);
+	if (current_interface < 0) {
+		if (auto_claim(transfer, &current_interface, USB_API_WINUSBX) != LIBUSB_SUCCESS)
+			return LIBUSB_ERROR_NOT_FOUND;
+	}
+
+	usbi_dbg("will use interface %d", current_interface);
+
+	transfer_priv->handle = winusb_handle = handle_priv->interface_handle[current_interface].api_handle;
+	overlapped = transfer_priv->pollable_fd.overlapped;
+
+	// Sending of set configuration control requests from WinUSB creates issues
+	if ((LIBUSB_REQ_TYPE(setup->RequestType) == LIBUSB_REQUEST_TYPE_STANDARD)
+			&& (setup->Request == LIBUSB_REQUEST_SET_CONFIGURATION)) {
+		if (setup->Value != priv->active_config) {
+			usbi_warn(ctx, "cannot set configuration other than the default one");
+			return LIBUSB_ERROR_INVALID_PARAM;
+		}
+		windows_force_sync_completion(overlapped, 0);
+	} else {
+		if (!WinUSBX[sub_api].ControlTransfer(winusb_handle, *setup, transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE, size, NULL, overlapped)) {
+			if (GetLastError() != ERROR_IO_PENDING) {
+				usbi_warn(ctx, "ControlTransfer failed: %s", windows_error_str(0));
+				return LIBUSB_ERROR_IO;
+			}
+		} else {
+			windows_force_sync_completion(overlapped, size);
+		}
+	}
+
+	transfer_priv->interface_number = (uint8_t)current_interface;
+
+	return LIBUSB_SUCCESS;
+}
+
+static int winusbx_set_interface_altsetting(int sub_api, struct libusb_device_handle *dev_handle, int iface, int altsetting)
+{
+	struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev);
+	struct winusb_device_handle_priv *handle_priv = _device_handle_priv(dev_handle);
+	struct winusb_device_priv *priv = _device_priv(dev_handle->dev);
+	HANDLE winusb_handle;
+
+	CHECK_WINUSBX_AVAILABLE(sub_api);
+
+	if (altsetting > 255)
+		return LIBUSB_ERROR_INVALID_PARAM;
+
+	winusb_handle = handle_priv->interface_handle[iface].api_handle;
+	if (!HANDLE_VALID(winusb_handle)) {
+		usbi_err(ctx, "interface must be claimed first");
+		return LIBUSB_ERROR_NOT_FOUND;
+	}
+
+	if (!WinUSBX[sub_api].SetCurrentAlternateSetting(winusb_handle, (UCHAR)altsetting)) {
+		usbi_err(ctx, "SetCurrentAlternateSetting failed: %s", windows_error_str(0));
+		return LIBUSB_ERROR_IO;
+	}
+
+	return LIBUSB_SUCCESS;
+}
+
+static int winusbx_submit_iso_transfer(int sub_api, 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 winusb_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
+	struct winusb_device_handle_priv *handle_priv = _device_handle_priv(transfer->dev_handle);
+	struct winusb_device_priv *priv = _device_priv(transfer->dev_handle->dev);
+	HANDLE winusb_handle;
+	OVERLAPPED *overlapped;
+	bool ret;
+	int current_interface;
+	int i;
+	UINT offset;
+	PKISO_CONTEXT iso_context;
+	size_t iso_ctx_size;
+
+	CHECK_WINUSBX_AVAILABLE(sub_api);
+
+	if ((sub_api != SUB_API_LIBUSBK) && (sub_api != SUB_API_LIBUSB0)) {
+		// iso only supported on libusbk-based backends
+		PRINT_UNSUPPORTED_API(submit_iso_transfer);
+		return LIBUSB_ERROR_NOT_SUPPORTED;
+	};
+
+	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);
+
+	transfer_priv->handle = winusb_handle = handle_priv->interface_handle[current_interface].api_handle;
+	overlapped = transfer_priv->pollable_fd.overlapped;
+
+	iso_ctx_size = sizeof(KISO_CONTEXT) + (transfer->num_iso_packets * sizeof(KISO_PACKET));
+	transfer_priv->iso_context = iso_context = calloc(1, iso_ctx_size);
+	if (transfer_priv->iso_context == NULL)
+		return LIBUSB_ERROR_NO_MEM;
+
+	// start ASAP
+	iso_context->StartFrame = 0;
+	iso_context->NumberOfPackets = (SHORT)transfer->num_iso_packets;
+
+	// convert the transfer packet lengths to iso_packet offsets
+	offset = 0;
+	for (i = 0; i < transfer->num_iso_packets; i++) {
+		iso_context->IsoPackets[i].offset = offset;
+		offset += transfer->iso_packet_desc[i].length;
+	}
+
+	if (IS_XFERIN(transfer)) {
+		usbi_dbg("reading %d iso packets", transfer->num_iso_packets);
+		ret = WinUSBX[sub_api].IsoReadPipe(winusb_handle, transfer->endpoint, transfer->buffer, transfer->length, overlapped, iso_context);
+	} else {
+		usbi_dbg("writing %d iso packets", transfer->num_iso_packets);
+		ret = WinUSBX[sub_api].IsoWritePipe(winusb_handle, transfer->endpoint, transfer->buffer, transfer->length, overlapped, iso_context);
+	}
+
+	if (!ret) {
+		if (GetLastError() != ERROR_IO_PENDING) {
+			usbi_err(ctx, "IsoReadPipe/IsoWritePipe failed: %s", windows_error_str(0));
+			return LIBUSB_ERROR_IO;
+		}
+	} else {
+		windows_force_sync_completion(overlapped, (ULONG)transfer->length);
+	}
+
+	transfer_priv->interface_number = (uint8_t)current_interface;
+
+	return LIBUSB_SUCCESS;
+}
+
+static int winusbx_submit_bulk_transfer(int sub_api, 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 winusb_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
+	struct winusb_device_handle_priv *handle_priv = _device_handle_priv(transfer->dev_handle);
+	struct winusb_device_priv *priv = _device_priv(transfer->dev_handle->dev);
+	HANDLE winusb_handle;
+	OVERLAPPED *overlapped;
+	bool ret;
+	int current_interface;
+
+	CHECK_WINUSBX_AVAILABLE(sub_api);
+
+	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);
+
+	transfer_priv->handle = winusb_handle = handle_priv->interface_handle[current_interface].api_handle;
+	overlapped = transfer_priv->pollable_fd.overlapped;
+
+	if (IS_XFERIN(transfer)) {
+		usbi_dbg("reading %d bytes", transfer->length);
+		ret = WinUSBX[sub_api].ReadPipe(winusb_handle, transfer->endpoint, transfer->buffer, transfer->length, NULL, overlapped);
+	} else {
+		usbi_dbg("writing %d bytes", transfer->length);
+		ret = WinUSBX[sub_api].WritePipe(winusb_handle, transfer->endpoint, transfer->buffer, transfer->length, NULL, overlapped);
+	}
+
+	if (!ret) {
+		if (GetLastError() != ERROR_IO_PENDING) {
+			usbi_err(ctx, "ReadPipe/WritePipe failed: %s", windows_error_str(0));
+			return LIBUSB_ERROR_IO;
+		}
+	} else {
+		windows_force_sync_completion(overlapped, (ULONG)transfer->length);
+	}
+
+	transfer_priv->interface_number = (uint8_t)current_interface;
+
+	return LIBUSB_SUCCESS;
+}
+
+static int winusbx_clear_halt(int sub_api, struct libusb_device_handle *dev_handle, unsigned char endpoint)
+{
+	struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev);
+	struct winusb_device_handle_priv *handle_priv = _device_handle_priv(dev_handle);
+	struct winusb_device_priv *priv = _device_priv(dev_handle->dev);
+	HANDLE winusb_handle;
+	int current_interface;
+
+	CHECK_WINUSBX_AVAILABLE(sub_api);
+
+	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 (!WinUSBX[sub_api].ResetPipe(winusb_handle, endpoint)) {
+		usbi_err(ctx, "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 winusbx_abort_control(int sub_api, struct usbi_transfer *itransfer)
+{
+	// Cancelling of the I/O is done in the parent
+	return LIBUSB_SUCCESS;
+}
+
+static int winusbx_abort_transfers(int sub_api, 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 winusb_device_handle_priv *handle_priv = _device_handle_priv(transfer->dev_handle);
+	struct winusb_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
+	struct winusb_device_priv *priv = _device_priv(transfer->dev_handle->dev);
+	HANDLE handle;
+	int current_interface;
+
+	CHECK_WINUSBX_AVAILABLE(sub_api);
+
+	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);
+
+	if (WinUSBX[sub_api].CancelIoEx_supported) {
+		// Try to use CancelIoEx if available to cancel just a single transfer
+		handle = handle_priv->interface_handle[current_interface].dev_handle;
+		if (pCancelIoEx(handle, transfer_priv->pollable_fd.overlapped))
+			return LIBUSB_SUCCESS;
+		else if (GetLastError() == ERROR_NOT_FOUND)
+			return LIBUSB_ERROR_NOT_FOUND;
+
+		// Not every driver implements the necessary functionality for CancelIoEx
+		usbi_warn(ctx, "CancelIoEx not supported for sub API %s", winusbx_driver_names[sub_api]);
+		WinUSBX[sub_api].CancelIoEx_supported = false;
+	}
+
+	handle = handle_priv->interface_handle[current_interface].api_handle;
+	if (!WinUSBX[sub_api].AbortPipe(handle, transfer->endpoint)) {
+		usbi_err(ctx, "AbortPipe failed: %s", windows_error_str(0));
+		return LIBUSB_ERROR_NO_DEVICE;
+	}
+
+	return LIBUSB_SUCCESS;
+}
+
+/*
+ * 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: (post hotplug): see if we can force eject the device and redetect it (reuse hotplug?)
+static int winusbx_reset_device(int sub_api, struct libusb_device_handle *dev_handle)
+{
+	struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev);
+	struct winusb_device_handle_priv *handle_priv = _device_handle_priv(dev_handle);
+	struct winusb_device_priv *priv = _device_priv(dev_handle->dev);
+	HANDLE winusb_handle;
+	int i, j;
+
+	CHECK_WINUSBX_AVAILABLE(sub_api);
+
+	// Reset any available pipe (except control)
+	for (i = 0; i < USB_MAXINTERFACES; i++) {
+		winusb_handle = handle_priv->interface_handle[i].api_handle;
+		if (HANDLE_VALID(winusb_handle)) {
+			for (j = 0; j < priv->usb_interface[i].nb_endpoints; j++) {
+				usbi_dbg("resetting ep %02X", priv->usb_interface[i].endpoint[j]);
+				if (!WinUSBX[sub_api].AbortPipe(winusb_handle, priv->usb_interface[i].endpoint[j]))
+					usbi_err(ctx, "AbortPipe (pipe address %02X) failed: %s",
+						priv->usb_interface[i].endpoint[j], windows_error_str(0));
+
+				// FlushPipe seems to fail on OUT pipes
+				if (IS_EPIN(priv->usb_interface[i].endpoint[j])
+						&& (!WinUSBX[sub_api].FlushPipe(winusb_handle, priv->usb_interface[i].endpoint[j])))
+					usbi_err(ctx, "FlushPipe (pipe address %02X) failed: %s",
+						priv->usb_interface[i].endpoint[j], windows_error_str(0));
+
+				if (!WinUSBX[sub_api].ResetPipe(winusb_handle, priv->usb_interface[i].endpoint[j]))
+					usbi_err(ctx, "ResetPipe (pipe address %02X) failed: %s",
+						priv->usb_interface[i].endpoint[j], windows_error_str(0));
+			}
+		}
+	}
+
+	// libusbK & libusb0 have the ability to issue an actual device reset
+	if (WinUSBX[sub_api].ResetDevice != NULL) {
+		winusb_handle = handle_priv->interface_handle[0].api_handle;
+		if (HANDLE_VALID(winusb_handle))
+			WinUSBX[sub_api].ResetDevice(winusb_handle);
+	}
+
+	return LIBUSB_SUCCESS;
+}
+
+static int winusbx_copy_transfer_data(int sub_api, struct usbi_transfer *itransfer, uint32_t io_size)
+{
+	struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+	struct winusb_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
+	struct winusb_device_priv *priv = _device_priv(transfer->dev_handle->dev);
+	PKISO_CONTEXT iso_context;
+	int i;
+
+	if (transfer->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS) {
+		CHECK_WINUSBX_AVAILABLE(sub_api);
+
+		// for isochronous, need to copy the individual iso packet actual_lengths and statuses
+		if ((sub_api == SUB_API_LIBUSBK) || (sub_api == SUB_API_LIBUSB0)) {
+			// iso only supported on libusbk-based backends for now
+			iso_context = transfer_priv->iso_context;
+			for (i = 0; i < transfer->num_iso_packets; i++) {
+				transfer->iso_packet_desc[i].actual_length = iso_context->IsoPackets[i].actual_length;
+				// TODO translate USDB_STATUS codes http://msdn.microsoft.com/en-us/library/ff539136(VS.85).aspx to libusb_transfer_status
+				//transfer->iso_packet_desc[i].status = transfer_priv->iso_context->IsoPackets[i].status;
+			}
+		} else {
+			// This should only occur if backend is not set correctly or other backend isoc is partially implemented
+			PRINT_UNSUPPORTED_API(copy_transfer_data);
+			return LIBUSB_ERROR_NOT_SUPPORTED;
+		}
+	}
+
+	itransfer->transferred += io_size;
+	return LIBUSB_TRANSFER_COMPLETED;
+}
+
+/*
+ * Composite API functions
+ */
+static int composite_open(int sub_api, struct libusb_device_handle *dev_handle)
+{
+	struct winusb_device_priv *priv = _device_priv(dev_handle->dev);
+	int r = LIBUSB_ERROR_NOT_FOUND;
+	uint8_t i;
+	// SUB_API_MAX + 1 as the SUB_API_MAX pos is used to indicate availability of HID
+	bool available[SUB_API_MAX + 1] = { 0 };
+
+	for (i = 0; i < USB_MAXINTERFACES; i++) {
+		switch (priv->usb_interface[i].apib->id) {
+		case USB_API_WINUSBX:
+			if (priv->usb_interface[i].sub_api != SUB_API_NOTSET) {
+				available[priv->usb_interface[i].sub_api] = true;
+			}
+			break;
+		case USB_API_HID:
+			available[SUB_API_MAX] = true;
+			break;
+		default:
+			break;
+		}
+	}
+
+	for (i = 0; i < SUB_API_MAX; i++) { // WinUSB-like drivers
+		if (available[i]) {
+			r = usb_api_backend[USB_API_WINUSBX].open(i, dev_handle);
+			if (r != LIBUSB_SUCCESS) {
+				return r;
+			}
+		}
+	}
+/*
+	if (available[SUB_API_MAX]) // HID driver
+		r = hid_open(SUB_API_NOTSET, dev_handle);
+*/
+	return r;
+}
+
+static void composite_close(int sub_api, struct libusb_device_handle *dev_handle)
+{
+	struct winusb_device_priv *priv = _device_priv(dev_handle->dev);
+	uint8_t i;
+	// SUB_API_MAX + 1 as the SUB_API_MAX pos is used to indicate availability of HID
+	bool available[SUB_API_MAX + 1] = { 0 };
+
+	for (i = 0; i < USB_MAXINTERFACES; i++) {
+		switch (priv->usb_interface[i].apib->id) {
+		case USB_API_WINUSBX:
+			if (priv->usb_interface[i].sub_api != SUB_API_NOTSET)
+				available[priv->usb_interface[i].sub_api] = true;
+			break;
+		case USB_API_HID:
+			available[SUB_API_MAX] = true;
+			break;
+		default:
+			break;
+		}
+	}
+
+	for (i = 0; i < SUB_API_MAX; i++) { // WinUSB-like drivers
+		if (available[i])
+			usb_api_backend[USB_API_WINUSBX].close(i, dev_handle);
+	}
+/*
+	if (available[SUB_API_MAX]) // HID driver
+		hid_close(SUB_API_NOTSET, dev_handle);
+*/
+}
+
+static int composite_claim_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface)
+{
+	struct winusb_device_priv *priv = _device_priv(dev_handle->dev);
+
+	CHECK_SUPPORTED_API(priv->usb_interface[iface].apib, claim_interface);
+
+	return priv->usb_interface[iface].apib->
+		claim_interface(priv->usb_interface[iface].sub_api, dev_handle, iface);
+}
+
+static int composite_set_interface_altsetting(int sub_api, struct libusb_device_handle *dev_handle, int iface, int altsetting)
+{
+	struct winusb_device_priv *priv = _device_priv(dev_handle->dev);
+
+	CHECK_SUPPORTED_API(priv->usb_interface[iface].apib, set_interface_altsetting);
+
+	return priv->usb_interface[iface].apib->
+		set_interface_altsetting(priv->usb_interface[iface].sub_api, dev_handle, iface, altsetting);
+}
+
+static int composite_release_interface(int sub_api, struct libusb_device_handle *dev_handle, int iface)
+{
+	struct winusb_device_priv *priv = _device_priv(dev_handle->dev);
+
+	CHECK_SUPPORTED_API(priv->usb_interface[iface].apib, release_interface);
+
+	return priv->usb_interface[iface].apib->
+		release_interface(priv->usb_interface[iface].sub_api, dev_handle, iface);
+}
+
+static int composite_submit_control_transfer(int sub_api, 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 winusb_device_priv *priv = _device_priv(transfer->dev_handle->dev);
+	struct libusb_config_descriptor *conf_desc;
+	WINUSB_SETUP_PACKET *setup = (WINUSB_SETUP_PACKET *)transfer->buffer;
+	int iface, pass, r;
+
+	// Interface shouldn't matter for control, but it does in practice, with Windows'
+	// restrictions with regards to accessing HID keyboards and mice. Try to target
+	// a specific interface first, if possible.
+	switch (LIBUSB_REQ_RECIPIENT(setup->RequestType)) {
+	case LIBUSB_RECIPIENT_INTERFACE:
+		iface = setup->Index & 0xFF;
+		break;
+	case LIBUSB_RECIPIENT_ENDPOINT:
+		r = libusb_get_active_config_descriptor(transfer->dev_handle->dev, &conf_desc);
+		if (r == LIBUSB_SUCCESS) {
+			iface = get_interface_by_endpoint(conf_desc, (setup->Index & 0xFF));
+			libusb_free_config_descriptor(conf_desc);
+			break;
+		}
+		// Fall through if not able to determine interface
+	default:
+		iface = -1;
+		break;
+	}
+
+	// Try and target a specific interface if the control setup indicates such
+	if ((iface >= 0) && (iface < USB_MAXINTERFACES)) {
+		usbi_dbg("attempting control transfer targeted to interface %d", iface);
+		if ((priv->usb_interface[iface].path != NULL)
+				&& (priv->usb_interface[iface].apib->submit_control_transfer != NULL)) {
+			r = priv->usb_interface[iface].apib->submit_control_transfer(priv->usb_interface[iface].sub_api, itransfer);
+			if (r == LIBUSB_SUCCESS)
+				return r;
+		}
+	}
+
+	// Either not targeted to a specific interface or no luck in doing so.
+	// Try a 2 pass approach with all interfaces.
+	for (pass = 0; pass < 2; pass++) {
+		for (iface = 0; iface < USB_MAXINTERFACES; iface++) {
+			if ((priv->usb_interface[iface].path != NULL)
+					&& (priv->usb_interface[iface].apib->submit_control_transfer != NULL)) {
+				if ((pass == 0) && (priv->usb_interface[iface].restricted_functionality)) {
+					usbi_dbg("trying to skip restricted interface #%d (HID keyboard or mouse?)", iface);
+					continue;
+				}
+				usbi_dbg("using interface %d", iface);
+				r = priv->usb_interface[iface].apib->submit_control_transfer(priv->usb_interface[iface].sub_api, itransfer);
+				// If not supported on this API, it may be supported on another, so don't give up yet!!
+				if (r == LIBUSB_ERROR_NOT_SUPPORTED)
+					continue;
+				return r;
+			}
+		}
+	}
+	usbi_err(ctx, "no libusb supported interfaces to complete request");
+	return LIBUSB_ERROR_NOT_FOUND;
+}
+
+static int composite_submit_bulk_transfer(int sub_api, 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 winusb_device_handle_priv *handle_priv = _device_handle_priv(transfer->dev_handle);
+	struct winusb_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;
+	}
+
+	CHECK_SUPPORTED_API(priv->usb_interface[current_interface].apib, submit_bulk_transfer);
+
+	return priv->usb_interface[current_interface].apib->
+		submit_bulk_transfer(priv->usb_interface[current_interface].sub_api, itransfer);
+}
+
+static int composite_submit_iso_transfer(int sub_api, 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 winusb_device_handle_priv *handle_priv = _device_handle_priv(transfer->dev_handle);
+	struct winusb_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;
+	}
+
+	CHECK_SUPPORTED_API(priv->usb_interface[current_interface].apib, submit_iso_transfer);
+
+	return priv->usb_interface[current_interface].apib->
+		submit_iso_transfer(priv->usb_interface[current_interface].sub_api, itransfer);
+}
+
+static int composite_clear_halt(int sub_api, struct libusb_device_handle *dev_handle, unsigned char endpoint)
+{
+	struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev);
+	struct winusb_device_handle_priv *handle_priv = _device_handle_priv(dev_handle);
+	struct winusb_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;
+	}
+
+	CHECK_SUPPORTED_API(priv->usb_interface[current_interface].apib, clear_halt);
+
+	return priv->usb_interface[current_interface].apib->
+		clear_halt(priv->usb_interface[current_interface].sub_api, dev_handle, endpoint);
+}
+
+static int composite_abort_control(int sub_api, struct usbi_transfer *itransfer)
+{
+	struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+	struct winusb_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
+	struct winusb_device_priv *priv = _device_priv(transfer->dev_handle->dev);
+	int current_interface = transfer_priv->interface_number;
+
+	if ((current_interface < 0) || (current_interface >= USB_MAXINTERFACES)) {
+		usbi_err(TRANSFER_CTX(transfer), "program assertion failed: invalid interface_number");
+		return LIBUSB_ERROR_NOT_FOUND;
+	}
+
+	CHECK_SUPPORTED_API(priv->usb_interface[current_interface].apib, abort_control);
+
+	return priv->usb_interface[current_interface].apib->
+		abort_control(priv->usb_interface[current_interface].sub_api, itransfer);
+}
+
+static int composite_abort_transfers(int sub_api, struct usbi_transfer *itransfer)
+{
+	struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+	struct winusb_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
+	struct winusb_device_priv *priv = _device_priv(transfer->dev_handle->dev);
+	int current_interface = transfer_priv->interface_number;
+
+	if ((current_interface < 0) || (current_interface >= USB_MAXINTERFACES)) {
+		usbi_err(TRANSFER_CTX(transfer), "program assertion failed: invalid interface_number");
+		return LIBUSB_ERROR_NOT_FOUND;
+	}
+
+	CHECK_SUPPORTED_API(priv->usb_interface[current_interface].apib, abort_transfers);
+
+	return priv->usb_interface[current_interface].apib->
+		abort_transfers(priv->usb_interface[current_interface].sub_api, itransfer);
+}
+
+static int composite_reset_device(int sub_api, struct libusb_device_handle *dev_handle)
+{
+	struct winusb_device_priv *priv = _device_priv(dev_handle->dev);
+	int r;
+	uint8_t i;
+	bool available[SUB_API_MAX];
+
+	for (i = 0; i < SUB_API_MAX; i++)
+		available[i] = false;
+
+	for (i = 0; i < USB_MAXINTERFACES; i++) {
+		if ((priv->usb_interface[i].apib->id == USB_API_WINUSBX)
+				&& (priv->usb_interface[i].sub_api != SUB_API_NOTSET))
+			available[priv->usb_interface[i].sub_api] = true;
+	}
+
+	for (i = 0; i < SUB_API_MAX; i++) {
+		if (available[i]) {
+			r = usb_api_backend[USB_API_WINUSBX].reset_device(i, dev_handle);
+			if (r != LIBUSB_SUCCESS)
+				return r;
+		}
+	}
+
+	return LIBUSB_SUCCESS;
+}
+
+static int composite_copy_transfer_data(int sub_api, struct usbi_transfer *itransfer, uint32_t io_size)
+{
+	struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
+	struct winusb_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
+	struct winusb_device_priv *priv = _device_priv(transfer->dev_handle->dev);
+	int current_interface = transfer_priv->interface_number;
+
+	CHECK_SUPPORTED_API(priv->usb_interface[current_interface].apib, copy_transfer_data);
+
+	return priv->usb_interface[current_interface].apib->
+		copy_transfer_data(priv->usb_interface[current_interface].sub_api, itransfer, io_size);
+}

+ 168 - 425
vendor/github.com/karalabe/hid/libusb/libusb/os/windows_winusb.h → vendor/github.com/karalabe/usb/libusb/libusb/os/windows_winusb.h

@@ -36,15 +36,14 @@
 #endif
 
 // Missing from MSVC6 setupapi.h
-#if !defined(SPDRP_ADDRESS)
+#ifndef SPDRP_ADDRESS
 #define SPDRP_ADDRESS		28
 #endif
-#if !defined(SPDRP_INSTALL_STATE)
+#ifndef SPDRP_INSTALL_STATE
 #define SPDRP_INSTALL_STATE	34
 #endif
 
 #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
@@ -61,17 +60,17 @@
 // http://msdn.microsoft.com/en-us/library/ff545978.aspx
 // http://msdn.microsoft.com/en-us/library/ff545972.aspx
 // http://msdn.microsoft.com/en-us/library/ff545982.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} };
+#ifndef 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} };
+#ifndef GUID_DEVINTERFACE_USB_DEVICE
+const GUID GUID_DEVINTERFACE_USB_DEVICE = {0xA5DCBF10, 0x6530, 0x11D2, {0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED}};
 #endif
-#if !defined(GUID_DEVINTERFACE_USB_HUB)
-const GUID GUID_DEVINTERFACE_USB_HUB = { 0xF18A0E88, 0xC30C, 0x11D0, {0x88, 0x15, 0x00, 0xA0, 0xC9, 0x06, 0xBE, 0xD8} };
+#ifndef GUID_DEVINTERFACE_USB_HUB
+const GUID GUID_DEVINTERFACE_USB_HUB = {0xF18A0E88, 0xC30C, 0x11D0, {0x88, 0x15, 0x00, 0xA0, 0xC9, 0x06, 0xBE, 0xD8}};
 #endif
-#if !defined(GUID_DEVINTERFACE_LIBUSB0_FILTER)
-const GUID GUID_DEVINTERFACE_LIBUSB0_FILTER = { 0xF9F3FF14, 0xAE21, 0x48A0, {0x8A, 0x25, 0x80, 0x11, 0xA7, 0xA9, 0x31, 0xD9} };
+#ifndef GUID_DEVINTERFACE_LIBUSB0_FILTER
+const GUID GUID_DEVINTERFACE_LIBUSB0_FILTER = {0xF9F3FF14, 0xAE21, 0x48A0, {0x8A, 0x25, 0x80, 0x11, 0xA7, 0xA9, 0x31, 0xD9}};
 #endif
 
 
@@ -84,8 +83,6 @@ const GUID GUID_DEVINTERFACE_LIBUSB0_FILTER = { 0xF9F3FF14, 0xAE21, 0x48A0, {0x8
 #define USB_API_WINUSBX		3
 #define USB_API_HID		4
 #define USB_API_MAX		5
-// The following is used to indicate if the HID or composite extra props have already been set.
-#define USB_API_SET		(1 << USB_API_MAX)
 
 // Sub-APIs for WinUSB-like driver APIs (WinUSB, libusbK, libusb-win32 through the libusbK DLL)
 // Must have the same values as the KUSB_DRVID enum from libusbk.h
@@ -95,15 +92,13 @@ const GUID GUID_DEVINTERFACE_LIBUSB0_FILTER = { 0xF9F3FF14, 0xAE21, 0x48A0, {0x8
 #define SUB_API_WINUSB		2
 #define SUB_API_MAX		3
 
-#define WINUSBX_DRV_NAMES	{"libusbK", "libusb0", "WinUSB"}
-
 struct windows_usb_api_backend {
 	const uint8_t id;
-	const char *designation;
-	const char **driver_name_list; // Driver name, without .sys, e.g. "usbccgp"
+	const char * const designation;
+	const char * const * const driver_name_list; // Driver name, without .sys, e.g. "usbccgp"
 	const uint8_t nb_driver_names;
-	int (*init)(int sub_api, struct libusb_context *ctx);
-	int (*exit)(int sub_api);
+	int (*init)(struct libusb_context *ctx);
+	void (*exit)(void);
 	int (*open)(int sub_api, struct libusb_device_handle *dev_handle);
 	void (*close)(int sub_api, struct libusb_device_handle *dev_handle);
 	int (*configure_endpoints)(int sub_api, struct libusb_device_handle *dev_handle, int iface);
@@ -123,9 +118,16 @@ struct windows_usb_api_backend {
 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;
+	usbi_dbg("unsupported API call for '%s' "		\
+		"(unrecognized device driver)", #fname)
+
+#define CHECK_SUPPORTED_API(apip, fname)			\
+	do {							\
+		if ((apip)->fname == NULL) {			\
+			PRINT_UNSUPPORTED_API(fname);		\
+			return LIBUSB_ERROR_NOT_SUPPORTED;	\
+		}						\
+	} while (0)
 
 /*
  * private structures definition
@@ -154,15 +156,16 @@ struct libusb_hid_descriptor {
 #define LIBUSB_REQ_IN(request_type)		((request_type) & LIBUSB_ENDPOINT_IN)
 #define LIBUSB_REQ_OUT(request_type)		(!LIBUSB_REQ_IN(request_type))
 
+#ifndef CTL_CODE
+#define CTL_CODE(DeviceType, Function, Method, Access) \
+	(((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method))
+#endif
+
 // The following are used for HID reports IOCTLs
-#define HID_CTL_CODE(id) \
-	CTL_CODE (FILE_DEVICE_KEYBOARD, (id), METHOD_NEITHER, FILE_ANY_ACCESS)
-#define HID_BUFFER_CTL_CODE(id) \
-	CTL_CODE (FILE_DEVICE_KEYBOARD, (id), METHOD_BUFFERED, FILE_ANY_ACCESS)
 #define HID_IN_CTL_CODE(id) \
-	CTL_CODE (FILE_DEVICE_KEYBOARD, (id), METHOD_IN_DIRECT, FILE_ANY_ACCESS)
+	CTL_CODE(FILE_DEVICE_KEYBOARD, (id), METHOD_IN_DIRECT, FILE_ANY_ACCESS)
 #define HID_OUT_CTL_CODE(id) \
-	CTL_CODE (FILE_DEVICE_KEYBOARD, (id), METHOD_OUT_DIRECT, FILE_ANY_ACCESS)
+	CTL_CODE(FILE_DEVICE_KEYBOARD, (id), METHOD_OUT_DIRECT, FILE_ANY_ACCESS)
 
 #define IOCTL_HID_GET_FEATURE		HID_OUT_CTL_CODE(100)
 #define IOCTL_HID_GET_INPUT_REPORT	HID_OUT_CTL_CODE(104)
@@ -193,39 +196,20 @@ struct hid_device_priv {
 	uint16_t input_report_size;
 	uint16_t output_report_size;
 	uint16_t feature_report_size;
+	uint16_t usage;
+	uint16_t usagePage;
 	WCHAR string[3][MAX_USB_STRING_LENGTH];
 	uint8_t string_index[3]; // man, prod, ser
 };
 
-struct windows_device_priv {
-	uint8_t depth; // distance to HCD
-	uint8_t port;  // port number on the hub
-	uint8_t active_config;
-	struct windows_usb_api_backend const *apib;
-	char *path;  // device interface path
-	int sub_api; // for WinUSB-like APIs
-	struct {
-		char *path; // each interface needs a device interface path,
-		struct windows_usb_api_backend const *apib; // an API backend (multiple drivers support),
-		int sub_api;
-		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];
-	struct hid_device_priv *hid;
-	USB_DEVICE_DESCRIPTOR dev_descriptor;
-	unsigned char **config_descriptor; // list of pointers to the cached config descriptors
-};
-
-static inline struct windows_device_priv *_device_priv(struct libusb_device *dev)
+static inline struct winusb_device_priv *_device_priv(struct libusb_device *dev)
 {
-	return (struct windows_device_priv *)dev->os_priv;
+	return (struct winusb_device_priv *)dev->os_priv;
 }
 
-static inline struct windows_device_priv *windows_device_priv_init(struct libusb_device *dev)
+static inline struct winusb_device_priv *winusb_device_priv_init(struct libusb_device *dev)
 {
-	struct windows_device_priv *p = _device_priv(dev);
+	struct winusb_device_priv *p = _device_priv(dev);
 	int i;
 
 	p->apib = &usb_api_backend[USB_API_UNSUPPORTED];
@@ -238,11 +222,12 @@ static inline struct windows_device_priv *windows_device_priv_init(struct libusb
 	return p;
 }
 
-static inline void windows_device_priv_release(struct libusb_device *dev)
+static inline void winusb_device_priv_release(struct libusb_device *dev)
 {
-	struct windows_device_priv *p = _device_priv(dev);
+	struct winusb_device_priv *p = _device_priv(dev);
 	int i;
 
+	free(p->dev_id);
 	free(p->path);
 	if ((dev->num_configurations > 0) && (p->config_descriptor != NULL)) {
 		for (i = 0; i < dev->num_configurations; i++)
@@ -256,32 +241,12 @@ static inline void windows_device_priv_release(struct libusb_device *dev)
 	}
 }
 
-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];
-	int autoclaim_count[USB_MAXINTERFACES]; // For auto-release
-};
-
-static inline struct windows_device_handle_priv *_device_handle_priv(
+static inline struct winusb_device_handle_priv *_device_handle_priv(
 	struct libusb_device_handle *handle)
 {
-	return (struct windows_device_handle_priv *)handle->os_priv;
+	return (struct winusb_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;
-};
-
 // used to match a device driver (including filter drivers) against a supported API
 struct driver_lookup {
 	char list[MAX_KEY_LENGTH + 1]; // REG_MULTI_SZ list of services (driver) names
@@ -289,94 +254,72 @@ struct driver_lookup {
 	const char* designation;       // internal designation (for debug output)
 };
 
+/*
+ * 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
+
+/* Cfgmgr32 dependencies */
+DLL_DECLARE_HANDLE(Cfgmgr32);
+DLL_DECLARE_FUNC(WINAPI, CONFIGRET, CM_Get_Parent, (PDEVINST, DEVINST, ULONG));
+DLL_DECLARE_FUNC(WINAPI, CONFIGRET, CM_Get_Child, (PDEVINST, DEVINST, ULONG));
+
+/* AdvAPI32 dependencies */
+DLL_DECLARE_HANDLE(AdvAPI32);
+DLL_DECLARE_FUNC_PREFIXED(WINAPI, LONG, p, RegQueryValueExW, (HKEY, LPCWSTR, LPDWORD, LPDWORD, LPBYTE, LPDWORD));
+DLL_DECLARE_FUNC_PREFIXED(WINAPI, LONG, p, RegCloseKey, (HKEY));
+
 /* OLE32 dependency */
 DLL_DECLARE_HANDLE(OLE32);
-DLL_DECLARE_FUNC_PREFIXED(WINAPI, HRESULT, p, CLSIDFromString, (LPCOLESTR, LPCLSID));
-
-/* Kernel32 dependencies */
-DLL_DECLARE_HANDLE(Kernel32);
-/* This call is only available from XP SP2 */
-DLL_DECLARE_FUNC_PREFIXED(WINAPI, BOOL, p, IsWow64Process, (HANDLE, PBOOL));
+DLL_DECLARE_FUNC_PREFIXED(WINAPI, HRESULT, p, IIDFromString, (LPCOLESTR, LPIID));
 
 /* SetupAPI dependencies */
 DLL_DECLARE_HANDLE(SetupAPI);
-DLL_DECLARE_FUNC_PREFIXED(WINAPI, HDEVINFO, p, SetupDiGetClassDevsA, (const GUID*, PCSTR, HWND, DWORD));
+DLL_DECLARE_FUNC_PREFIXED(WINAPI, HDEVINFO, p, SetupDiGetClassDevsA, (LPCGUID, PCSTR, HWND, DWORD));
 DLL_DECLARE_FUNC_PREFIXED(WINAPI, BOOL, p, SetupDiEnumDeviceInfo, (HDEVINFO, DWORD, PSP_DEVINFO_DATA));
 DLL_DECLARE_FUNC_PREFIXED(WINAPI, BOOL, p, SetupDiEnumDeviceInterfaces, (HDEVINFO, PSP_DEVINFO_DATA,
-			const GUID*, DWORD, PSP_DEVICE_INTERFACE_DATA));
+			LPCGUID, DWORD, PSP_DEVICE_INTERFACE_DATA));
+DLL_DECLARE_FUNC_PREFIXED(WINAPI, BOOL, p, SetupDiGetDeviceInstanceIdA, (HDEVINFO, PSP_DEVINFO_DATA,
+			PCSTR, DWORD, PDWORD));
 DLL_DECLARE_FUNC_PREFIXED(WINAPI, BOOL, p, SetupDiGetDeviceInterfaceDetailA, (HDEVINFO, PSP_DEVICE_INTERFACE_DATA,
 			PSP_DEVICE_INTERFACE_DETAIL_DATA_A, DWORD, PDWORD, PSP_DEVINFO_DATA));
-DLL_DECLARE_FUNC_PREFIXED(WINAPI, BOOL, p, SetupDiDestroyDeviceInfoList, (HDEVINFO));
-DLL_DECLARE_FUNC_PREFIXED(WINAPI, HKEY, p, SetupDiOpenDevRegKey, (HDEVINFO, PSP_DEVINFO_DATA, DWORD, DWORD, DWORD, REGSAM));
 DLL_DECLARE_FUNC_PREFIXED(WINAPI, BOOL, p, SetupDiGetDeviceRegistryPropertyA, (HDEVINFO,
 			PSP_DEVINFO_DATA, DWORD, PDWORD, PBYTE, DWORD, PDWORD));
+DLL_DECLARE_FUNC_PREFIXED(WINAPI, BOOL, p, SetupDiDestroyDeviceInfoList, (HDEVINFO));
+DLL_DECLARE_FUNC_PREFIXED(WINAPI, HKEY, p, SetupDiOpenDevRegKey, (HDEVINFO, PSP_DEVINFO_DATA, DWORD, DWORD, DWORD, REGSAM));
 DLL_DECLARE_FUNC_PREFIXED(WINAPI, HKEY, p, SetupDiOpenDeviceInterfaceRegKey, (HDEVINFO, PSP_DEVICE_INTERFACE_DATA, DWORD, DWORD));
 
-/* AdvAPI32 dependencies */
-DLL_DECLARE_HANDLE(AdvAPI32);
-DLL_DECLARE_FUNC_PREFIXED(WINAPI, LONG, p, RegQueryValueExW, (HKEY, LPCWSTR, LPDWORD, LPDWORD, LPBYTE, LPDWORD));
-DLL_DECLARE_FUNC_PREFIXED(WINAPI, LONG, p, RegCloseKey, (HKEY));
 
-/*
- * 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 USB_GET_NODE_INFORMATION		258
-#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_NODE_CONNECTION_INFORMATION_EX)
-#define USB_GET_NODE_CONNECTION_INFORMATION_EX	274
+#ifndef USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION
+#define USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION		260
 #endif
-#if !defined(USB_GET_HUB_CAPABILITIES_EX)
-#define USB_GET_HUB_CAPABILITIES_EX		276
+#ifndef USB_GET_NODE_CONNECTION_INFORMATION_EX
+#define USB_GET_NODE_CONNECTION_INFORMATION_EX		274
 #endif
-#if !defined(USB_GET_NODE_CONNECTION_INFORMATION_EX_V2)
+#ifndef USB_GET_NODE_CONNECTION_INFORMATION_EX_V2
 #define USB_GET_NODE_CONNECTION_INFORMATION_EX_V2	279
 #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
+#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
+#define USB_CTL_CODE(id) \
+	CTL_CODE(FILE_DEVICE_USB, (id), METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION \
+	USB_CTL_CODE(USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION)
+
+#define IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX \
+	USB_CTL_CODE(USB_GET_NODE_CONNECTION_INFORMATION_EX)
+
+#define IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX_V2 \
+	USB_CTL_CODE(USB_GET_NODE_CONNECTION_INFORMATION_EX_V2)
 
 typedef enum USB_CONNECTION_STATUS {
 	NoDeviceConnected,
@@ -395,79 +338,10 @@ typedef enum USB_HUB_NODE {
 	UsbMIParent
 } USB_HUB_NODE;
 
-/* Cfgmgr32.dll interface */
-DLL_DECLARE_HANDLE(Cfgmgr32);
-DLL_DECLARE_FUNC(WINAPI, CONFIGRET, CM_Get_Parent, (PDEVINST, DEVINST, ULONG));
-DLL_DECLARE_FUNC(WINAPI, CONFIGRET, CM_Get_Child, (PDEVINST, DEVINST, ULONG));
-DLL_DECLARE_FUNC(WINAPI, CONFIGRET, CM_Get_Sibling, (PDEVINST, DEVINST, ULONG));
-DLL_DECLARE_FUNC(WINAPI, CONFIGRET, CM_Get_Device_IDA, (DEVINST, PCHAR, ULONG, ULONG));
-
-#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_EX \
-	CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_CONNECTION_INFORMATION_EX, METHOD_BUFFERED, FILE_ANY_ACCESS)
-
-#define IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX_V2 \
-	CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_CONNECTION_INFORMATION_EX_V2, 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_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;
+#include <pshpack1.h>
 
-typedef struct USB_DESCRIPTOR_REQUEST {
+typedef struct _USB_DESCRIPTOR_REQUEST {
 	ULONG ConnectionIndex;
 	struct {
 		UCHAR bmRequest;
@@ -479,68 +353,24 @@ typedef struct USB_DESCRIPTOR_REQUEST {
 //	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_CONFIGURATION_DESCRIPTOR_SHORT {
+	USB_DESCRIPTOR_REQUEST req;
+	USB_CONFIGURATION_DESCRIPTOR desc;
+} USB_CONFIGURATION_DESCRIPTOR_SHORT;
 
-typedef struct USB_PIPE_INFO {
-	USB_ENDPOINT_DESCRIPTOR EndpointDescriptor;
-	ULONG ScheduleOffset;
-} USB_PIPE_INFO, *PUSB_PIPE_INFO;
+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_NODE_CONNECTION_INFORMATION_EX {
+typedef struct _USB_NODE_CONNECTION_INFORMATION_EX {
 	ULONG ConnectionIndex;
 	USB_DEVICE_DESCRIPTOR DeviceDescriptor;
 	UCHAR CurrentConfigurationValue;
@@ -578,25 +408,7 @@ typedef struct _USB_NODE_CONNECTION_INFORMATION_EX_V2 {
 	USB_NODE_CONNECTION_INFORMATION_EX_V2_FLAGS Flags;
 } USB_NODE_CONNECTION_INFORMATION_EX_V2, *PUSB_NODE_CONNECTION_INFORMATION_EX_V2;
 
-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)
+#include <poppack.h>
 
 /* winusb.dll interface */
 
@@ -608,36 +420,25 @@ typedef struct USB_HUB_CAPABILITIES_EX {
 #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 {
+
+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;
+#include <pshpack1.h>
+
+typedef struct _WINUSB_SETUP_PACKET {
+	UCHAR RequestType;
+	UCHAR Request;
+	USHORT Value;
+	USHORT Index;
+	USHORT Length;
 } WINUSB_SETUP_PACKET, *PWINUSB_SETUP_PACKET;
-#pragma pack()
+
+#include <poppack.h>
 
 typedef void *WINUSB_INTERFACE_HANDLE, *PWINUSB_INTERFACE_HANDLE;
 
@@ -665,59 +466,10 @@ typedef BOOL (WINAPI *WinUsb_GetAssociatedInterface_t)(
 	UCHAR AssociatedInterfaceIndex,
 	PWINUSB_INTERFACE_HANDLE AssociatedInterfaceHandle
 );
-typedef BOOL (WINAPI *WinUsb_GetCurrentAlternateSetting_t)(
-	WINUSB_INTERFACE_HANDLE InterfaceHandle,
-	PUCHAR AlternateSetting
-);
-typedef BOOL (WINAPI *WinUsb_GetDescriptor_t)(
-	WINUSB_INTERFACE_HANDLE InterfaceHandle,
-	UCHAR DescriptorType,
-	UCHAR Index,
-	USHORT LanguageID,
-	PUCHAR Buffer,
-	ULONG BufferLength,
-	PULONG LengthTransferred
-);
-typedef BOOL (WINAPI *WinUsb_GetOverlappedResult_t)(
-	WINUSB_INTERFACE_HANDLE InterfaceHandle,
-	LPOVERLAPPED lpOverlapped,
-	LPDWORD lpNumberOfBytesTransferred,
-	BOOL bWait
-);
-typedef BOOL (WINAPI *WinUsb_GetPipePolicy_t)(
-	WINUSB_INTERFACE_HANDLE InterfaceHandle,
-	UCHAR PipeID,
-	ULONG PolicyType,
-	PULONG ValueLength,
-	PVOID Value
-);
-typedef BOOL (WINAPI *WinUsb_GetPowerPolicy_t)(
-	WINUSB_INTERFACE_HANDLE InterfaceHandle,
-	ULONG PolicyType,
-	PULONG ValueLength,
-	PVOID Value
-);
 typedef BOOL (WINAPI *WinUsb_Initialize_t)(
 	HANDLE DeviceHandle,
 	PWINUSB_INTERFACE_HANDLE InterfaceHandle
 );
-typedef BOOL (WINAPI *WinUsb_QueryDeviceInformation_t)(
-	WINUSB_INTERFACE_HANDLE InterfaceHandle,
-	ULONG InformationType,
-	PULONG BufferLength,
-	PVOID Buffer
-);
-typedef BOOL (WINAPI *WinUsb_QueryInterfaceSettings_t)(
-	WINUSB_INTERFACE_HANDLE InterfaceHandle,
-	UCHAR AlternateSettingNumber,
-	PUSB_INTERFACE_DESCRIPTOR UsbAltInterfaceDescriptor
-);
-typedef BOOL (WINAPI *WinUsb_QueryPipe_t)(
-	WINUSB_INTERFACE_HANDLE InterfaceHandle,
-	UCHAR AlternateInterfaceNumber,
-	UCHAR PipeIndex,
-	PWINUSB_PIPE_INFORMATION PipeInformation
-);
 typedef BOOL (WINAPI *WinUsb_ReadPipe_t)(
 	WINUSB_INTERFACE_HANDLE InterfaceHandle,
 	UCHAR PipeID,
@@ -726,6 +478,9 @@ typedef BOOL (WINAPI *WinUsb_ReadPipe_t)(
 	PULONG LengthTransferred,
 	LPOVERLAPPED Overlapped
 );
+typedef BOOL (WINAPI *WinUsb_ResetDevice_t)(
+	WINUSB_INTERFACE_HANDLE InterfaceHandle
+);
 typedef BOOL (WINAPI *WinUsb_ResetPipe_t)(
 	WINUSB_INTERFACE_HANDLE InterfaceHandle,
 	UCHAR PipeID
@@ -741,12 +496,6 @@ typedef BOOL (WINAPI *WinUsb_SetPipePolicy_t)(
 	ULONG ValueLength,
 	PVOID Value
 );
-typedef BOOL (WINAPI *WinUsb_SetPowerPolicy_t)(
-	WINUSB_INTERFACE_HANDLE InterfaceHandle,
-	ULONG PolicyType,
-	ULONG ValueLength,
-	PVOID Value
-);
 typedef BOOL (WINAPI *WinUsb_WritePipe_t)(
 	WINUSB_INTERFACE_HANDLE InterfaceHandle,
 	UCHAR PipeID,
@@ -755,9 +504,6 @@ typedef BOOL (WINAPI *WinUsb_WritePipe_t)(
 	PULONG LengthTransferred,
 	LPOVERLAPPED Overlapped
 );
-typedef BOOL (WINAPI *WinUsb_ResetDevice_t)(
-	WINUSB_INTERFACE_HANDLE InterfaceHandle
-);
 
 /* /!\ These must match the ones from the official libusbk.h */
 typedef enum _KUSB_FNID {
@@ -803,8 +549,7 @@ typedef struct _KLIB_VERSION {
 	INT Minor;
 	INT Micro;
 	INT Nano;
-} KLIB_VERSION;
-typedef KLIB_VERSION* PKLIB_VERSION;
+} KLIB_VERSION, *PKLIB_VERSION;
 
 typedef BOOL (WINAPI *LibK_GetProcAddress_t)(
 	PVOID *ProcAddress,
@@ -816,29 +561,63 @@ typedef VOID (WINAPI *LibK_GetVersion_t)(
 	PKLIB_VERSION Version
 );
 
+//KISO_PACKET is equivalent of libusb_iso_packet_descriptor except uses absolute "offset" field instead of sequential Lengths
+typedef struct _KISO_PACKET {
+	UINT offset;
+	USHORT actual_length; //changed from libusbk_shared.h "Length" for clarity
+	USHORT status;
+} KISO_PACKET, *PKISO_PACKET;
+
+typedef enum _KISO_FLAG {
+	KISO_FLAG_NONE = 0,
+	KISO_FLAG_SET_START_FRAME = 0x00000001,
+} KISO_FLAG;
+
+//KISO_CONTEXT is the conceptual equivalent of libusb_transfer except is isochronous-specific and must match libusbk's version
+typedef struct _KISO_CONTEXT {
+	KISO_FLAG Flags;
+	UINT StartFrame;
+	SHORT ErrorCount;
+	SHORT NumberOfPackets;
+	UINT UrbHdrStatus;
+	KISO_PACKET IsoPackets[0];
+} KISO_CONTEXT, *PKISO_CONTEXT;
+
+typedef BOOL(WINAPI *WinUsb_IsoReadPipe_t)(
+	WINUSB_INTERFACE_HANDLE InterfaceHandle,
+	UCHAR PipeID,
+	PUCHAR Buffer,
+	ULONG BufferLength,
+	LPOVERLAPPED Overlapped,
+	PKISO_CONTEXT IsoContext
+);
+
+typedef BOOL(WINAPI *WinUsb_IsoWritePipe_t)(
+	WINUSB_INTERFACE_HANDLE InterfaceHandle,
+	UCHAR PipeID,
+	PUCHAR Buffer,
+	ULONG BufferLength,
+	LPOVERLAPPED Overlapped,
+	PKISO_CONTEXT IsoContext
+);
+
 struct winusb_interface {
 	bool initialized;
+	bool CancelIoEx_supported;
 	WinUsb_AbortPipe_t AbortPipe;
 	WinUsb_ControlTransfer_t ControlTransfer;
 	WinUsb_FlushPipe_t FlushPipe;
 	WinUsb_Free_t Free;
 	WinUsb_GetAssociatedInterface_t GetAssociatedInterface;
-	WinUsb_GetCurrentAlternateSetting_t GetCurrentAlternateSetting;
-	WinUsb_GetDescriptor_t GetDescriptor;
-	WinUsb_GetOverlappedResult_t GetOverlappedResult;
-	WinUsb_GetPipePolicy_t GetPipePolicy;
-	WinUsb_GetPowerPolicy_t GetPowerPolicy;
 	WinUsb_Initialize_t Initialize;
-	WinUsb_QueryDeviceInformation_t QueryDeviceInformation;
-	WinUsb_QueryInterfaceSettings_t QueryInterfaceSettings;
-	WinUsb_QueryPipe_t QueryPipe;
 	WinUsb_ReadPipe_t ReadPipe;
+	WinUsb_ResetDevice_t ResetDevice;
 	WinUsb_ResetPipe_t ResetPipe;
 	WinUsb_SetCurrentAlternateSetting_t SetCurrentAlternateSetting;
 	WinUsb_SetPipePolicy_t SetPipePolicy;
-	WinUsb_SetPowerPolicy_t SetPowerPolicy;
 	WinUsb_WritePipe_t WritePipe;
-	WinUsb_ResetDevice_t ResetDevice;
+	WinUsb_IsoReadPipe_t IsoReadPipe;
+	WinUsb_IsoWritePipe_t IsoWritePipe;
 };
 
 /* hid.dll interface */
@@ -846,34 +625,10 @@ struct winusb_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()
+#include <pshpack1.h>
+#include <poppack.h>
 
 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,
@@ -919,19 +674,7 @@ typedef struct _HIDP_VALUE_CAPS {
 } HIDP_VALUE_CAPS, *PHIDP_VALUE_CAPS;
 
 DLL_DECLARE_HANDLE(hid);
-DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_GetAttributes, (HANDLE, PHIDD_ATTRIBUTES));
 DLL_DECLARE_FUNC(WINAPI, VOID, HidD_GetHidGuid, (LPGUID));
-DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_GetPreparsedData, (HANDLE, PHIDP_PREPARSED_DATA *));
-DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_FreePreparsedData, (PHIDP_PREPARSED_DATA));
-DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_GetManufacturerString, (HANDLE, PVOID, ULONG));
-DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_GetProductString, (HANDLE, PVOID, ULONG));
-DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_GetSerialNumberString, (HANDLE, PVOID, ULONG));
-DLL_DECLARE_FUNC(WINAPI, LONG, HidP_GetCaps, (PHIDP_PREPARSED_DATA, PHIDP_CAPS));
-DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_SetNumInputBuffers, (HANDLE, ULONG));
-DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_SetFeature, (HANDLE, PVOID, ULONG));
-DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_GetFeature, (HANDLE, PVOID, ULONG));
 DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_GetPhysicalDescriptor, (HANDLE, PVOID, ULONG));
-DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_GetInputReport, (HANDLE, PVOID, ULONG));
-DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_SetOutputReport, (HANDLE, PVOID, ULONG));
 DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_FlushQueue, (HANDLE));
-DLL_DECLARE_FUNC(WINAPI, BOOL, HidP_GetValueCaps, (HIDP_REPORT_TYPE, PHIDP_VALUE_CAPS, PULONG, PHIDP_PREPARSED_DATA));
+DLL_DECLARE_FUNC(WINAPI, BOOL, HidP_GetValueCaps, (HIDP_REPORT_TYPE, PHIDP_VALUE_CAPS, PULONG, PHIDP_PREPARSED_DATA));

+ 0 - 0
vendor/github.com/karalabe/hid/libusb/libusb/strerror.c → vendor/github.com/karalabe/usb/libusb/libusb/strerror.c


+ 0 - 0
vendor/github.com/karalabe/hid/libusb/libusb/sync.c → vendor/github.com/karalabe/usb/libusb/libusb/sync.c


+ 1 - 1
vendor/github.com/karalabe/hid/libusb/libusb/version.h → vendor/github.com/karalabe/usb/libusb/libusb/version.h

@@ -7,7 +7,7 @@
 #define LIBUSB_MINOR 0
 #endif
 #ifndef LIBUSB_MICRO
-#define LIBUSB_MICRO 21
+#define LIBUSB_MICRO 22
 #endif
 #ifndef LIBUSB_NANO
 #define LIBUSB_NANO 0

+ 1 - 0
vendor/github.com/karalabe/usb/libusb/libusb/version_nano.h

@@ -0,0 +1 @@
+#define LIBUSB_NANO 11312

+ 42 - 0
vendor/github.com/karalabe/usb/raw_disabled.go

@@ -0,0 +1,42 @@
+// usb - Self contained USB and HID library for Go
+// Copyright 2017 The library Authors
+//
+// 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 3 of the License, or (at your option) any
+// later version.
+//
+// The 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 the library. If not, see <http://www.gnu.org/licenses/>.
+
+// +build !freebsd,!linux,!darwin,!windows ios !cgo
+
+package usb
+
+// RawDevice is a live raw USB connected device handle. On platforms that this file
+// implements, the type lacks the actual USB device and all methods are noop.
+type RawDevice struct {
+	DeviceInfo // Embed the infos for easier access
+}
+
+// Close releases the USB device handle. On platforms that this file implements,
+// the method is just a noop.
+func (dev *RawDevice) Close() error {
+	return ErrUnsupportedPlatform
+}
+
+// Write sends a binary blob to a USB device. On platforms that this file
+// implements, the method just returns an error.
+func (dev *RawDevice) Write(b []byte) (int, error) {
+	return 0, ErrUnsupportedPlatform
+}
+
+// Read retrieves a binary blob from a USB device. On platforms that this file
+// implements, the method just returns an error.
+func (dev *RawDevice) Read(b []byte) (int, error) {
+	return 0, ErrUnsupportedPlatform
+}

+ 253 - 0
vendor/github.com/karalabe/usb/raw_enabled.go

@@ -0,0 +1,253 @@
+// usb - Self contained USB and HID library for Go
+// Copyright 2019 The library Authors
+//
+// 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 3 of the License, or (at your option) any
+// later version.
+//
+// The 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 the library. If not, see <http://www.gnu.org/licenses/>.
+
+// +build freebsd,cgo linux,cgo darwin,!ios,cgo windows,cgo
+
+package usb
+
+/*
+	#include "./libusb/libusb/libusb.h"
+
+	// ctx is a global libusb context to interact with devices through.
+	libusb_context* ctx;
+*/
+import "C"
+
+import (
+	"fmt"
+	"reflect"
+	"sync"
+	"unsafe"
+)
+
+// enumerateRaw returns a list of all the USB devices attached to the system which
+// match the vendor and product id:
+//  - If the vendor id is set to 0 then any vendor matches.
+//  - If the product id is set to 0 then any product matches.
+//  - If the vendor and product id are both 0, all USB devices are returned.
+func enumerateRaw(vendorID uint16, productID uint16) ([]DeviceInfo, error) {
+	// Enumerate the devices, and free all the matching refcounts (we'll reopen any
+	// explicitly requested).
+	infos, err := enumerateRawWithRef(vendorID, productID)
+	for _, info := range infos {
+		C.libusb_unref_device(info.rawDevice.(*C.libusb_device))
+	}
+	// If enumeration failed, don't return anything, otherwise everything
+	if err != nil {
+		return nil, err
+	}
+	return infos, nil
+}
+
+// enumerateRawWithRef is the internal device enumerator that retains 1 reference
+// to every matched device so they may selectively be opened on request.
+func enumerateRawWithRef(vendorID uint16, productID uint16) ([]DeviceInfo, error) {
+	// Ensure we have a libusb context to interact through. The enumerate call is
+	// protexted by a mutex outside, so it's fine to do the below check and init.
+	if C.ctx == nil {
+		if err := fromRawErrno(C.libusb_init((**C.libusb_context)(&C.ctx))); err != nil {
+			return nil, fmt.Errorf("failed to initialize libusb: %v", err)
+		}
+	}
+	// Retrieve all the available USB devices and wrap them in Go
+	var deviceList **C.libusb_device
+	count := C.libusb_get_device_list(C.ctx, &deviceList)
+	if count < 0 {
+		return nil, rawError(count)
+	}
+	defer C.libusb_free_device_list(deviceList, 1)
+
+	var devices []*C.libusb_device
+	*(*reflect.SliceHeader)(unsafe.Pointer(&devices)) = reflect.SliceHeader{
+		Data: uintptr(unsafe.Pointer(deviceList)),
+		Len:  int(count),
+		Cap:  int(count),
+	}
+	//
+	var infos []DeviceInfo
+	for devnum, dev := range devices {
+		// Retrieve the libusb device descriptor and skip non-queried ones
+		var desc C.struct_libusb_device_descriptor
+		if err := fromRawErrno(C.libusb_get_device_descriptor(dev, &desc)); err != nil {
+			return infos, fmt.Errorf("failed to get device %d descriptor: %v", devnum, err)
+		}
+		if (vendorID > 0 && uint16(desc.idVendor) != vendorID) || (productID > 0 && uint16(desc.idProduct) != productID) {
+			continue
+		}
+		// Skip HID devices, they are handled directly by OS libraries
+		if desc.bDeviceClass == C.LIBUSB_CLASS_HID {
+			continue
+		}
+		// Iterate over all the configurations and find raw interfaces
+		for cfgnum := 0; cfgnum < int(desc.bNumConfigurations); cfgnum++ {
+			// Retrieve the all the possible USB configurations of the device
+			var cfg *C.struct_libusb_config_descriptor
+			if err := fromRawErrno(C.libusb_get_config_descriptor(dev, C.uint8_t(cfgnum), &cfg)); err != nil {
+				return infos, fmt.Errorf("failed to get device %d config %d: %v", devnum, cfgnum, err)
+			}
+			var ifaces []C.struct_libusb_interface
+			*(*reflect.SliceHeader)(unsafe.Pointer(&ifaces)) = reflect.SliceHeader{
+				Data: uintptr(unsafe.Pointer(cfg._interface)),
+				Len:  int(cfg.bNumInterfaces),
+				Cap:  int(cfg.bNumInterfaces),
+			}
+			// Drill down into each advertised interface
+			for ifacenum, iface := range ifaces {
+				if iface.num_altsetting == 0 {
+					continue
+				}
+				var alts []C.struct_libusb_interface_descriptor
+				*(*reflect.SliceHeader)(unsafe.Pointer(&alts)) = reflect.SliceHeader{
+					Data: uintptr(unsafe.Pointer(iface.altsetting)),
+					Len:  int(iface.num_altsetting),
+					Cap:  int(iface.num_altsetting),
+				}
+				for _, alt := range alts {
+					// Skip HID interfaces, they are handled directly by OS libraries
+					if alt.bInterfaceClass == C.LIBUSB_CLASS_HID {
+						continue
+					}
+					// Find the endpoints that can speak libusb interrupts
+					var ends []C.struct_libusb_endpoint_descriptor
+					*(*reflect.SliceHeader)(unsafe.Pointer(&ends)) = reflect.SliceHeader{
+						Data: uintptr(unsafe.Pointer(alt.endpoint)),
+						Len:  int(alt.bNumEndpoints),
+						Cap:  int(alt.bNumEndpoints),
+					}
+					var reader, writer *uint8
+					for _, end := range ends {
+						// Skip any non-interrupt endpoints
+						if end.bmAttributes != C.LIBUSB_TRANSFER_TYPE_INTERRUPT {
+							continue
+						}
+						if end.bEndpointAddress&C.LIBUSB_ENDPOINT_IN == C.LIBUSB_ENDPOINT_IN {
+							reader = new(uint8)
+							*reader = uint8(end.bEndpointAddress)
+						} else {
+							writer = new(uint8)
+							*writer = uint8(end.bEndpointAddress)
+						}
+					}
+					// If both in and out interrupts are available, match the device
+					if reader != nil && writer != nil {
+						// Enumeration matched, bump the device refcount to avoid cleaning it up
+						C.libusb_ref_device(dev)
+
+						port := uint8(C.libusb_get_port_number(dev))
+						infos = append(infos, DeviceInfo{
+							Path:      fmt.Sprintf("%04x:%04x:%02d", vendorID, uint16(desc.idProduct), port),
+							VendorID:  uint16(desc.idVendor),
+							ProductID: uint16(desc.idProduct),
+							Interface: ifacenum,
+							rawDevice: dev,
+							rawPort:   &port,
+							rawReader: reader,
+							rawWriter: writer,
+						})
+					}
+				}
+			}
+		}
+	}
+	return infos, nil
+}
+
+// openRaw connects to a low level libusb device by its path name.
+func openRaw(info DeviceInfo) (*rawDevice, error) {
+	// Enumerate all the devices matching this particular info
+	matches, err := enumerateRawWithRef(info.VendorID, info.ProductID)
+	if err != nil {
+		// Enumeration failed, make sure any subresults are released
+		for _, match := range matches {
+			C.libusb_unref_device(match.rawDevice.(*C.libusb_device))
+		}
+		return nil, err
+	}
+	// Find the specific endpoint we're interested in
+	var device *C.libusb_device
+	for _, match := range matches {
+		// Keep the matching device reference, release anything else
+		if device == nil && *match.rawPort == *info.rawPort && match.Interface == info.Interface {
+			device = match.rawDevice.(*C.libusb_device)
+		} else {
+			C.libusb_unref_device(match.rawDevice.(*C.libusb_device))
+		}
+	}
+	if device == nil {
+		return nil, fmt.Errorf("failed to open device: not found")
+	}
+	// Open the mathcing device
+	info.rawDevice = device
+
+	var handle *C.struct_libusb_device_handle
+	if err := fromRawErrno(C.libusb_open(info.rawDevice.(*C.libusb_device), (**C.struct_libusb_device_handle)(&handle))); err != nil {
+		return nil, fmt.Errorf("failed to open device: %v", err)
+	}
+	if err := fromRawErrno(C.libusb_claim_interface(handle, (C.int)(info.Interface))); err != nil {
+		C.libusb_close(handle)
+		return nil, fmt.Errorf("failed to claim interface: %v", err)
+	}
+	return &rawDevice{
+		DeviceInfo: info,
+		handle:     handle,
+	}, nil
+}
+
+// rawDevice is a live low level USB connected device handle.
+type rawDevice struct {
+	DeviceInfo // Embed the infos for easier access
+
+	handle *C.struct_libusb_device_handle // Low level USB device to communicate through
+	lock   sync.Mutex
+}
+
+// Close releases the raw USB device handle.
+func (dev *rawDevice) Close() error {
+	dev.lock.Lock()
+	defer dev.lock.Unlock()
+
+	if dev.handle != nil {
+		C.libusb_release_interface(dev.handle, (C.int)(dev.Interface))
+		C.libusb_close(dev.handle)
+		dev.handle = nil
+	}
+	C.libusb_unref_device(dev.rawDevice.(*C.libusb_device))
+
+	return nil
+}
+
+// Write sends a binary blob to a low level USB device.
+func (dev *rawDevice) Write(b []byte) (int, error) {
+	dev.lock.Lock()
+	defer dev.lock.Unlock()
+
+	var transferred C.int
+	if err := fromRawErrno(C.libusb_interrupt_transfer(dev.handle, (C.uchar)(*dev.rawWriter), (*C.uchar)(&b[0]), (C.int)(len(b)), &transferred, (C.uint)(0))); err != nil {
+		return 0, fmt.Errorf("failed to write to device: %v", err)
+	}
+	return int(transferred), nil
+}
+
+// Read retrieves a binary blob from a low level USB device.
+func (dev *rawDevice) Read(b []byte) (int, error) {
+	dev.lock.Lock()
+	defer dev.lock.Unlock()
+
+	var transferred C.int
+	if err := fromRawErrno(C.libusb_interrupt_transfer(dev.handle, (C.uchar)(*dev.rawReader), (*C.uchar)(&b[0]), (C.int)(len(b)), &transferred, (C.uint)(0))); err != nil {
+		return 0, fmt.Errorf("failed to read from device: %v", err)
+	}
+	return int(transferred), nil
+}

+ 74 - 0
vendor/github.com/karalabe/usb/raw_errors.go

@@ -0,0 +1,74 @@
+// Copyright 2013 Google Inc.  All rights reserved.
+// Copyright 2016 the gousb Authors.  All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package usb
+
+import (
+	"fmt"
+)
+
+// #include "./libusb/libusb/libusb.h"
+import "C"
+
+// rawError is an error code from libusb.
+type rawError C.int
+
+// Error implements the error interface.
+func (e rawError) Error() string {
+	return fmt.Sprintf("libusb: %s [code %d]", rawErrorString[e], e)
+}
+
+// fromRawErrno converts a raw libusb error into a Go type.
+func fromRawErrno(errno C.int) error {
+	err := rawError(errno)
+	if err == errSuccess {
+		return nil
+	}
+	return err
+}
+
+const (
+	errSuccess      rawError = C.LIBUSB_SUCCESS
+	errIO           rawError = C.LIBUSB_ERROR_IO
+	errInvalidParam rawError = C.LIBUSB_ERROR_INVALID_PARAM
+	errAccess       rawError = C.LIBUSB_ERROR_ACCESS
+	errNoDevice     rawError = C.LIBUSB_ERROR_NO_DEVICE
+	errNotFound     rawError = C.LIBUSB_ERROR_NOT_FOUND
+	errBusy         rawError = C.LIBUSB_ERROR_BUSY
+	errTimeout      rawError = C.LIBUSB_ERROR_TIMEOUT
+	errOverflow     rawError = C.LIBUSB_ERROR_OVERFLOW
+	errPipe         rawError = C.LIBUSB_ERROR_PIPE
+	errInterrupted  rawError = C.LIBUSB_ERROR_INTERRUPTED
+	errNoMem        rawError = C.LIBUSB_ERROR_NO_MEM
+	errNotSupported rawError = C.LIBUSB_ERROR_NOT_SUPPORTED
+	errOther        rawError = C.LIBUSB_ERROR_OTHER
+)
+
+var rawErrorString = map[rawError]string{
+	errSuccess:      "success",
+	errIO:           "i/o error",
+	errInvalidParam: "invalid param",
+	errAccess:       "bad access",
+	errNoDevice:     "no device",
+	errNotFound:     "not found",
+	errBusy:         "device or resource busy",
+	errTimeout:      "timeout",
+	errOverflow:     "overflow",
+	errPipe:         "pipe error",
+	errInterrupted:  "interrupted",
+	errNoMem:        "out of memory",
+	errNotSupported: "not supported",
+	errOther:        "unknown error",
+}

+ 68 - 0
vendor/github.com/karalabe/usb/usb.go

@@ -0,0 +1,68 @@
+// usb - Self contained USB and HID library for Go
+// Copyright 2019 The library Authors
+//
+// 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 3 of the License, or (at your option) any
+// later version.
+//
+// The 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 the library. If not, see <http://www.gnu.org/licenses/>.
+
+// Package usb provide interfaces for generic USB devices.
+package usb
+
+import "errors"
+
+// ErrDeviceClosed is returned for operations where the device closed before or
+// during the execution.
+var ErrDeviceClosed = errors.New("usb: device closed")
+
+// ErrUnsupportedPlatform is returned for all operations where the underlying
+// operating system is not supported by the library.
+var ErrUnsupportedPlatform = errors.New("usb: unsupported platform")
+
+// DeviceInfo contains all the information we know about a USB device. In case of
+// HID devices, that might be a lot more extensive (empty fields for raw USB).
+type DeviceInfo struct {
+	Path         string // Platform-specific device path
+	VendorID     uint16 // Device Vendor ID
+	ProductID    uint16 // Device Product ID
+	Release      uint16 // Device Release Number in binary-coded decimal, also known as Device Version Number
+	Serial       string // Serial Number
+	Manufacturer string // Manufacturer String
+	Product      string // Product string
+	UsagePage    uint16 // Usage Page for this Device/Interface (Windows/Mac only)
+	Usage        uint16 // Usage for this Device/Interface (Windows/Mac only)
+
+	// The USB interface which this logical device
+	// represents. Valid on both Linux implementations
+	// in all cases, and valid on the Windows implementation
+	// only if the device contains more than one interface.
+	Interface int
+
+	// Raw low level libusb endpoint data for simplified communication
+	rawDevice interface{}
+	rawPort   *uint8 // Pointer to differentiate between unset and port 0
+	rawReader *uint8 // Pointer to differentiate between unset and endpoint 0
+	rawWriter *uint8 // Pointer to differentiate between unset and endpoint 0
+}
+
+// Device is a generic USB device interface. It may either be backed by a USB HID
+// device or a low level raw (libusb) device.
+type Device interface {
+	// Close releases the USB device handle.
+	Close() error
+
+	// Write sends a binary blob to a USB device. For HID devices write uses reports,
+	// for low level USB write uses interrupt transfers.
+	Write(b []byte) (int, error)
+
+	// Read retrieves a binary blob from a USB device. For HID devices read uses
+	// reports, for low level USB read uses interrupt transfers.
+	Read(b []byte) (int, error)
+}

+ 46 - 0
vendor/github.com/karalabe/usb/usb_disabled.go

@@ -0,0 +1,46 @@
+// usb - Self contained USB and HID library for Go
+// Copyright 2019 The library Authors
+//
+// 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 3 of the License, or (at your option) any
+// later version.
+//
+// The 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 the library. If not, see <http://www.gnu.org/licenses/>.
+
+// +build !freebsd,!linux,!darwin,!windows ios !cgo
+
+package usb
+
+// Supported returns whether this platform is supported by the USB library or not.
+// The goal of this method is to allow programatically handling platforms that do
+// not support USB and not having to fall back to build constraints.
+func Supported() bool {
+	return false
+}
+
+// Enumerate returns a list of all the USB devices attached to the system which
+// match the vendor and product id. On platforms that this file implements the
+// function is a noop and returns an empty list always.
+func Enumerate(vendorID uint16, productID uint16) []DeviceInfo {
+	return nil
+}
+
+// EnumerateRaw returns a list of all the USB devices attached to the system which
+// match the vendor and product id. On platforms that this file implements the
+// function is a noop and returns an empty list always.
+func EnumerateRaw(vendorID uint16, productID uint16) ([]DeviceInfo, error) {
+	return nil
+}
+
+// EnumerateHid returns a list of all the HID devices attached to the system which
+// match the vendor and product id. On platforms that this file implements the
+// function is a noop and returns an empty list always.
+func EnumerateHid(vendorID uint16, productID uint16) ([]DeviceInfo, error) {
+	return nil
+}

+ 98 - 0
vendor/github.com/karalabe/usb/usb_enabled.go

@@ -0,0 +1,98 @@
+// usb - Self contained USB and HID library for Go
+// Copyright 2019 The library Authors
+//
+// 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 3 of the License, or (at your option) any
+// later version.
+//
+// The 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 the library. If not, see <http://www.gnu.org/licenses/>.
+
+// +build freebsd,cgo linux,cgo darwin,!ios,cgo windows,cgo
+
+package usb
+
+import (
+	"sync"
+)
+
+// enumerateLock is a mutex serializing access to USB device enumeration needed
+// by the macOS USB HID system calls, which require 2 consecutive method calls
+// for enumeration, causing crashes if called concurrently.
+//
+// For more details, see:
+//   https://developer.apple.com/documentation/iokit/1438371-iohidmanagersetdevicematching
+//   > "subsequent calls will cause the hid manager to release previously enumerated devices"
+var enumerateLock sync.Mutex
+
+// Supported returns whether this platform is supported by the USB library or not.
+// The goal of this method is to allow programatically handling platforms that do
+// not support USB and not having to fall back to build constraints.
+func Supported() bool {
+	return true
+}
+
+// Enumerate returns a list of all the USB devices attached to the system which
+// match the vendor and product id:
+//  - If the vendor id is set to 0 then any vendor matches.
+//  - If the product id is set to 0 then any product matches.
+//  - If the vendor and product id are both 0, all devices are returned.
+//
+// For any device that is HID capable, the enumeration will return an interface
+// to the HID endpoints. For pure raw USB access, please use EnumerateRaw.
+func Enumerate(vendorID uint16, productID uint16) ([]DeviceInfo, error) {
+	enumerateLock.Lock()
+	defer enumerateLock.Unlock()
+
+	// Enumerate all the raw USB devices and skip the HID ones
+	raws, err := enumerateRaw(vendorID, productID)
+	if err != nil {
+		return nil, err
+	}
+	// Enumerate all the HID USB devices
+	hids, err := enumerateHid(vendorID, productID)
+	if err != nil {
+		return nil, err
+	}
+	return append(raws, hids...), nil
+}
+
+// EnumerateRaw returns a list of all the USB devices attached to the system which
+// match the vendor and product id:
+//  - If the vendor id is set to 0 then any vendor matches.
+//  - If the product id is set to 0 then any product matches.
+//  - If the vendor and product id are both 0, all devices are returned.
+func EnumerateRaw(vendorID uint16, productID uint16) ([]DeviceInfo, error) {
+	enumerateLock.Lock()
+	defer enumerateLock.Unlock()
+
+	return enumerateRaw(vendorID, productID)
+}
+
+// EnumerateHid returns a list of all the HID devices attached to the system which
+// match the vendor and product id:
+//  - If the vendor id is set to 0 then any vendor matches.
+//  - If the product id is set to 0 then any product matches.
+//  - If the vendor and product id are both 0, all devices are returned.
+func EnumerateHid(vendorID uint16, productID uint16) ([]DeviceInfo, error) {
+	enumerateLock.Lock()
+	defer enumerateLock.Unlock()
+
+	return enumerateHid(vendorID, productID)
+}
+
+// Open connects to a previsouly discovered USB device.
+func (info DeviceInfo) Open() (Device, error) {
+	enumerateLock.Lock()
+	defer enumerateLock.Unlock()
+
+	if info.rawDevice == nil {
+		return openHid(info)
+	}
+	return openRaw(info)
+}

+ 1 - 1
vendor/github.com/karalabe/hid/wchar.go → vendor/github.com/karalabe/usb/wchar.go

@@ -9,7 +9,7 @@
 // +build !ios
 // +build freebsd linux darwin windows
 
-package hid
+package usb
 
 /*
 #include <wchar.h>

+ 4 - 4
vendor/vendor.json

@@ -267,10 +267,10 @@
 			"revisionTime": "2017-04-30T22:20:11Z"
 		},
 		{
-			"checksumSHA1": "p6UjFsx/1ACWAhsdEOWrXAHptGY=",
-			"path": "github.com/karalabe/hid",
-			"revision": "e40407cce1c217644c09da5415bbfb07d330ea5e",
-			"revisionTime": "2019-05-28T15:16:06Z",
+			"checksumSHA1": "3v8Z4/daUVp9PCcFzEGYVkPadG8=",
+			"path": "github.com/karalabe/usb",
+			"revision": "c012609e094b8a96375fee53cc11f1bcd5cf3aa2",
+			"revisionTime": "2019-06-04T10:57:36Z",
 			"tree": true
 		},
 		{