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

This commit is contained in:
Péter Szilágyi 2019-06-01 17:19:47 +03:00
parent b4cc7b660c
commit 5d68400cad
No known key found for this signature in database
GPG Key ID: E9AE538CEDF8293D
92 changed files with 7522 additions and 7842 deletions

View File

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

View File

@ -36,8 +36,6 @@ import (
"github.com/golang/protobuf/proto" "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 // 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 // this case, the calling application should display a pinpad and send back the
// encoded passphrase. // encoded passphrase.

View File

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

View File

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

View File

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

View File

@ -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.

View File

@ -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.

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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;
}

View File

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

View File

@ -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();
}

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -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
vendor/github.com/karalabe/usb/AUTHORS generated vendored Normal file
View File

@ -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
vendor/github.com/karalabe/usb/LICENSE generated vendored Normal file
View File

@ -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
vendor/github.com/karalabe/usb/README.md generated vendored Normal file
View File

@ -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).

View File

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

76
vendor/github.com/karalabe/usb/demo.go generated vendored Normal file
View File

@ -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
vendor/github.com/karalabe/usb/go.mod generated vendored Normal file
View File

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

42
vendor/github.com/karalabe/usb/hid_disabled.go generated vendored Normal file
View File

@ -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
vendor/github.com/karalabe/usb/hid_enabled.go generated vendored Normal file
View File

@ -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
}

74
vendor/github.com/karalabe/usb/libs.go generated vendored Normal file
View File

@ -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"

View File

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

View File

@ -44,32 +44,6 @@
#include "libusbi.h" #include "libusbi.h"
#include "hotplug.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; struct libusb_context *usbi_default_context = NULL;
static const struct libusb_version libusb_version_internal = static const struct libusb_version libusb_version_internal =
{ LIBUSB_MAJOR, LIBUSB_MINOR, LIBUSB_MICRO, LIBUSB_NANO, { 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, * 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 * which means that no output will be produced. However, unless the library
* has been compiled with logging disabled, then any application calls to * has been compiled with logging disabled, then any application calls to
* libusb_set_debug(), or the setting of the environmental variable * libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, level), or the setting of the
* LIBUSB_DEBUG outside of the application, can result in logging being * environmental variable LIBUSB_DEBUG outside of the application, can result
* produced. Your application should therefore not close stderr, but instead * in logging being produced. Your application should therefore not close
* direct it to the null device if its output is undesirable. * 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 * The libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, level) function can be
* messages. Under standard configuration, libusb doesn't really log much * used to enable logging of certain messages. Under standard configuration,
* so you are advised to use this function to enable all error/warning/ * libusb doesn't really log much so you are advised to use this function
* informational messages. It will help debug problems with your software. * 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 * The logged messages are unstructured. There is no one-to-one correspondence
* between messages being logged and success or failure return codes from * 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 * 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, * 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 * 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 * libusb can be compiled without any logging functions, useful for embedded
* systems. In this case, libusb_set_debug() and the LIBUSB_DEBUG environment * systems. In this case, libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, level)
* variable have no effects. * and the LIBUSB_DEBUG environment variable have no effects.
* *
* libusb can also be compiled with verbose debugging messages always. When * libusb can also be compiled with verbose debugging messages always. When
* the library is compiled in this way, all messages of all verbosities are * the library is compiled in this way, all messages of all verbosities are
* always logged. libusb_set_debug() and the LIBUSB_DEBUG environment variable * always logged. libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, level) and
* have no effects. * the LIBUSB_DEBUG environment variable have no effects.
* *
* \section remarks Other remarks * \section remarks Other remarks
* *
@ -187,6 +165,20 @@ struct list_head active_contexts_list;
/** /**
* \page libusb_caveats Caveats * \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 * \section devresets Device resets
* *
* The libusb_reset_device() function allows you to reset a device. If your * 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 * information about the end of the short packet, and the user probably wanted
* that surplus data to arrive in the next logical transfer. * that surplus data to arrive in the next logical transfer.
* *
*
* \section zlp Zero length packets * \section zlp Zero length packets
* *
* - libusb is able to send a packet of zero length to an endpoint simply by * - 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. * developed modules may both use libusb.
* *
* libusb is written to allow for these multiple user scenarios. The two * 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 * 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. * 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_debug()
* - libusb_set_interface_alt_setting() * - libusb_set_interface_alt_setting()
* - libusb_set_iso_packet_lengths() * - libusb_set_iso_packet_lengths()
* - libusb_set_option()
* - libusb_setlocale() * - libusb_setlocale()
* - libusb_set_pollfd_notifiers() * - libusb_set_pollfd_notifiers()
* - libusb_strerror() * - libusb_strerror()
@ -478,6 +470,7 @@ if (cfg != desired)
* - \ref libusb_iso_sync_type * - \ref libusb_iso_sync_type
* - \ref libusb_iso_usage_type * - \ref libusb_iso_usage_type
* - \ref libusb_log_level * - \ref libusb_log_level
* - \ref libusb_option
* - \ref libusb_request_recipient * - \ref libusb_request_recipient
* - \ref libusb_request_type * - \ref libusb_request_type
* - \ref libusb_speed * - \ref libusb_speed
@ -680,7 +673,7 @@ struct discovered_devs *discovered_devs_append(
struct libusb_device *usbi_alloc_device(struct libusb_context *ctx, struct libusb_device *usbi_alloc_device(struct libusb_context *ctx,
unsigned long session_id) 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); struct libusb_device *dev = calloc(1, sizeof(*dev) + priv_size);
int r; int r;
@ -824,8 +817,8 @@ ssize_t API_EXPORTED libusb_get_device_list(libusb_context *ctx,
/* backend provides hotplug support */ /* backend provides hotplug support */
struct libusb_device *dev; struct libusb_device *dev;
if (usbi_backend->hotplug_poll) if (usbi_backend.hotplug_poll)
usbi_backend->hotplug_poll(); usbi_backend.hotplug_poll();
usbi_mutex_lock(&ctx->usb_devs_lock); usbi_mutex_lock(&ctx->usb_devs_lock);
list_for_each_entry(dev, &ctx->usb_devs, list, struct libusb_device) { 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); usbi_mutex_unlock(&ctx->usb_devs_lock);
} else { } else {
/* backend does not provide hotplug support */ /* 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) { if (r < 0) {
@ -1167,8 +1160,8 @@ void API_EXPORTED libusb_unref_device(libusb_device *dev)
libusb_unref_device(dev->parent_dev); libusb_unref_device(dev->parent_dev);
if (usbi_backend->destroy_device) if (usbi_backend.destroy_device)
usbi_backend->destroy_device(dev); usbi_backend.destroy_device(dev);
if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) { if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
/* backend does not support 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_context *ctx = DEVICE_CTX(dev);
struct libusb_device_handle *_dev_handle; 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; int r;
usbi_dbg("open %d.%d", dev->bus_number, dev->device_address); 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; _dev_handle->claimed_interfaces = 0;
memset(&_dev_handle->os_priv, 0, priv_size); memset(&_dev_handle->os_priv, 0, priv_size);
r = usbi_backend->open(_dev_handle); r = usbi_backend.open(_dev_handle);
if (r < 0) { if (r < 0) {
usbi_dbg("open %d.%d returns %d", dev->bus_number, dev->device_address, r); usbi_dbg("open %d.%d returns %d", dev->bus_number, dev->device_address, r);
libusb_unref_device(dev); libusb_unref_device(dev);
@ -1382,7 +1375,7 @@ static void do_close(struct libusb_context *ctx,
list_del(&dev_handle->list); list_del(&dev_handle->list);
usbi_mutex_unlock(&ctx->open_devs_lock); usbi_mutex_unlock(&ctx->open_devs_lock);
usbi_backend->close(dev_handle); usbi_backend.close(dev_handle);
libusb_unref_device(dev_handle->dev); libusb_unref_device(dev_handle->dev);
usbi_mutex_destroy(&dev_handle->lock); usbi_mutex_destroy(&dev_handle->lock);
free(dev_handle); free(dev_handle);
@ -1491,8 +1484,8 @@ int API_EXPORTED libusb_get_configuration(libusb_device_handle *dev_handle,
int r = LIBUSB_ERROR_NOT_SUPPORTED; int r = LIBUSB_ERROR_NOT_SUPPORTED;
usbi_dbg(""); usbi_dbg("");
if (usbi_backend->get_configuration) if (usbi_backend.get_configuration)
r = usbi_backend->get_configuration(dev_handle, config); r = usbi_backend.get_configuration(dev_handle, config);
if (r == LIBUSB_ERROR_NOT_SUPPORTED) { if (r == LIBUSB_ERROR_NOT_SUPPORTED) {
uint8_t tmp = 0; uint8_t tmp = 0;
@ -1567,7 +1560,7 @@ int API_EXPORTED libusb_set_configuration(libusb_device_handle *dev_handle,
int configuration) int configuration)
{ {
usbi_dbg("configuration %d", 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 /** \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)) if (dev_handle->claimed_interfaces & (1 << interface_number))
goto out; goto out;
r = usbi_backend->claim_interface(dev_handle, interface_number); r = usbi_backend.claim_interface(dev_handle, interface_number);
if (r == 0) if (r == 0)
dev_handle->claimed_interfaces |= 1 << interface_number; dev_handle->claimed_interfaces |= 1 << interface_number;
@ -1657,7 +1650,7 @@ int API_EXPORTED libusb_release_interface(libusb_device_handle *dev_handle,
goto out; goto out;
} }
r = usbi_backend->release_interface(dev_handle, interface_number); r = usbi_backend.release_interface(dev_handle, interface_number);
if (r == 0) if (r == 0)
dev_handle->claimed_interfaces &= ~(1 << interface_number); 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); 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); alternate_setting);
} }
@ -1734,7 +1727,7 @@ int API_EXPORTED libusb_clear_halt(libusb_device_handle *dev_handle,
if (!dev_handle->dev->attached) if (!dev_handle->dev->attached)
return LIBUSB_ERROR_NO_DEVICE; return LIBUSB_ERROR_NO_DEVICE;
return usbi_backend->clear_halt(dev_handle, endpoint); return usbi_backend.clear_halt(dev_handle, endpoint);
} }
/** \ingroup libusb_dev /** \ingroup libusb_dev
@ -1762,7 +1755,7 @@ int API_EXPORTED libusb_reset_device(libusb_device_handle *dev_handle)
if (!dev_handle->dev->attached) if (!dev_handle->dev->attached)
return LIBUSB_ERROR_NO_DEVICE; return LIBUSB_ERROR_NO_DEVICE;
return usbi_backend->reset_device(dev_handle); return usbi_backend.reset_device(dev_handle);
} }
/** \ingroup libusb_asyncio /** \ingroup libusb_asyncio
@ -1794,8 +1787,8 @@ int API_EXPORTED libusb_alloc_streams(libusb_device_handle *dev_handle,
if (!dev_handle->dev->attached) if (!dev_handle->dev->attached)
return LIBUSB_ERROR_NO_DEVICE; return LIBUSB_ERROR_NO_DEVICE;
if (usbi_backend->alloc_streams) if (usbi_backend.alloc_streams)
return usbi_backend->alloc_streams(dev_handle, num_streams, endpoints, return usbi_backend.alloc_streams(dev_handle, num_streams, endpoints,
num_endpoints); num_endpoints);
else else
return LIBUSB_ERROR_NOT_SUPPORTED; 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) if (!dev_handle->dev->attached)
return LIBUSB_ERROR_NO_DEVICE; return LIBUSB_ERROR_NO_DEVICE;
if (usbi_backend->free_streams) if (usbi_backend.free_streams)
return usbi_backend->free_streams(dev_handle, endpoints, return usbi_backend.free_streams(dev_handle, endpoints,
num_endpoints); num_endpoints);
else else
return LIBUSB_ERROR_NOT_SUPPORTED; 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) if (!dev_handle->dev->attached)
return NULL; return NULL;
if (usbi_backend->dev_mem_alloc) if (usbi_backend.dev_mem_alloc)
return usbi_backend->dev_mem_alloc(dev_handle, length); return usbi_backend.dev_mem_alloc(dev_handle, length);
else else
return NULL; 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, int API_EXPORTED libusb_dev_mem_free(libusb_device_handle *dev_handle,
unsigned char *buffer, size_t length) unsigned char *buffer, size_t length)
{ {
if (usbi_backend->dev_mem_free) if (usbi_backend.dev_mem_free)
return usbi_backend->dev_mem_free(dev_handle, buffer, length); return usbi_backend.dev_mem_free(dev_handle, buffer, length);
else else
return LIBUSB_ERROR_NOT_SUPPORTED; 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) if (!dev_handle->dev->attached)
return LIBUSB_ERROR_NO_DEVICE; return LIBUSB_ERROR_NO_DEVICE;
if (usbi_backend->kernel_driver_active) if (usbi_backend.kernel_driver_active)
return usbi_backend->kernel_driver_active(dev_handle, interface_number); return usbi_backend.kernel_driver_active(dev_handle, interface_number);
else else
return LIBUSB_ERROR_NOT_SUPPORTED; 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) if (!dev_handle->dev->attached)
return LIBUSB_ERROR_NO_DEVICE; return LIBUSB_ERROR_NO_DEVICE;
if (usbi_backend->detach_kernel_driver) if (usbi_backend.detach_kernel_driver)
return usbi_backend->detach_kernel_driver(dev_handle, interface_number); return usbi_backend.detach_kernel_driver(dev_handle, interface_number);
else else
return LIBUSB_ERROR_NOT_SUPPORTED; 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) if (!dev_handle->dev->attached)
return LIBUSB_ERROR_NO_DEVICE; return LIBUSB_ERROR_NO_DEVICE;
if (usbi_backend->attach_kernel_driver) if (usbi_backend.attach_kernel_driver)
return usbi_backend->attach_kernel_driver(dev_handle, interface_number); return usbi_backend.attach_kernel_driver(dev_handle, interface_number);
else else
return LIBUSB_ERROR_NOT_SUPPORTED; 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( int API_EXPORTED libusb_set_auto_detach_kernel_driver(
libusb_device_handle *dev_handle, int enable) 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; return LIBUSB_ERROR_NOT_SUPPORTED;
dev_handle->auto_detach_kernel_driver = enable; dev_handle->auto_detach_kernel_driver = enable;
@ -2015,37 +2008,100 @@ int API_EXPORTED libusb_set_auto_detach_kernel_driver(
} }
/** \ingroup libusb_lib /** \ingroup libusb_lib
* Set log message verbosity. * \deprecated Use libusb_set_option() instead using the
* * \ref LIBUSB_OPTION_LOG_LEVEL option.
* 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.
*
* 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.
*
* \param ctx the context to operate on, or NULL for the default context
* \param level debug level to set
*/ */
void API_EXPORTED libusb_set_debug(libusb_context *ctx, int level) void API_EXPORTED libusb_set_debug(libusb_context *ctx, int level)
{ {
#if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING)
USBI_GET_CONTEXT(ctx); USBI_GET_CONTEXT(ctx);
if (!ctx->debug_fixed) if (!ctx->debug_fixed) {
ctx->debug = level; 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.
*
* Use this function to configure a specific option within the library.
*
* Some options require one or more arguments to be provided. Consult each
* option's documentation for specific requirements.
*
* Since version 1.0.22, \ref LIBUSB_API_VERSION >= 0x01000106
*
* \param ctx context on which to operate
* \param option which option to set
* \param ... any required arguments for the specified option
*
* \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
*/
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);
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 /** \ingroup libusb_lib
* Initialize libusb. This function must be called before calling any other * Initialize libusb. This function must be called before calling any other
* libusb function. * 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) int API_EXPORTED libusb_init(libusb_context **context)
{ {
struct libusb_device *dev, *next; struct libusb_device *dev, *next;
char *dbg = getenv("LIBUSB_DEBUG"); size_t priv_size = usbi_backend.context_priv_size;
struct libusb_context *ctx; struct libusb_context *ctx;
static int first_init = 1; static int first_init = 1;
int r = 0; int r = 0;
@ -2070,7 +2126,7 @@ int API_EXPORTED libusb_init(libusb_context **context)
usbi_mutex_static_lock(&default_context_lock); usbi_mutex_static_lock(&default_context_lock);
if (!timestamp_origin.tv_sec) { 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) { if (!context && usbi_default_context) {
@ -2080,22 +2136,18 @@ int API_EXPORTED libusb_init(libusb_context **context)
return 0; return 0;
} }
ctx = calloc(1, sizeof(*ctx)); ctx = calloc(1, sizeof(*ctx) + priv_size);
if (!ctx) { if (!ctx) {
r = LIBUSB_ERROR_NO_MEM; r = LIBUSB_ERROR_NO_MEM;
goto err_unlock; goto err_unlock;
} }
#ifdef ENABLE_DEBUG_LOGGING #if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING)
ctx->debug = LIBUSB_LOG_LEVEL_DEBUG; ctx->debug = get_env_debug_level();
if (ctx->debug != LIBUSB_LOG_LEVEL_NONE)
ctx->debug_fixed = 1;
#endif #endif
if (dbg) {
ctx->debug = atoi(dbg);
if (ctx->debug)
ctx->debug_fixed = 1;
}
/* default context should be initialized before calling usbi_dbg */ /* default context should be initialized before calling usbi_dbg */
if (!usbi_default_context) { if (!usbi_default_context) {
usbi_default_context = ctx; usbi_default_context = ctx;
@ -2112,6 +2164,7 @@ int API_EXPORTED libusb_init(libusb_context **context)
list_init(&ctx->usb_devs); list_init(&ctx->usb_devs);
list_init(&ctx->open_devs); list_init(&ctx->open_devs);
list_init(&ctx->hotplug_cbs); list_init(&ctx->hotplug_cbs);
ctx->next_hotplug_cb_handle = 1;
usbi_mutex_static_lock(&active_contexts_lock); usbi_mutex_static_lock(&active_contexts_lock);
if (first_init) { if (first_init) {
@ -2121,8 +2174,8 @@ int API_EXPORTED libusb_init(libusb_context **context)
list_add (&ctx->list, &active_contexts_list); list_add (&ctx->list, &active_contexts_list);
usbi_mutex_static_unlock(&active_contexts_lock); usbi_mutex_static_unlock(&active_contexts_lock);
if (usbi_backend->init) { if (usbi_backend.init) {
r = usbi_backend->init(ctx); r = usbi_backend.init(ctx);
if (r) if (r)
goto err_free_ctx; goto err_free_ctx;
} }
@ -2139,8 +2192,8 @@ int API_EXPORTED libusb_init(libusb_context **context)
return 0; return 0;
err_backend_exit: err_backend_exit:
if (usbi_backend->exit) if (usbi_backend.exit)
usbi_backend->exit(); usbi_backend.exit(ctx);
err_free_ctx: err_free_ctx:
if (ctx == usbi_default_context) { if (ctx == usbi_default_context) {
usbi_default_context = NULL; usbi_default_context = NULL;
@ -2200,7 +2253,7 @@ void API_EXPORTED libusb_exit(struct libusb_context *ctx)
usbi_mutex_static_unlock(&active_contexts_lock); usbi_mutex_static_unlock(&active_contexts_lock);
if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) { 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 * 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_warn(ctx, "application left some devices open");
usbi_io_exit(ctx); usbi_io_exit(ctx);
if (usbi_backend->exit) if (usbi_backend.exit)
usbi_backend->exit(); usbi_backend.exit(ctx);
usbi_mutex_destroy(&ctx->open_devs_lock); usbi_mutex_destroy(&ctx->open_devs_lock);
usbi_mutex_destroy(&ctx->usb_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: case LIBUSB_CAP_HAS_CAPABILITY:
return 1; return 1;
case LIBUSB_CAP_HAS_HOTPLUG: case LIBUSB_CAP_HAS_HOTPLUG:
return !(usbi_backend->get_device_list); return !(usbi_backend.get_device_list);
case LIBUSB_CAP_HAS_HID_ACCESS: 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: 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; return 0;
} }
#ifdef ENABLE_LOGGING
/* this is defined in libusbi.h if needed */ /* this is defined in libusbi.h if needed */
#ifdef LIBUSB_PRINTF_WIN32 #ifdef LIBUSB_PRINTF_WIN32
/* /*
@ -2301,10 +2356,9 @@ int usbi_vsnprintf(char *str, size_t size, const char *format, va_list ap)
return ret; return ret;
} }
#endif #endif /* LIBUSB_PRINTF_WIN32 */
static void usbi_log_str(struct libusb_context *ctx, static void usbi_log_str(enum libusb_log_level level, const char *str)
enum libusb_log_level level, const char * str)
{ {
#if defined(USE_SYSTEM_LOGGING_FACILITY) #if defined(USE_SYSTEM_LOGGING_FACILITY)
#if defined(OS_WINDOWS) #if defined(OS_WINDOWS)
@ -2317,18 +2371,20 @@ static void usbi_log_str(struct libusb_context *ctx,
#elif defined(__ANDROID__) #elif defined(__ANDROID__)
int priority = ANDROID_LOG_UNKNOWN; int priority = ANDROID_LOG_UNKNOWN;
switch (level) { switch (level) {
case LIBUSB_LOG_LEVEL_INFO: priority = ANDROID_LOG_INFO; break; case LIBUSB_LOG_LEVEL_NONE: return;
case LIBUSB_LOG_LEVEL_WARNING: priority = ANDROID_LOG_WARN; break;
case LIBUSB_LOG_LEVEL_ERROR: priority = ANDROID_LOG_ERROR; break; 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; case LIBUSB_LOG_LEVEL_DEBUG: priority = ANDROID_LOG_DEBUG; break;
} }
__android_log_write(priority, "libusb", str); __android_log_write(priority, "libusb", str);
#elif defined(HAVE_SYSLOG_FUNC) #elif defined(HAVE_SYSLOG_FUNC)
int syslog_level = LOG_INFO; int syslog_level = LOG_INFO;
switch (level) { switch (level) {
case LIBUSB_LOG_LEVEL_INFO: syslog_level = LOG_INFO; break; case LIBUSB_LOG_LEVEL_NONE: return;
case LIBUSB_LOG_LEVEL_WARNING: syslog_level = LOG_WARNING; break;
case LIBUSB_LOG_LEVEL_ERROR: syslog_level = LOG_ERR; break; 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; case LIBUSB_LOG_LEVEL_DEBUG: syslog_level = LOG_DEBUG; break;
} }
syslog(syslog_level, "%s", str); syslog(syslog_level, "%s", str);
@ -2339,14 +2395,13 @@ static void usbi_log_str(struct libusb_context *ctx,
#else #else
fputs(str, stderr); fputs(str, stderr);
#endif /* USE_SYSTEM_LOGGING_FACILITY */ #endif /* USE_SYSTEM_LOGGING_FACILITY */
UNUSED(ctx);
UNUSED(level); UNUSED(level);
} }
void usbi_log_v(struct libusb_context *ctx, enum libusb_log_level 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 *function, const char *format, va_list args)
{ {
const char *prefix = ""; const char *prefix;
char buf[USBI_MAX_LOG_LEN]; char buf[USBI_MAX_LOG_LEN];
struct timespec now; struct timespec now;
int global_debug, header_len, text_len; 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; global_debug = 1;
UNUSED(ctx); UNUSED(ctx);
#else #else
int ctx_level = 0; enum libusb_log_level ctx_level = LIBUSB_LOG_LEVEL_NONE;
USBI_GET_CONTEXT(ctx); USBI_GET_CONTEXT(ctx);
if (ctx) { if (ctx)
ctx_level = ctx->debug; ctx_level = ctx->debug;
} else { else
char *dbg = getenv("LIBUSB_DEBUG"); ctx_level = get_env_debug_level();
if (dbg)
ctx_level = atoi(dbg); if (ctx_level == LIBUSB_LOG_LEVEL_NONE)
}
global_debug = (ctx_level == LIBUSB_LOG_LEVEL_DEBUG);
if (!ctx_level)
return; return;
if (level == LIBUSB_LOG_LEVEL_WARNING && ctx_level < LIBUSB_LOG_LEVEL_WARNING) if (level == LIBUSB_LOG_LEVEL_WARNING && ctx_level < LIBUSB_LOG_LEVEL_WARNING)
return; return;
@ -2375,13 +2427,15 @@ void usbi_log_v(struct libusb_context *ctx, enum libusb_log_level level,
return; return;
if (level == LIBUSB_LOG_LEVEL_DEBUG && ctx_level < LIBUSB_LOG_LEVEL_DEBUG) if (level == LIBUSB_LOG_LEVEL_DEBUG && ctx_level < LIBUSB_LOG_LEVEL_DEBUG)
return; return;
global_debug = (ctx_level == LIBUSB_LOG_LEVEL_DEBUG);
#endif #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)) { if ((global_debug) && (!has_debug_header_been_displayed)) {
has_debug_header_been_displayed = 1; 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(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, "--------------------------------------------------------------------------------" USBI_LOG_LINE_END);
} }
if (now.tv_nsec < timestamp_origin.tv_nsec) { if (now.tv_nsec < timestamp_origin.tv_nsec) {
now.tv_sec--; 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; now.tv_nsec -= timestamp_origin.tv_nsec;
switch (level) { switch (level) {
case LIBUSB_LOG_LEVEL_INFO: case LIBUSB_LOG_LEVEL_NONE:
prefix = "info"; return;
case LIBUSB_LOG_LEVEL_ERROR:
prefix = "error";
break; break;
case LIBUSB_LOG_LEVEL_WARNING: case LIBUSB_LOG_LEVEL_WARNING:
prefix = "warning"; prefix = "warning";
break; break;
case LIBUSB_LOG_LEVEL_ERROR: case LIBUSB_LOG_LEVEL_INFO:
prefix = "error"; prefix = "info";
break; break;
case LIBUSB_LOG_LEVEL_DEBUG: case LIBUSB_LOG_LEVEL_DEBUG:
prefix = "debug"; prefix = "debug";
break; break;
case LIBUSB_LOG_LEVEL_NONE:
return;
default: default:
prefix = "unknown"; prefix = "unknown";
break; 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); 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, 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); va_end (args);
} }
#endif /* ENABLE_LOGGING */
/** \ingroup libusb_misc /** \ingroup libusb_misc
* Returns a constant NULL-terminated string with the ASCII name of a libusb * 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 * error or transfer status code. The caller must not free() the returned

View File

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

View File

@ -154,36 +154,30 @@ int main (void) {
\endcode \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_device *dev, libusb_hotplug_event event,
struct libusb_hotplug_callback *hotplug_cb) struct libusb_hotplug_callback *hotplug_cb)
{ {
/* Handle lazy deregistration of callback */ if (!(hotplug_cb->flags & event)) {
if (hotplug_cb->needs_free) {
/* Free callback */
return 1;
}
if (!(hotplug_cb->events & event)) {
return 0; 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) { hotplug_cb->vendor_id != dev->device_descriptor.idVendor) {
return 0; 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) { hotplug_cb->product_id != dev->device_descriptor.idProduct) {
return 0; 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) { hotplug_cb->dev_class != dev->device_descriptor.bDeviceClass) {
return 0; 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, 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); usbi_mutex_lock(&ctx->hotplug_cbs_lock);
list_for_each_entry_safe(hotplug_cb, next, &ctx->hotplug_cbs, list, struct libusb_hotplug_callback) { 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); 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); usbi_mutex_lock(&ctx->hotplug_cbs_lock);
if (ret) { 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); 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, void usbi_hotplug_notification(struct libusb_context *ctx, struct libusb_device *dev,
libusb_hotplug_event event) libusb_hotplug_event event)
{ {
int pending_events; int pending_events;
libusb_hotplug_message *message = calloc(1, sizeof(*message)); struct libusb_hotplug_message *message = calloc(1, sizeof(*message));
if (!message) { if (!message) {
usbi_err(ctx, "error allocating hotplug 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_fn cb_fn, void *user_data,
libusb_hotplug_callback_handle *callback_handle) libusb_hotplug_callback_handle *callback_handle)
{ {
libusb_hotplug_callback *new_callback; struct 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;
}
/* check for sane values */ /* 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 != product_id && (~0xffff & product_id)) ||
(LIBUSB_HOTPLUG_MATCH_ANY != dev_class && (~0xff & dev_class)) || (LIBUSB_HOTPLUG_MATCH_ANY != dev_class && (~0xff & dev_class)) ||
!cb_fn) { !cb_fn) {
return LIBUSB_ERROR_INVALID_PARAM; 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); USBI_GET_CONTEXT(ctx);
new_callback = (libusb_hotplug_callback *)calloc(1, sizeof (*new_callback)); new_callback = calloc(1, sizeof(*new_callback));
if (!new_callback) { if (!new_callback) {
return LIBUSB_ERROR_NO_MEM; return LIBUSB_ERROR_NO_MEM;
} }
new_callback->ctx = ctx; new_callback->flags = (uint8_t)events;
new_callback->vendor_id = vendor_id; if (LIBUSB_HOTPLUG_MATCH_ANY != vendor_id) {
new_callback->product_id = product_id; new_callback->flags |= USBI_HOTPLUG_VENDOR_ID_VALID;
new_callback->dev_class = dev_class; new_callback->vendor_id = (uint16_t)vendor_id;
new_callback->flags = flags; }
new_callback->events = events; 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->cb = cb_fn;
new_callback->user_data = user_data; new_callback->user_data = user_data;
new_callback->needs_free = 0;
usbi_mutex_lock(&ctx->hotplug_cbs_lock); usbi_mutex_lock(&ctx->hotplug_cbs_lock);
/* protect the handle by the context hotplug lock. it doesn't matter if the same handle /* protect the handle by the context hotplug lock */
* is used for different contexts only that the handle is unique for this context */ new_callback->handle = ctx->next_hotplug_cb_handle++;
new_callback->handle = handle_id++;
/* 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); list_add(&new_callback->list, &ctx->hotplug_cbs);
usbi_mutex_unlock(&ctx->hotplug_cbs_lock); 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) { if ((flags & LIBUSB_HOTPLUG_ENUMERATE) && (events & LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED)) {
int i, len; ssize_t i, len;
struct libusb_device **devs; struct libusb_device **devs;
len = (int) libusb_get_device_list(ctx, &devs); len = libusb_get_device_list(ctx, &devs);
if (len < 0) { if (len < 0) {
libusb_hotplug_deregister_callback(ctx, libusb_hotplug_deregister_callback(ctx,
new_callback->handle); new_callback->handle);
return len; return (int)len;
} }
for (i = 0; i < len; i++) { for (i = 0; i < len; i++) {
@ -311,10 +319,11 @@ int API_EXPORTED libusb_hotplug_register_callback(libusb_context *ctx,
return LIBUSB_SUCCESS; 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) libusb_hotplug_callback_handle callback_handle)
{ {
struct libusb_hotplug_callback *hotplug_cb; struct libusb_hotplug_callback *hotplug_cb;
int deregistered = 0;
/* check for hotplug support */ /* check for hotplug support */
if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) { 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_GET_CONTEXT(ctx);
usbi_dbg("deregister hotplug cb %d", callback_handle);
usbi_mutex_lock(&ctx->hotplug_cbs_lock); usbi_mutex_lock(&ctx->hotplug_cbs_lock);
list_for_each_entry(hotplug_cb, &ctx->hotplug_cbs, list, list_for_each_entry(hotplug_cb, &ctx->hotplug_cbs, list, struct libusb_hotplug_callback) {
struct libusb_hotplug_callback) {
if (callback_handle == hotplug_cb->handle) { if (callback_handle == hotplug_cb->handle) {
/* Mark this callback for deregistration */ /* 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_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; struct libusb_hotplug_callback *hotplug_cb, *next;
usbi_mutex_lock(&ctx->hotplug_cbs_lock); usbi_mutex_lock(&ctx->hotplug_cbs_lock);
list_for_each_entry_safe(hotplug_cb, next, &ctx->hotplug_cbs, list, list_for_each_entry_safe(hotplug_cb, next, &ctx->hotplug_cbs, list, struct libusb_hotplug_callback) {
struct libusb_hotplug_callback) { if (forced || (hotplug_cb->flags & USBI_HOTPLUG_NEEDS_FREE)) {
list_del(&hotplug_cb->list); usbi_dbg("freeing hotplug cb %p with handle %d", hotplug_cb,
free(hotplug_cb); hotplug_cb->handle);
list_del(&hotplug_cb->list);
free(hotplug_cb);
}
} }
usbi_mutex_unlock(&ctx->hotplug_cbs_lock); usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
} }

View File

@ -19,12 +19,34 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * 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 #define USBI_HOTPLUG_H
#ifndef LIBUSBI_H
#include "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 /** \ingroup hotplug
* The hotplug callback structure. The user populates this structure with * The hotplug callback structure. The user populates this structure with
@ -32,23 +54,17 @@
* to receive notification of hotplug events. * to receive notification of hotplug events.
*/ */
struct libusb_hotplug_callback { struct libusb_hotplug_callback {
/** Context this callback is associated with */ /** Flags that control how this callback behaves */
struct libusb_context *ctx; uint8_t flags;
/** Vendor ID to match or LIBUSB_HOTPLUG_MATCH_ANY */ /** Vendor ID to match (if flags says this is valid) */
int vendor_id; uint16_t vendor_id;
/** Product ID to match or LIBUSB_HOTPLUG_MATCH_ANY */ /** Product ID to match (if flags says this is valid) */
int product_id; uint16_t product_id;
/** Device class to match or LIBUSB_HOTPLUG_MATCH_ANY */ /** Device class to match (if flags says this is valid) */
int dev_class; uint8_t dev_class;
/** Hotplug callback flags */
libusb_hotplug_flag flags;
/** Event(s) that will trigger this callback */
libusb_hotplug_event events;
/** Callback function to invoke for matching event/device */ /** Callback function to invoke for matching event/device */
libusb_hotplug_callback_fn cb; libusb_hotplug_callback_fn cb;
@ -59,15 +75,10 @@ struct libusb_hotplug_callback {
/** User data that will be passed to the callback function */ /** User data that will be passed to the callback function */
void *user_data; void *user_data;
/** Callback is marked for deletion */
int needs_free;
/** List this callback is registered in (ctx->hotplug_cbs) */ /** List this callback is registered in (ctx->hotplug_cbs) */
struct list_head list; struct list_head list;
}; };
typedef struct libusb_hotplug_callback libusb_hotplug_callback;
struct libusb_hotplug_message { struct libusb_hotplug_message {
/** The hotplug event that occurred */ /** The hotplug event that occurred */
libusb_hotplug_event event; libusb_hotplug_event event;
@ -79,9 +90,7 @@ struct libusb_hotplug_message {
struct list_head list; struct list_head list;
}; };
typedef struct libusb_hotplug_message libusb_hotplug_message; void usbi_hotplug_deregister(struct libusb_context *ctx, int forced);
void usbi_hotplug_deregister_all(struct libusb_context *ctx);
void usbi_hotplug_match(struct libusb_context *ctx, struct libusb_device *dev, void usbi_hotplug_match(struct libusb_context *ctx, struct libusb_device *dev,
libusb_hotplug_event event); libusb_hotplug_event event);
void usbi_hotplug_notification(struct libusb_context *ctx, struct libusb_device *dev, void usbi_hotplug_notification(struct libusb_context *ctx, struct libusb_device *dev,

View File

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

View File

@ -54,13 +54,19 @@ typedef unsigned __int32 uint32_t;
#include <sys/types.h> #include <sys/types.h>
#endif #endif
#if defined(__linux) || defined(__APPLE__) || defined(__CYGWIN__) || defined(__HAIKU__) #if defined(__linux__) || defined(__APPLE__) || defined(__CYGWIN__) || defined(__HAIKU__)
#include <sys/time.h> #include <sys/time.h>
#endif #endif
#include <time.h> #include <time.h>
#include <limits.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 /* '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 * undefine it so as not to break the current libusb API, because
* libusb_config_descriptor has an 'interface' member * libusb_config_descriptor has an 'interface' member
@ -79,6 +85,8 @@ typedef unsigned __int32 uint32_t;
#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
#define LIBUSB_DEPRECATED_FOR(f) \ #define LIBUSB_DEPRECATED_FOR(f) \
__attribute__((deprecated("Use " #f " instead"))) __attribute__((deprecated("Use " #f " instead")))
#elif __GNUC__ >= 3
#define LIBUSB_DEPRECATED_FOR(f) __attribute__((deprecated))
#else #else
#define LIBUSB_DEPRECATED_FOR(f) #define LIBUSB_DEPRECATED_FOR(f)
#endif /* __GNUC__ */ #endif /* __GNUC__ */
@ -141,7 +149,7 @@ typedef unsigned __int32 uint32_t;
* Internally, LIBUSB_API_VERSION is defined as follows: * Internally, LIBUSB_API_VERSION is defined as follows:
* (libusb major << 24) | (libusb minor << 16) | (16 bit incremental) * (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 */ /* The following is kept for compatibility, but will be deprecated in the future */
#define LIBUSBX_API_VERSION LIBUSB_API_VERSION #define LIBUSBX_API_VERSION LIBUSB_API_VERSION
@ -729,13 +737,7 @@ struct libusb_bos_dev_capability_descriptor {
/** Device Capability type */ /** Device Capability type */
uint8_t bDevCapabilityType; uint8_t bDevCapabilityType;
/** Device Capability data (bLength - 3 bytes) */ /** Device Capability data (bLength - 3 bytes) */
uint8_t dev_capability_data uint8_t dev_capability_data[ZERO_SIZED_ARRAY];
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
[] /* valid C99 code */
#else
[0] /* non-standard, but usually working code */
#endif
;
}; };
/** \ingroup libusb_desc /** \ingroup libusb_desc
@ -760,13 +762,7 @@ struct libusb_bos_descriptor {
uint8_t bNumDeviceCaps; uint8_t bNumDeviceCaps;
/** bNumDeviceCap Device Capability Descriptors */ /** bNumDeviceCap Device Capability Descriptors */
struct libusb_bos_dev_capability_descriptor *dev_capability struct libusb_bos_dev_capability_descriptor *dev_capability[ZERO_SIZED_ARRAY];
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
[] /* valid C99 code */
#else
[0] /* non-standard, but usually working code */
#endif
;
}; };
/** \ingroup libusb_desc /** \ingroup libusb_desc
@ -927,7 +923,7 @@ struct libusb_version {
* sessions allows for your program to use two libraries (or dynamically * sessions allows for your program to use two libraries (or dynamically
* load two modules) which both independently use libusb. This will prevent * load two modules) which both independently use libusb. This will prevent
* interference between the individual libusb users - for example * 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 * libusb_exit() will not destroy resources that the other user is still
* using. * using.
* *
@ -987,6 +983,9 @@ enum libusb_speed {
/** The device is operating at super speed (5000MBit/s). */ /** The device is operating at super speed (5000MBit/s). */
LIBUSB_SPEED_SUPER = 4, LIBUSB_SPEED_SUPER = 4,
/** The device is operating at super speed plus (10000MBit/s). */
LIBUSB_SPEED_SUPER_PLUS = 5,
}; };
/** \ingroup libusb_dev /** \ingroup libusb_dev
@ -1256,13 +1255,7 @@ struct libusb_transfer {
int num_iso_packets; int num_iso_packets;
/** Isochronous packet descriptors, for isochronous transfers only. */ /** Isochronous packet descriptors, for isochronous transfers only. */
struct libusb_iso_packet_descriptor iso_packet_desc struct libusb_iso_packet_descriptor iso_packet_desc[ZERO_SIZED_ARRAY];
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
[] /* valid C99 code */
#else
[0] /* non-standard, but usually working code */
#endif
;
}; };
/** \ingroup libusb_misc /** \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_NONE (0) : no messages ever printed by the library (default)
* - LIBUSB_LOG_LEVEL_ERROR (1) : error messages are printed to stderr * - 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_WARNING (2) : warning and error messages are printed to stderr
* - LIBUSB_LOG_LEVEL_INFO (3) : informational messages are printed to stdout, warning * - LIBUSB_LOG_LEVEL_INFO (3) : informational messages are printed to stderr
* and error messages are printed to stderr * - LIBUSB_LOG_LEVEL_DEBUG (4) : debug and informational messages are printed to stderr
* - LIBUSB_LOG_LEVEL_DEBUG (4) : debug and informational messages are printed to stdout,
* warnings and errors to stderr
*/ */
enum libusb_log_level { enum libusb_log_level {
LIBUSB_LOG_LEVEL_NONE = 0, LIBUSB_LOG_LEVEL_NONE = 0,
LIBUSB_LOG_LEVEL_ERROR, LIBUSB_LOG_LEVEL_ERROR = 1,
LIBUSB_LOG_LEVEL_WARNING, LIBUSB_LOG_LEVEL_WARNING = 2,
LIBUSB_LOG_LEVEL_INFO, LIBUSB_LOG_LEVEL_INFO = 3,
LIBUSB_LOG_LEVEL_DEBUG, LIBUSB_LOG_LEVEL_DEBUG = 4,
}; };
int LIBUSB_CALL libusb_init(libusb_context **ctx); int LIBUSB_CALL libusb_init(libusb_context **ctx);
void LIBUSB_CALL libusb_exit(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); void LIBUSB_CALL libusb_set_debug(libusb_context *ctx, int level);
const struct libusb_version * LIBUSB_CALL libusb_get_version(void); const struct libusb_version * LIBUSB_CALL libusb_get_version(void);
int LIBUSB_CALL libusb_has_capability(uint32_t capability); 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, void LIBUSB_CALL libusb_hotplug_deregister_callback(libusb_context *ctx,
libusb_hotplug_callback_handle callback_handle); 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 #ifdef __cplusplus
} }
#endif #endif

View File

@ -39,6 +39,20 @@
#include "libusb.h" #include "libusb.h"
#include "version.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: /* Inside the libusb code, mark all public functions as follows:
* return_type API_EXPORTED function_name(params) { ... } * return_type API_EXPORTED function_name(params) { ... }
* But if the function returns a pointer, mark it as follows: * 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; 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) static inline void *usbi_reallocf(void *ptr, size_t size)
{ {
void *ret = realloc(ptr, 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); \ const typeof( ((type *)0)->member ) *mptr = (ptr); \
(type *)( (char *)mptr - offsetof(type,member) );}) (type *)( (char *)mptr - offsetof(type,member) );})
#ifndef CLAMP
#define CLAMP(val, min, max) ((val) < (min) ? (min) : ((val) > (max) ? (max) : (val)))
#endif
#ifndef MIN #ifndef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b)) #define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif #endif
@ -175,29 +205,33 @@ static inline void *usbi_reallocf(void *ptr, size_t size)
} while (0) } while (0)
#endif #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, void usbi_log(struct libusb_context *ctx, enum libusb_log_level level,
const char *function, const char *format, ...); const char *function, const char *format, ...);
void usbi_log_v(struct libusb_context *ctx, enum libusb_log_level 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 *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_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_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) \ #define LOG_BODY(ctxt, level) \
{ \ { \
va_list args; \ 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); \ usbi_log_v(ctxt, level, "", format, args); \
va_end(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, ...) static inline void usbi_err(struct libusb_context *ctx, const char *format, ...)
LOG_BODY(ctx, LIBUSB_LOG_LEVEL_ERROR) 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, ...) static inline void usbi_dbg(const char *format, ...)
LOG_BODY(NULL, LIBUSB_LOG_LEVEL_DEBUG) 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) \ #define USBI_GET_CONTEXT(ctx) \
do { \ do { \
@ -254,8 +290,10 @@ extern struct libusb_context *usbi_default_context;
struct pollfd; struct pollfd;
struct libusb_context { struct libusb_context {
int debug; #if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING)
enum libusb_log_level debug;
int debug_fixed; int debug_fixed;
#endif
/* internal event pipe, used for signalling occurrence of an internal event. */ /* internal event pipe, used for signalling occurrence of an internal event. */
int event_pipe[2]; int event_pipe[2];
@ -270,6 +308,7 @@ struct libusb_context {
/* A list of registered hotplug callbacks */ /* A list of registered hotplug callbacks */
struct list_head hotplug_cbs; struct list_head hotplug_cbs;
libusb_hotplug_callback_handle next_hotplug_cb_handle;
usbi_mutex_t hotplug_cbs_lock; usbi_mutex_t hotplug_cbs_lock;
/* this is a list of in-flight transfer handles, sorted by timeout /* this is a list of in-flight transfer handles, sorted by timeout
@ -331,6 +370,8 @@ struct libusb_context {
#endif #endif
struct list_head list; struct list_head list;
PTR_ALIGNED unsigned char os_priv[ZERO_SIZED_ARRAY];
}; };
enum usbi_event_flags { enum usbi_event_flags {
@ -339,6 +380,9 @@ enum usbi_event_flags {
/* The user has interrupted the event handler */ /* The user has interrupted the event handler */
USBI_EVENT_USER_INTERRUPT = 1 << 1, 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 */ /* Macros for managing event handling state */
@ -383,17 +427,7 @@ struct libusb_device {
struct libusb_device_descriptor device_descriptor; struct libusb_device_descriptor device_descriptor;
int attached; int attached;
unsigned char os_priv PTR_ALIGNED unsigned char os_priv[ZERO_SIZED_ARRAY];
#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
}; };
struct libusb_device_handle { struct libusb_device_handle {
@ -404,17 +438,8 @@ struct libusb_device_handle {
struct list_head list; struct list_head list;
struct libusb_device *dev; struct libusb_device *dev;
int auto_detach_kernel_driver; int auto_detach_kernel_driver;
unsigned char os_priv
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) PTR_ALIGNED unsigned char os_priv[ZERO_SIZED_ARRAY];
[] /* valid C99 code */
#else
[0] /* non-standard, but usually working code */
#endif
#if defined(OS_SUNOS)
__attribute__ ((aligned (8)));
#else
;
#endif
}; };
enum { enum {
@ -540,14 +565,6 @@ int usbi_clear_event(struct libusb_context *ctx);
#include "os/poll_windows.h" #include "os/poll_windows.h"
#endif #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 { struct usbi_pollfd {
/* must come first */ /* must come first */
struct libusb_pollfd pollfd; struct libusb_pollfd pollfd;
@ -568,13 +585,7 @@ void usbi_remove_pollfd(struct libusb_context *ctx, int fd);
struct discovered_devs { struct discovered_devs {
size_t len; size_t len;
size_t capacity; size_t capacity;
struct libusb_device *devices struct libusb_device *devices[ZERO_SIZED_ARRAY];
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
[] /* valid C99 code */
#else
[0] /* non-standard, but usually working code */
#endif
;
}; };
struct discovered_devs *discovered_devs_append( struct discovered_devs *discovered_devs_append(
@ -607,7 +618,17 @@ struct usbi_os_backend {
* *
* This function is called when the user deinitializes the library. * 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 /* Enumerate all the USB devices on the system, returning them in a list
* of discovered devices. * of discovered devices.
@ -1110,6 +1131,11 @@ struct usbi_os_backend {
clockid_t (*get_timerfd_clockid)(void); clockid_t (*get_timerfd_clockid)(void);
#endif #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. /* Number of bytes to reserve for per-device private backend data.
* This private data area is accessible through the "os_priv" field of * This private data area is accessible through the "os_priv" field of
* struct libusb_device. */ * struct libusb_device. */
@ -1127,17 +1153,7 @@ struct usbi_os_backend {
size_t transfer_priv_size; size_t transfer_priv_size;
}; };
extern const struct usbi_os_backend * const usbi_backend; extern const struct usbi_os_backend 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 struct list_head active_contexts_list; extern struct list_head active_contexts_list;
extern usbi_mutex_static_t active_contexts_lock; extern usbi_mutex_static_t active_contexts_lock;

View File

@ -1,7 +1,7 @@
/* -*- Mode: C; indent-tabs-mode:nil -*- */ /* -*- Mode: C; indent-tabs-mode:nil -*- */
/* /*
* darwin backend for libusb 1.0 * 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 * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
@ -36,6 +36,10 @@
#include <mach/mach_host.h> #include <mach/mach_host.h>
#include <mach/mach_port.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> #include <AvailabilityMacros.h>
#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 && MAC_OS_X_VERSION_MIN_REQUIRED < 101200 #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 && MAC_OS_X_VERSION_MIN_REQUIRED < 101200
#include <objc/objc-auto.h> #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) #define libusb_darwin_atomic_fetch_add(x, y) (OSAtomicAdd32Barrier(y, x) - y)
static volatile int32_t initCount = 0; 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 #endif
#include "darwin_usb.h" #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; 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_realtime;
static clock_serv_t clock_monotonic; static clock_serv_t clock_monotonic;
#endif
static CFRunLoopRef libusb_darwin_acfl = NULL; /* event cf loop */ static CFRunLoopRef libusb_darwin_acfl = NULL; /* event cf loop */
static CFRunLoopSourceRef libusb_darwin_acfls = NULL; /* shutdown signal for 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 usbi_mutex_t darwin_cached_devices_lock = PTHREAD_MUTEX_INITIALIZER;
static struct list_head darwin_cached_devices = {&darwin_cached_devices, &darwin_cached_devices}; 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)) #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, &kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks); &kCFTypeDictionaryValueCallBacks);
if (propertyMatchDict) { /* there are no unsigned CFNumber types so treat the value as signed. the OS seems to do this
/* there are no unsigned CFNumber types so treat the value as signed. the os seems to do this internally (CFNumberType of locationID is kCFNumberSInt32Type) */
internally (CFNumberType of locationID is 3) */ CFTypeRef locationCF = CFNumberCreate (NULL, kCFNumberSInt32Type, &location);
CFTypeRef locationCF = CFNumberCreate (NULL, kCFNumberSInt32Type, &location);
if (propertyMatchDict && locationCF) {
CFDictionarySetValue (propertyMatchDict, CFSTR(kUSBDevicePropertyLocationID), locationCF); CFDictionarySetValue (propertyMatchDict, CFSTR(kUSBDevicePropertyLocationID), locationCF);
/* release our reference to the CFNumber (CFDictionarySetValue retains it) */
CFRelease (locationCF);
CFDictionarySetValue (matchingDict, CFSTR(kIOPropertyMatchKey), propertyMatchDict); 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 */ /* 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); 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) { static void darwin_devices_attached (void *ptr, io_iterator_t add_devices) {
UNUSED(ptr);
struct libusb_context *ctx; struct libusb_context *ctx;
io_service_t service; 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))) { while ((service = IOIteratorNext(add_devices))) {
/* add this device to each active context's device list */ /* add this device to each active context's device list */
list_for_each_entry(ctx, &active_contexts_list, list, struct libusb_context) { list_for_each_entry(ctx, &active_contexts_list, list, struct libusb_context) {
process_new_device (ctx, service);; process_new_device (ctx, service);
} }
IOObjectRelease(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) { static void darwin_devices_detached (void *ptr, io_iterator_t rem_devices) {
UNUSED(ptr);
struct libusb_device *dev = NULL; struct libusb_device *dev = NULL;
struct libusb_context *ctx; struct libusb_context *ctx;
struct darwin_cached_device *old_device; struct darwin_cached_device *old_device;
@ -516,7 +533,6 @@ static void darwin_check_version (void) {
} }
static int darwin_init(struct libusb_context *ctx) { static int darwin_init(struct libusb_context *ctx) {
host_name_port_t host_self;
int rc; int rc;
rc = pthread_once (&darwin_init_once, darwin_check_version); 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) { 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_self = mach_host_self();
host_get_clock_service(host_self, CALENDAR_CLOCK, &clock_realtime); host_get_clock_service(host_self, CALENDAR_CLOCK, &clock_realtime);
host_get_clock_service(host_self, SYSTEM_CLOCK, &clock_monotonic); host_get_clock_service(host_self, SYSTEM_CLOCK, &clock_monotonic);
mach_port_deallocate(mach_task_self(), host_self); mach_port_deallocate(mach_task_self(), host_self);
#endif
pthread_create (&libusb_darwin_at, NULL, darwin_event_thread_main, ctx); 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; 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 (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_realtime);
mach_port_deallocate(mach_task_self(), clock_monotonic); mach_port_deallocate(mach_task_self(), clock_monotonic);
#endif
/* stop the event runloop and wait for the thread to terminate. */ /* stop the event runloop and wait for the thread to terminate. */
CFRunLoopSourceSignal(libusb_darwin_acfls); CFRunLoopSourceSignal(libusb_darwin_acfls);
@ -866,14 +888,29 @@ static int get_device_port (io_service_t service, UInt8 *port) {
return ret; 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, static int darwin_get_cached_device(struct libusb_context *ctx, io_service_t service,
struct darwin_cached_device **cached_out) { struct darwin_cached_device **cached_out) {
struct darwin_cached_device *new_device; struct darwin_cached_device *new_device;
UInt64 sessionID = 0, parent_sessionID = 0; UInt64 sessionID = 0, parent_sessionID = 0;
int ret = LIBUSB_SUCCESS; int ret = LIBUSB_SUCCESS;
usb_device_t **device; usb_device_t **device;
io_service_t parent;
kern_return_t result;
UInt8 port = 0; UInt8 port = 0;
/* get some info from the io registry */ /* 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); usbi_dbg("finding cached device for sessionID 0x%" PRIx64, sessionID);
result = IORegistryEntryGetParentEntry (service, kIOUSBPlane, &parent); if (get_device_parent_sessionID(service, &parent_sessionID)) {
usbi_dbg("parent sessionID: 0x%" PRIx64, parent_sessionID);
if (kIOReturnSuccess == result) {
(void) get_ioregistry_value_number (parent, CFSTR("sessionID"), kCFNumberSInt64Type, &parent_sessionID);
IOObjectRelease(parent);
} }
usbi_mutex_lock(&darwin_cached_devices_lock); 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 kUSBDeviceSpeedLow: dev->speed = LIBUSB_SPEED_LOW; break;
case kUSBDeviceSpeedFull: dev->speed = LIBUSB_SPEED_FULL; break; case kUSBDeviceSpeedFull: dev->speed = LIBUSB_SPEED_FULL; break;
case kUSBDeviceSpeedHigh: dev->speed = LIBUSB_SPEED_HIGH; 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; 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 #endif
default: default:
usbi_warn (ctx, "Got unknown device speed %d", devSpeed); 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; kern_return_t kresult;
u_int8_t numep, direction, number; UInt8 numep, direction, number;
u_int8_t dont_care1, dont_care3; UInt8 dont_care1, dont_care3;
u_int16_t dont_care2; UInt16 dont_care2;
int rc; int rc;
usbi_dbg ("building table of endpoints."); 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) { static int darwin_clock_gettime(int clk_id, struct timespec *tp) {
#if !OSX_USE_CLOCK_GETTIME
mach_timespec_t sys_time; mach_timespec_t sys_time;
clock_serv_t clock_ref; 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; tp->tv_nsec = sys_time.tv_nsec;
return 0; 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 #if InterfaceVersion >= 550
@ -2047,7 +2095,7 @@ static int darwin_free_streams (struct libusb_device_handle *dev_handle, unsigne
} }
#endif #endif
const struct usbi_os_backend darwin_backend = { const struct usbi_os_backend usbi_backend = {
.name = "Darwin", .name = "Darwin",
.caps = 0, .caps = 0,
.init = darwin_init, .init = darwin_init,

View File

@ -28,37 +28,58 @@
#include <IOKit/IOCFPlugIn.h> #include <IOKit/IOCFPlugIn.h>
/* IOUSBInterfaceInferface */ /* 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 usb_interface_t IOUSBInterfaceInterface700
#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID700 #define InterfaceInterfaceID kIOUSBInterfaceInterfaceID700
#define InterfaceVersion 700 #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 usb_interface_t IOUSBInterfaceInterface550
#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID550 #define InterfaceInterfaceID kIOUSBInterfaceInterfaceID550
#define InterfaceVersion 550 #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 usb_interface_t IOUSBInterfaceInterface500
#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID500 #define InterfaceInterfaceID kIOUSBInterfaceInterfaceID500
#define InterfaceVersion 500 #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 usb_interface_t IOUSBInterfaceInterface300
#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID300 #define InterfaceInterfaceID kIOUSBInterfaceInterfaceID300
#define InterfaceVersion 300 #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 usb_interface_t IOUSBInterfaceInterface245
#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID245 #define InterfaceInterfaceID kIOUSBInterfaceInterfaceID245
#define InterfaceVersion 245 #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 usb_interface_t IOUSBInterfaceInterface220
#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID220 #define InterfaceInterfaceID kIOUSBInterfaceInterfaceID220
@ -66,43 +87,57 @@
#else #else
#error "IOUSBFamily is too old. Please upgrade your OS" #error "IOUSBFamily is too old. Please upgrade your SDK and/or deployment target"
#endif #endif
/* IOUSBDeviceInterface */ /* 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 usb_device_t IOUSBDeviceInterface500
#define DeviceInterfaceID kIOUSBDeviceInterfaceID500 #define DeviceInterfaceID kIOUSBDeviceInterfaceID500
#define DeviceVersion 500 #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 usb_device_t IOUSBDeviceInterface320
#define DeviceInterfaceID kIOUSBDeviceInterfaceID320 #define DeviceInterfaceID kIOUSBDeviceInterfaceID320
#define DeviceVersion 320 #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 usb_device_t IOUSBDeviceInterface300
#define DeviceInterfaceID kIOUSBDeviceInterfaceID300 #define DeviceInterfaceID kIOUSBDeviceInterfaceID300
#define DeviceVersion 300 #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 usb_device_t IOUSBDeviceInterface245
#define DeviceInterfaceID kIOUSBDeviceInterfaceID245 #define DeviceInterfaceID kIOUSBDeviceInterfaceID245
#define DeviceVersion 245 #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 usb_device_t IOUSBDeviceInterface197
#define DeviceInterfaceID kIOUSBDeviceInterfaceID197 #define DeviceInterfaceID kIOUSBDeviceInterfaceID197
#define DeviceVersion 197 #define DeviceVersion 197
#else #else
#error "IOUSBFamily is too old. Please upgrade your OS" #error "IOUSBFamily is too old. Please upgrade your SDK and/or deployment target"
#endif #endif

View File

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

View File

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

View File

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

View File

@ -81,6 +81,19 @@ static const char *usbfs_path = NULL;
/* use usbdev*.* device names in /dev instead of the usbfs bus directories */ /* use usbdev*.* device names in /dev instead of the usbfs bus directories */
static int usbdev_names = 0; 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 /* 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 * 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 * 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); static int linux_default_scan_devices (struct libusb_context *ctx);
#endif #endif
struct kernel_version {
int major;
int minor;
int sublevel;
};
struct linux_device_priv { struct linux_device_priv {
char *sysfs_dir; char *sysfs_dir;
unsigned char *descriptors; unsigned char *descriptors;
@ -180,6 +199,16 @@ struct linux_transfer_priv {
int iso_packet_offset; 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) static int _get_usbfs_fd(struct libusb_device *dev, mode_t mode, int silent)
{ {
struct libusb_context *ctx = DEVICE_CTX(dev); 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", snprintf(path, PATH_MAX, "%s/%03d/%03d",
usbfs_path, dev->bus_number, dev->device_address); usbfs_path, dev->bus_number, dev->device_address);
fd = open(path, mode); fd = _open(path, mode);
if (fd != -1) if (fd != -1)
return fd; /* Success */ 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.*/ /* Wait 10ms for USB device path creation.*/
nanosleep(&(struct timespec){delay / 1000000, (delay * 1000) % 1000000000UL}, NULL); nanosleep(&(struct timespec){delay / 1000000, (delay * 1000) % 1000000000UL}, NULL);
fd = open(path, mode); fd = _open(path, mode);
if (fd != -1) if (fd != -1)
return fd; /* Success */ return fd; /* Success */
} }
@ -342,39 +371,59 @@ static clockid_t find_monotonic_clock(void)
return CLOCK_REALTIME; 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; struct utsname uts;
int atoms, kmajor, kminor, ksublevel; int atoms;
if (uname(&uts) < 0) if (uname(&uts) < 0) {
return -1; usbi_err(ctx, "uname failed, errno %d", errno);
atoms = sscanf(uts.release, "%d.%d.%d", &kmajor, &kminor, &ksublevel);
if (atoms < 1)
return -1; return -1;
}
if (kmajor > major) 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;
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; return 1;
if (kmajor < major) else if (ver->major < major)
return 0; return 0;
/* kmajor == major */ /* kmajor == major */
if (atoms < 2) if (ver->minor == -1 && ver->sublevel == -1)
return 0 == minor && 0 == sublevel; return 0 == minor && 0 == sublevel;
if (kminor > minor) else if (ver->minor > minor)
return 1; return 1;
if (kminor < minor) else if (ver->minor < minor)
return 0; return 0;
/* kminor == minor */ /* kminor == minor */
if (atoms < 3) if (ver->sublevel == -1)
return 0 == sublevel; return 0 == sublevel;
return ksublevel >= sublevel; return ver->sublevel >= sublevel;
} }
static int op_init(struct libusb_context *ctx) static int op_init(struct libusb_context *ctx)
{ {
struct kernel_version kversion;
struct stat statbuf; struct stat statbuf;
int r; int r;
@ -387,13 +436,17 @@ static int op_init(struct libusb_context *ctx)
if (monotonic_clkid == -1) if (monotonic_clkid == -1)
monotonic_clkid = find_monotonic_clock(); 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) { if (supports_flag_bulk_continuation == -1) {
/* bulk continuation URB flag available from Linux 2.6.32 */ /* bulk continuation URB flag available from Linux 2.6.32 */
supports_flag_bulk_continuation = kernel_version_ge(2,6,32); supports_flag_bulk_continuation = kernel_version_ge(&kversion,2,6,32);
if (supports_flag_bulk_continuation == -1) {
usbi_err(ctx, "error checking for bulk continuation support");
return LIBUSB_ERROR_OTHER;
}
} }
if (supports_flag_bulk_continuation) if (supports_flag_bulk_continuation)
@ -401,32 +454,31 @@ static int op_init(struct libusb_context *ctx)
if (-1 == supports_flag_zero_packet) { if (-1 == supports_flag_zero_packet) {
/* zero length packet URB flag fixed since Linux 2.6.31 */ /* zero length packet URB flag fixed since Linux 2.6.31 */
supports_flag_zero_packet = kernel_version_ge(2,6,31); supports_flag_zero_packet = kernel_version_ge(&kversion,2,6,31);
if (-1 == supports_flag_zero_packet) {
usbi_err(ctx, "error checking for zero length packet support");
return LIBUSB_ERROR_OTHER;
}
} }
if (supports_flag_zero_packet) if (supports_flag_zero_packet)
usbi_dbg("zero length packet flag supported"); 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) { if (-1 == sysfs_has_descriptors) {
/* sysfs descriptors has all descriptors since Linux 2.6.26 */ /* sysfs descriptors has all descriptors since Linux 2.6.26 */
sysfs_has_descriptors = kernel_version_ge(2,6,26); sysfs_has_descriptors = kernel_version_ge(&kversion,2,6,26);
if (-1 == sysfs_has_descriptors) {
usbi_err(ctx, "error checking for sysfs descriptors");
return LIBUSB_ERROR_OTHER;
}
} }
if (-1 == sysfs_can_relate_devices) { if (-1 == sysfs_can_relate_devices) {
/* sysfs has busnum since Linux 2.6.22 */ /* sysfs has busnum since Linux 2.6.22 */
sysfs_can_relate_devices = kernel_version_ge(2,6,22); sysfs_can_relate_devices = kernel_version_ge(&kversion,2,6,22);
if (-1 == sysfs_can_relate_devices) {
usbi_err(ctx, "error checking for sysfs busnum");
return LIBUSB_ERROR_OTHER;
}
} }
if (sysfs_can_relate_devices || sysfs_has_descriptors) { if (sysfs_can_relate_devices || sysfs_has_descriptors) {
@ -463,8 +515,9 @@ static int op_init(struct libusb_context *ctx)
return r; 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); usbi_mutex_static_lock(&linux_hotplug_startstop_lock);
assert(init_count != 0); assert(init_count != 0);
if (!--init_count) { 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", snprintf(filename, PATH_MAX, "%s/%s/%s",
SYSFS_DEVICE_PATH, priv->sysfs_dir, attr); SYSFS_DEVICE_PATH, priv->sysfs_dir, attr);
fd = open(filename, O_RDONLY); fd = _open(filename, O_RDONLY);
if (fd < 0) { if (fd < 0) {
usbi_err(DEVICE_CTX(dev), usbi_err(DEVICE_CTX(dev),
"open %s failed ret=%d errno=%d", filename, fd, errno); "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]; char filename[PATH_MAX];
FILE *f; FILE *f;
int r, value; int fd, r, value;
snprintf(filename, PATH_MAX, "%s/%s/%s", SYSFS_DEVICE_PATH, snprintf(filename, PATH_MAX, "%s/%s/%s", SYSFS_DEVICE_PATH,
devname, attr); devname, attr);
f = fopen(filename, "r"); fd = _open(filename, O_RDONLY);
if (f == NULL) { if (fd == -1) {
if (errno == ENOENT) { if (errno == ENOENT) {
/* File doesn't exist. Assume the device has been /* File doesn't exist. Assume the device has been
disconnected (see trac ticket #70). */ disconnected (see trac ticket #70). */
@ -557,6 +610,13 @@ static int __read_sysfs_attr(struct libusb_context *ctx,
return LIBUSB_ERROR_IO; 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); r = fscanf(f, "%d", &value);
fclose(f); fclose(f);
if (r != 1) { if (r != 1) {
@ -806,7 +866,7 @@ static int op_get_active_config_descriptor(struct libusb_device *dev,
if (r < 0) if (r < 0)
return r; return r;
len = MIN(len, r); len = MIN(len, (size_t)r);
memcpy(buffer, config_desc, len); memcpy(buffer, config_desc, len);
return len; return len;
} }
@ -836,7 +896,7 @@ static int op_get_config_descriptor(struct libusb_device *dev,
descriptors += r; descriptors += r;
} }
len = MIN(len, r); len = MIN(len, (size_t)r);
memcpy(buffer, descriptors, len); memcpy(buffer, descriptors, len);
return 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 12: dev->speed = LIBUSB_SPEED_FULL; break;
case 480: dev->speed = LIBUSB_SPEED_HIGH; break; case 480: dev->speed = LIBUSB_SPEED_HIGH; break;
case 5000: dev->speed = LIBUSB_SPEED_SUPER; break; case 5000: dev->speed = LIBUSB_SPEED_SUPER; break;
case 10000: dev->speed = LIBUSB_SPEED_SUPER_PLUS; break;
default: default:
usbi_warn(DEVICE_CTX(dev), "Unknown device speed: %d Mbps", speed); 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); DIR *devices = opendir(SYSFS_DEVICE_PATH);
struct dirent *entry; struct dirent *entry;
int r = LIBUSB_ERROR_IO; int num_devices = 0;
int num_enumerated = 0;
if (!devices) { if (!devices) {
usbi_err(ctx, "opendir devices failed errno=%d", errno); usbi_err(ctx, "opendir devices failed errno=%d", errno);
return r; return LIBUSB_ERROR_IO;
} }
while ((entry = readdir(devices))) { while ((entry = readdir(devices))) {
@ -1251,16 +1313,23 @@ static int sysfs_get_device_list(struct libusb_context *ctx)
|| strchr(entry->d_name, ':')) || strchr(entry->d_name, ':'))
continue; continue;
num_devices++;
if (sysfs_scan_device(ctx, entry->d_name)) { if (sysfs_scan_device(ctx, entry->d_name)) {
usbi_dbg("failed to enumerate dir entry %s", entry->d_name); usbi_dbg("failed to enumerate dir entry %s", entry->d_name);
continue; continue;
} }
r = 0; num_enumerated++;
} }
closedir(devices); 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) 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"); strcpy(dc.driver, "usbfs");
dc.flags = USBFS_DISCONNECT_CLAIM_EXCEPT_DRIVER; dc.flags = USBFS_DISCONNECT_CLAIM_EXCEPT_DRIVER;
r = ioctl(fd, IOCTL_USBFS_DISCONNECT_CLAIM, &dc); r = ioctl(fd, IOCTL_USBFS_DISCONNECT_CLAIM, &dc);
if (r == 0 || (r != 0 && errno != ENOTTY)) { if (r != 0 && errno != ENOTTY) {
if (r == 0)
return 0;
switch (errno) { switch (errno) {
case EBUSY: case EBUSY:
return LIBUSB_ERROR_BUSY; 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), usbi_err(HANDLE_CTX(handle),
"disconnect-and-claim failed errno %d", errno); "disconnect-and-claim failed errno %d", errno);
return LIBUSB_ERROR_OTHER; return LIBUSB_ERROR_OTHER;
} } else if (r == 0)
return 0;
/* Fallback code for kernels which don't support the /* Fallback code for kernels which don't support the
disconnect-and-claim ioctl */ disconnect-and-claim ioctl */
@ -1973,38 +2040,43 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer)
struct linux_device_handle_priv *dpriv = struct linux_device_handle_priv *dpriv =
_device_handle_priv(transfer->dev_handle); _device_handle_priv(transfer->dev_handle);
struct usbfs_urb **urbs; struct usbfs_urb **urbs;
size_t alloc_size;
int num_packets = transfer->num_iso_packets; int num_packets = transfer->num_iso_packets;
int i; int num_packets_remaining;
int this_urb_len = 0; int i, j;
int num_urbs = 1; int num_urbs;
int packet_offset = 0;
unsigned int packet_len; unsigned int packet_len;
unsigned int total_len = 0;
unsigned char *urb_buffer = transfer->buffer; unsigned char *urb_buffer = transfer->buffer;
/* usbfs places arbitrary limits on iso URBs. this limit has changed if (num_packets < 1)
* at least three times, and it's difficult to accurately detect which return LIBUSB_ERROR_INVALID_PARAM;
* 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.
*/
/* calculate how many URBs we need */ /* usbfs places arbitrary limits on iso URBs. this limit has changed
* 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.
*/
for (i = 0; i < num_packets; i++) { 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; packet_len = transfer->iso_packet_desc[i].length;
if (packet_len > space_remaining) { if (packet_len > max_iso_packet_len) {
num_urbs++; usbi_warn(TRANSFER_CTX(transfer),
this_urb_len = packet_len; "iso packet length of %u bytes exceeds maximum of %u bytes",
/* check that we can actually support this packet length */ packet_len, max_iso_packet_len);
if (this_urb_len > MAX_ISO_BUFFER_LENGTH) return LIBUSB_ERROR_INVALID_PARAM;
return LIBUSB_ERROR_INVALID_PARAM;
} else {
this_urb_len += packet_len;
} }
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)); urbs = calloc(num_urbs, sizeof(*urbs));
if (!urbs) if (!urbs)
@ -2017,31 +2089,15 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer)
tpriv->iso_packet_offset = 0; tpriv->iso_packet_offset = 0;
/* allocate + initialize each URB with the correct number of packets */ /* 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; struct usbfs_urb *urb;
unsigned int space_remaining_in_urb = MAX_ISO_BUFFER_LENGTH; size_t alloc_size;
int urb_packet_offset = 0;
unsigned char *urb_buffer_orig = urb_buffer;
int j;
int k; 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) 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); urb = calloc(1, alloc_size);
if (!urb) { if (!urb) {
free_iso_urbs(tpriv); free_iso_urbs(tpriv);
@ -2050,10 +2106,10 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer)
urbs[i] = urb; urbs[i] = urb;
/* populate packet lengths */ /* populate packet lengths */
for (j = 0, k = packet_offset - urb_packet_offset; for (k = 0; k < num_packets_in_urb; j++, k++) {
k < packet_offset; k++, j++) { packet_len = transfer->iso_packet_desc[j].length;
packet_len = transfer->iso_packet_desc[k].length; urb->buffer_length += packet_len;
urb->iso_frame_desc[j].length = packet_len; urb->iso_frame_desc[k].length = packet_len;
} }
urb->usercontext = itransfer; urb->usercontext = itransfer;
@ -2061,8 +2117,11 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer)
/* FIXME: interface for non-ASAP data? */ /* FIXME: interface for non-ASAP data? */
urb->flags = USBFS_URB_ISO_ASAP; urb->flags = USBFS_URB_ISO_ASAP;
urb->endpoint = transfer->endpoint; urb->endpoint = transfer->endpoint;
urb->number_of_packets = urb_packet_offset; urb->number_of_packets = num_packets_in_urb;
urb->buffer = urb_buffer_orig; urb->buffer = urb_buffer;
urb_buffer += urb->buffer_length;
num_packets_remaining -= num_packets_in_urb;
} }
/* submit URBs */ /* submit URBs */
@ -2075,6 +2134,10 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer)
usbi_warn(TRANSFER_CTX(transfer), usbi_warn(TRANSFER_CTX(transfer),
"submiturb failed, transfer too large"); "submiturb failed, transfer too large");
r = LIBUSB_ERROR_INVALID_PARAM; 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 { } else {
usbi_err(TRANSFER_CTX(transfer), usbi_err(TRANSFER_CTX(transfer),
"submiturb failed error %d errno=%d", r, errno); "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); USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
struct linux_transfer_priv *tpriv = usbi_transfer_get_os_priv(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) { switch (transfer->type) {
case LIBUSB_TRANSFER_TYPE_CONTROL: case LIBUSB_TRANSFER_TYPE_CONTROL:
case LIBUSB_TRANSFER_TYPE_BULK: case LIBUSB_TRANSFER_TYPE_BULK:
@ -2685,7 +2747,7 @@ static clockid_t op_get_timerfd_clockid(void)
} }
#endif #endif
const struct usbi_os_backend linux_usbfs_backend = { const struct usbi_os_backend usbi_backend = {
.name = "Linux usbfs", .name = "Linux usbfs",
.caps = USBI_CAP_HAS_HID_ACCESS|USBI_CAP_SUPPORTS_DETACH_KERNEL_DRIVER, .caps = USBI_CAP_HAS_HID_ACCESS|USBI_CAP_SUPPORTS_DETACH_KERNEL_DRIVER,
.init = op_init, .init = op_init,

View File

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

View File

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

View File

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

View File

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

View File

@ -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;
}

View File

@ -2,6 +2,7 @@
* Windows compat: POSIX compatibility wrapper * Windows compat: POSIX compatibility wrapper
* Copyright © 2012-2013 RealVNC Ltd. * Copyright © 2012-2013 RealVNC Ltd.
* Copyright © 2009-2010 Pete Batard <pete@akeo.ie> * 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. * With contributions from Michael Plante, Orin Eman et al.
* Parts of poll implementation from libusb-win32, by Stephan Meyer 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) #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 MAX_FDS 256
#define POLLIN 0x0001 /* There is data to read */ #define POLLIN 0x0001 /* There is data to read */
@ -65,46 +51,26 @@ extern int windows_version;
#define POLLNVAL 0x0020 /* Invalid request: fd not open */ #define POLLNVAL 0x0020 /* Invalid request: fd not open */
struct pollfd { struct pollfd {
int fd; /* file descriptor */ int fd; /* file descriptor */
short events; /* requested events */ short events; /* requested events */
short revents; /* returned events */ short revents; /* returned events */
}; };
// access modes
enum rw_type {
RW_NONE,
RW_READ,
RW_WRITE,
};
// fd struct that can be used for polling on Windows
typedef int cancel_transfer(struct usbi_transfer *itransfer);
struct winfd { struct winfd {
int fd; // what's exposed to libusb core 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
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)
}; };
extern const struct winfd INVALID_WINFD; extern const struct winfd INVALID_WINFD;
struct winfd usbi_create_fd(void);
int usbi_pipe(int pipefd[2]); int usbi_pipe(int pipefd[2]);
int usbi_poll(struct pollfd *fds, unsigned int nfds, int timeout); 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_write(int fd, const void *buf, size_t count);
ssize_t usbi_read(int fd, void *buf, size_t count); ssize_t usbi_read(int fd, void *buf, size_t count);
int usbi_close(int fd); 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 * Timeval operations
*/ */

View File

@ -21,6 +21,7 @@
#include <sys/time.h> #include <sys/time.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/list.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <strings.h> #include <strings.h>
#include <errno.h> #include <errno.h>
@ -28,21 +29,34 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <wait.h>
#include <unistd.h> #include <unistd.h>
#include <aio.h> #include <aio.h>
#include <libdevinfo.h> #include <libdevinfo.h>
#include <sys/nvpair.h>
#include <sys/devctl.h>
#include <sys/usb/clients/ugen/usb_ugen.h> #include <sys/usb/clients/ugen/usb_ugen.h>
#include <errno.h>
#include <sys/usb/usba.h> #include <sys/usb/usba.h>
#include <sys/pci.h> #include <sys/pci.h>
#include "libusbi.h" #include "libusbi.h"
#include "sunos_usb.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 * Backend functions
*/ */
static int sunos_init(struct libusb_context *); 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 *, static int sunos_get_device_list(struct libusb_context *,
struct discovered_devs **); struct discovered_devs **);
static int sunos_open(struct libusb_device_handle *); 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 void sunos_clear_transfer_priv(struct usbi_transfer *);
static int sunos_handle_transfer_completion(struct usbi_transfer *); static int sunos_handle_transfer_completion(struct usbi_transfer *);
static int sunos_clock_gettime(int, struct timespec *); 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 * Private functions
@ -79,11 +249,229 @@ static int sunos_init(struct libusb_context *ctx)
return (LIBUSB_SUCCESS); return (LIBUSB_SUCCESS);
} }
static void sunos_exit(void) static void sunos_exit(struct libusb_context *ctx)
{ {
usbi_dbg(""); 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 static int
sunos_fill_in_dev_info(di_node_t node, struct libusb_device *dev) 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; uint8_t *rdata;
struct libusb_device_descriptor *descr; struct libusb_device_descriptor *descr;
sunos_dev_priv_t *dpriv = (sunos_dev_priv_t *)dev->os_priv; sunos_dev_priv_t *dpriv = (sunos_dev_priv_t *)dev->os_priv;
char match_str[PATH_MAX];
/* Device descriptors */ /* Device descriptors */
proplen = di_prop_lookup_bytes(DDI_DEV_T_ANY, node, 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); phypath = di_devfs_path(node);
if (phypath) { if (phypath) {
dpriv->phypath = strdup(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); di_devfs_path_free(phypath);
} else { } else {
free(dpriv->raw_cfgdescr); free(dpriv->raw_cfgdescr);
@ -170,111 +563,98 @@ sunos_fill_in_dev_info(di_node_t node, struct libusb_device *dev)
return (LIBUSB_SUCCESS); return (LIBUSB_SUCCESS);
} }
static int static int
sunos_add_devices(di_devlink_t link, void *arg) sunos_add_devices(di_devlink_t link, void *arg)
{ {
struct devlink_cbarg *largs = (struct devlink_cbarg *)arg; struct devlink_cbarg *largs = (struct devlink_cbarg *)arg;
struct node_args *nargs; struct node_args *nargs;
di_node_t myself, pnode; di_node_t myself, dn;
uint64_t session_id = 0; uint64_t session_id = 0;
uint16_t bdf = 0; uint64_t sid = 0;
uint64_t bdf = 0;
struct libusb_device *dev; struct libusb_device *dev;
sunos_dev_priv_t *devpriv; sunos_dev_priv_t *devpriv;
const char *path, *newpath; int n;
int n, i; int i = 0;
int *addr_prop; int *addr_prop;
uint8_t bus_number = 0; uint8_t bus_number = 0;
uint32_t * regbuf = NULL;
uint32_t reg;
nargs = (struct node_args *)largs->nargs; nargs = (struct node_args *)largs->nargs;
myself = largs->myself; myself = largs->myself;
if (nargs->last_ugenpath) {
/* the same node's links */
return (DI_WALK_CONTINUE);
}
/* /*
* Construct session ID. * 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; if (myself == DI_NODE_NIL)
i = 0; return (DI_WALK_CONTINUE);
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", dn = myself;
(int **)&regbuf); /* find the root hub */
reg = regbuf[0]; while (di_prop_exists(DDI_DEV_T_ANY, dn, "root-hub") != 1) {
bdf = (PCI_REG_BUS_G(reg) << 8) | usbi_dbg("find_root_hub:%s", di_devfs_path(dn));
(PCI_REG_DEV_G(reg) << 3) | PCI_REG_FUNC_G(reg); n = di_prop_lookup_ints(DDI_DEV_T_ANY, dn,
session_id |= (bdf << i * 8); "assigned-address", &addr_prop);
session_id |= ((addr_prop[0] & 0xff) << i++ * 8);
dn = di_parent_node(dn);
}
/* same as 'unit-address' property */ /* dn is the root hub node */
bus_number = n = di_prop_lookup_ints(DDI_DEV_T_ANY, dn, "reg", (int **)&regbuf);
(PCI_REG_DEV_G(reg) << 3) | PCI_REG_FUNC_G(reg); 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", usbi_dbg("device bus address=%s:%x, name:%s",
di_bus_addr(pnode), bus_number); di_bus_addr(myself), bus_number, di_node_name(dn));
usbi_dbg("session id org:%lx", session_id);
break;
}
/* 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 */ /* 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); "assigned-address", &addr_prop);
if ((n != 1) || (addr_prop[0] == 0)) { if ((n != 1) || (addr_prop[0] == 0)) {
usbi_dbg("cannot get valid usb_addr"); usbi_dbg("cannot get valid usb_addr");
continue;
return (DI_WALK_CONTINUE);
} }
session_id |= ((addr_prop[0] & 0xff) << i * 8); sid = (session_id << 8) | (addr_prop[0] & 0xff) ;
if (++i > 7) usbi_dbg("session id %lx", sid);
break;
pnode = di_parent_node(pnode); dev = usbi_get_device_by_session_id(nargs->ctx, sid);
}
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);
if (dev == NULL) { if (dev == NULL) {
usbi_dbg("can't alloc device"); 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 (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);
} }
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);
return (DI_WALK_TERMINATE);
}
if (usbi_sanitize_device(dev) < 0) {
libusb_unref_device(dev);
usbi_dbg("sanatize failed: ");
return (DI_WALK_TERMINATE);
}
} 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) { if (discovered_devs_append(*(nargs->discdevs), dev) == NULL) {
usbi_dbg("cannot append device"); 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. * hereafter. Front end or app should take care of their ref.
*/ */
libusb_unref_device(dev); libusb_unref_device(dev);
}
usbi_dbg("Device %s %s id=0x%llx, devcount:%d, bdf=%x", usbi_dbg("Device %s %s id=0x%llx, devcount:%d, bdf=%x",
devpriv->ugenpath, path, (uint64_t)session_id, devpriv->ugenpath, di_devfs_path(dn), (uint64_t)sid,
(*nargs->discdevs)->len, bdf); (*nargs->discdevs)->len, bdf);
}
return (DI_WALK_CONTINUE); 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; struct node_args *nargs = (struct node_args *)args;
di_devlink_handle_t devlink_hdl = nargs->dlink_hdl; 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) { while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
minor_path = di_devfs_minor_path(minor); minor_path = di_devfs_minor_path(minor);
arg.nargs = args; arg.nargs = args;
arg.myself = node; arg.myself = node;
arg.minor = minor; arg.minor = minor;
(void) di_devlink_walk(devlink_hdl, (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_PRIMARY_LINK, (void *)&arg, sunos_add_devices);
di_devfs_path_free(minor_path); 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", usbi_dbg("can't find interface for endpoint 0x%02x",
ep_addr); ep_addr);
return (LIBUSB_ERROR_ACCESS); return (EACCES);
} }
/* create filename */ /* create filename */
@ -603,6 +983,11 @@ sunos_open(struct libusb_device_handle *handle)
hpriv->eps[i].statfd = -1; 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) { if ((ret = sunos_usb_open_ep0(hpriv, dpriv)) != LIBUSB_SUCCESS) {
usbi_dbg("fail: %d", ret); usbi_dbg("fail: %d", ret);
return (ret); return (ret);
@ -1010,9 +1395,7 @@ void
sunos_destroy_device(struct libusb_device *dev) sunos_destroy_device(struct libusb_device *dev)
{ {
sunos_dev_priv_t *dpriv = (sunos_dev_priv_t *)dev->os_priv; sunos_dev_priv_t *dpriv = (sunos_dev_priv_t *)dev->os_priv;
usbi_dbg("destroy everyting");
usbi_dbg("");
free(dpriv->raw_cfgdescr); free(dpriv->raw_cfgdescr);
free(dpriv->ugenpath); free(dpriv->ugenpath);
free(dpriv->phypath); free(dpriv->phypath);
@ -1254,7 +1637,7 @@ sunos_usb_get_status(int fd)
return (status); return (status);
} }
const struct usbi_os_backend sunos_backend = { const struct usbi_os_backend usbi_backend = {
.name = "Solaris", .name = "Solaris",
.caps = 0, .caps = 0,
.init = sunos_init, .init = sunos_init,
@ -1276,9 +1659,9 @@ const struct usbi_os_backend sunos_backend = {
.reset_device = sunos_reset_device, /* TODO */ .reset_device = sunos_reset_device, /* TODO */
.alloc_streams = NULL, .alloc_streams = NULL,
.free_streams = NULL, .free_streams = NULL,
.kernel_driver_active = NULL, .kernel_driver_active = sunos_kernel_driver_active,
.detach_kernel_driver = NULL, .detach_kernel_driver = sunos_detach_kernel_driver,
.attach_kernel_driver = NULL, .attach_kernel_driver = sunos_attach_kernel_driver,
.destroy_device = sunos_destroy_device, .destroy_device = sunos_destroy_device,
.submit_transfer = sunos_submit_transfer, .submit_transfer = sunos_submit_transfer,
.cancel_transfer = sunos_cancel_transfer, .cancel_transfer = sunos_cancel_transfer,

View File

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

View File

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

View File

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

View File

@ -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);
}
}

View File

@ -21,17 +21,40 @@
#ifndef LIBUSB_THREADS_WINDOWS_H #ifndef LIBUSB_THREADS_WINDOWS_H
#define LIBUSB_THREADS_WINDOWS_H #define LIBUSB_THREADS_WINDOWS_H
#define usbi_mutex_static_t volatile LONG #define USBI_MUTEX_INITIALIZER 0L
#define USBI_MUTEX_INITIALIZER 0 #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);
}
#define usbi_mutex_t HANDLE typedef CRITICAL_SECTION usbi_mutex_t;
static inline int usbi_mutex_init(usbi_mutex_t *mutex)
typedef struct usbi_cond { {
// Every time a thread touches the CV, it winds up in one of these lists. InitializeCriticalSection(mutex);
// It stays there until the CV is destroyed, even if the thread terminates. return 0;
struct list_head waiters; }
struct list_head not_waiting; static inline void usbi_mutex_lock(usbi_mutex_t *mutex)
} usbi_cond_t; {
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: // We *were* getting timespec from pthread.h:
#if (!defined(HAVE_STRUCT_TIMESPEC) && !defined(_TIMESPEC_DEFINED)) #if (!defined(HAVE_STRUCT_TIMESPEC) && !defined(_TIMESPEC_DEFINED))
@ -45,32 +68,44 @@ struct timespec {
// We *were* getting ETIMEDOUT from pthread.h: // We *were* getting ETIMEDOUT from pthread.h:
#ifndef ETIMEDOUT #ifndef ETIMEDOUT
# define ETIMEDOUT 10060 /* This is the value in winsock.h. */ #define ETIMEDOUT 10060 /* This is the value in winsock.h. */
#endif #endif
#define usbi_tls_key_t DWORD 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_mutex_static_lock(usbi_mutex_static_t *mutex); void usbi_cond_init(usbi_cond_t *cond);
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);
int usbi_cond_init(usbi_cond_t *cond);
int usbi_cond_wait(usbi_cond_t *cond, usbi_mutex_t *mutex); int usbi_cond_wait(usbi_cond_t *cond, usbi_mutex_t *mutex);
int usbi_cond_timedwait(usbi_cond_t *cond, int usbi_cond_timedwait(usbi_cond_t *cond,
usbi_mutex_t *mutex, const struct timeval *tv); usbi_mutex_t *mutex, const struct timeval *tv);
int usbi_cond_broadcast(usbi_cond_t *cond); void usbi_cond_broadcast(usbi_cond_t *cond);
int usbi_cond_destroy(usbi_cond_t *cond); void usbi_cond_destroy(usbi_cond_t *cond);
int usbi_tls_key_create(usbi_tls_key_t *key); typedef DWORD usbi_tls_key_t;
void *usbi_tls_key_get(usbi_tls_key_t key); static inline void usbi_tls_key_create(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); *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 */ #endif /* LIBUSB_THREADS_WINDOWS_H */

View File

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

View File

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

View File

@ -32,6 +32,14 @@
#include "windows_common.h" #include "windows_common.h"
#include "windows_nt_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 // Global variables for clock_gettime mechanism
static uint64_t hires_ticks_to_ps; static uint64_t hires_ticks_to_ps;
static uint64_t hires_frequency; static uint64_t hires_frequency;
@ -50,6 +58,11 @@ struct timer_request {
static HANDLE timer_thread = NULL; static HANDLE timer_thread = NULL;
static DWORD timer_thread_id = 0; 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 */ /* User32 dependencies */
DLL_DECLARE_HANDLE(User32); DLL_DECLARE_HANDLE(User32);
DLL_DECLARE_FUNC_PREFIXED(WINAPI, BOOL, p, GetMessageA, (LPMSG, HWND, UINT, UINT)); 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 #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: /* Hash table functions - modified From glibc 2.3.2:
[Aho,Sethi,Ullman] Compilers: Principles, Techniques and Tools, 1986 [Aho,Sethi,Ullman] Compilers: Principles, Techniques and Tools, 1986
[Knuth] The Art of Computer Programming, part 3 (6.4) */ [Knuth] The Art of Computer Programming, part 3 (6.4) */
@ -123,7 +141,7 @@ typedef struct htab_entry {
} htab_entry; } htab_entry;
static htab_entry *htab_table = NULL; static htab_entry *htab_table = NULL;
static usbi_mutex_t htab_mutex = NULL; static usbi_mutex_t htab_mutex;
static unsigned long htab_filled; static unsigned long htab_filled;
/* Before using the hash table we must allocate memory for it. /* Before using the hash table we must allocate memory for it.
@ -256,35 +274,46 @@ out_unlock:
return idx; 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_GET_HANDLE(User32);
DLL_LOAD_FUNC_PREFIXED(User32, p, GetMessageA, TRUE); DLL_LOAD_FUNC_PREFIXED(User32, p, GetMessageA, TRUE);
DLL_LOAD_FUNC_PREFIXED(User32, p, PeekMessageA, TRUE); DLL_LOAD_FUNC_PREFIXED(User32, p, PeekMessageA, TRUE);
DLL_LOAD_FUNC_PREFIXED(User32, p, PostThreadMessageA, TRUE); DLL_LOAD_FUNC_PREFIXED(User32, p, PostThreadMessageA, TRUE);
return LIBUSB_SUCCESS; return TRUE;
} }
static void windows_exit_dlls(void) static void windows_exit_dlls(void)
{ {
DLL_FREE_HANDLE(Kernel32);
DLL_FREE_HANDLE(User32); DLL_FREE_HANDLE(User32);
} }
static bool windows_init_clock(struct libusb_context *ctx) static bool windows_init_clock(struct libusb_context *ctx)
{ {
DWORD_PTR affinity, dummy; DWORD_PTR affinity, dummy;
HANDLE event = NULL; HANDLE event;
LARGE_INTEGER li_frequency; LARGE_INTEGER li_frequency;
int i; int i;
if (QueryPerformanceFrequency(&li_frequency)) { 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 // 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 // to picoseconds to compute the tv_nsecs part in clock_gettime
hires_frequency = li_frequency.QuadPart; hires_frequency = li_frequency.QuadPart;
@ -340,7 +369,7 @@ static bool windows_init_clock(struct libusb_context *ctx)
return true; return true;
} }
void windows_destroy_clock(void) static void windows_destroy_clock(void)
{ {
if (timer_thread) { if (timer_thread) {
// actually the signal to quit the 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 * Monotonic and real time functions
*/ */
@ -401,7 +534,379 @@ static unsigned __stdcall windows_clock_gettime_threaded(void *param)
} }
} }
int windows_clock_gettime(int clk_id, struct timespec *tp) 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", (unsigned int)io_result, (unsigned int)io_size);
switch (io_result) {
case NO_ERROR:
status = backend->copy_transfer_data(itransfer, (uint32_t)io_size);
break;
case ERROR_GEN_FAILURE:
usbi_dbg("detected endpoint stall");
status = LIBUSB_TRANSFER_STALL;
break;
case ERROR_SEM_TIMEOUT:
usbi_dbg("detected semaphore timeout");
status = LIBUSB_TRANSFER_TIMED_OUT;
break;
case ERROR_OPERATION_ABORTED:
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", (unsigned int)io_result, windows_error_str(io_result));
status = LIBUSB_TRANSFER_ERROR;
break;
}
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);
}
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);
switch (transfer->type) {
case LIBUSB_TRANSFER_TYPE_CONTROL:
case LIBUSB_TRANSFER_TYPE_BULK:
case LIBUSB_TRANSFER_TYPE_INTERRUPT:
case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
windows_transfer_callback(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");
break;
default:
usbi_err(ITRANSFER_CTX(itransfer), "unknown endpoint type %d", transfer->type);
}
}
static int windows_init(struct libusb_context *ctx)
{
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);
for (i = 0; i < nfds && num_ready > 0; i++) {
usbi_dbg("checking fd %d with revents = %04x", fds[i].fd, fds[i].revents);
if (!fds[i].revents)
continue;
num_ready--;
// Because a Windows OVERLAPPED is used for poll emulation,
// a pollable fd is created and stored with each transfer
found = false;
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;
}
}
usbi_mutex_unlock(&ctx->flying_transfers_lock);
if (found) {
priv->backend->get_overlapped_result(itransfer, &io_result, &io_size);
usbi_remove_pollfd(ctx, transfer_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(priv->backend, itransfer, io_result, io_size);
} else {
usbi_err(ctx, "could not find a matching transfer for fd %d", fds[i].fd);
r = LIBUSB_ERROR_NOT_FOUND;
break;
}
}
usbi_mutex_unlock(&ctx->open_devs_lock);
return r;
}
static int windows_clock_gettime(int clk_id, struct timespec *tp)
{ {
struct timer_request request; struct timer_request request;
#if !defined(_MSC_VER) || (_MSC_VER < 1900) #if !defined(_MSC_VER) || (_MSC_VER < 1900)
@ -460,132 +965,44 @@ int windows_clock_gettime(int clk_id, struct timespec *tp)
} }
} }
static void windows_transfer_callback(struct usbi_transfer *itransfer, uint32_t io_result, uint32_t io_size) // NB: MSVC6 does not support named initializers.
{ const struct usbi_os_backend usbi_backend = {
int status, istatus; "Windows",
USBI_CAP_HAS_HID_ACCESS,
usbi_dbg("handling I/O completion with errcode %u, size %u", io_result, io_size); windows_init,
windows_exit,
switch (io_result) { windows_set_option,
case NO_ERROR: windows_get_device_list,
status = windows_copy_transfer_data(itransfer, io_size); NULL, /* hotplug_poll */
break; windows_open,
case ERROR_GEN_FAILURE: windows_close,
usbi_dbg("detected endpoint stall"); windows_get_device_descriptor,
status = LIBUSB_TRANSFER_STALL; windows_get_active_config_descriptor,
break; windows_get_config_descriptor,
case ERROR_SEM_TIMEOUT: windows_get_config_descriptor_by_value,
usbi_dbg("detected semaphore timeout"); windows_get_configuration,
status = LIBUSB_TRANSFER_TIMED_OUT; windows_set_configuration,
break; windows_claim_interface,
case ERROR_OPERATION_ABORTED: windows_release_interface,
istatus = windows_copy_transfer_data(itransfer, io_size); windows_set_interface_altsetting,
if (istatus != LIBUSB_TRANSFER_COMPLETED) windows_clear_halt,
usbi_dbg("Failed to copy partial data in aborted operation: %d", istatus); windows_reset_device,
NULL, /* alloc_streams */
usbi_dbg("detected operation aborted"); NULL, /* free_streams */
status = LIBUSB_TRANSFER_CANCELLED; NULL, /* dev_mem_alloc */
break; NULL, /* dev_mem_free */
default: NULL, /* kernel_driver_active */
usbi_err(ITRANSFER_CTX(itransfer), "detected I/O error %u: %s", io_result, windows_error_str(io_result)); NULL, /* detach_kernel_driver */
status = LIBUSB_TRANSFER_ERROR; NULL, /* attach_kernel_driver */
break; windows_destroy_device,
} windows_submit_transfer,
windows_clear_transfer_priv(itransfer); // Cancel polling windows_cancel_transfer,
if (status == LIBUSB_TRANSFER_CANCELLED) windows_clear_transfer_priv,
usbi_handle_transfer_cancellation(itransfer); windows_handle_events,
else NULL, /* handle_transfer_completion */
usbi_handle_transfer_completion(itransfer, (enum libusb_transfer_status)status); windows_clock_gettime,
} sizeof(struct windows_context_priv),
sizeof(union windows_device_priv),
void windows_handle_callback(struct usbi_transfer *itransfer, uint32_t io_result, uint32_t io_size) sizeof(union windows_device_handle_priv),
{ sizeof(union windows_transfer_priv),
struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); };
switch (transfer->type) {
case LIBUSB_TRANSFER_TYPE_CONTROL:
case LIBUSB_TRANSFER_TYPE_BULK:
case LIBUSB_TRANSFER_TYPE_INTERRUPT:
case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
windows_transfer_callback(itransfer, io_result, io_size);
break;
case LIBUSB_TRANSFER_TYPE_BULK_STREAM:
usbi_warn(ITRANSFER_CTX(itransfer), "bulk stream transfers are not yet supported on this platform");
break;
default:
usbi_err(ITRANSFER_CTX(itransfer), "unknown endpoint type %d", transfer->type);
}
}
int windows_handle_events(struct libusb_context *ctx, struct pollfd *fds, POLL_NFDS_TYPE nfds, int num_ready)
{
POLL_NFDS_TYPE i;
bool found = false;
struct usbi_transfer *transfer;
struct winfd *pollable_fd = NULL;
DWORD io_size, io_result;
int r = LIBUSB_SUCCESS;
usbi_mutex_lock(&ctx->open_devs_lock);
for (i = 0; i < nfds && num_ready > 0; i++) {
usbi_dbg("checking fd %d with revents = %04x", fds[i].fd, fds[i].revents);
if (!fds[i].revents)
continue;
num_ready--;
// Because a Windows OVERLAPPED is used for poll emulation,
// a pollable fd is created and stored with each transfer
usbi_mutex_lock(&ctx->flying_transfers_lock);
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) {
found = true;
break;
}
}
usbi_mutex_unlock(&ctx->flying_transfers_lock);
if (found) {
windows_get_overlapped_result(transfer, pollable_fd, &io_result, &io_size);
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);
} else {
usbi_err(ctx, "could not find a matching transfer for fd %d", fds[i]);
r = LIBUSB_ERROR_NOT_FOUND;
break;
}
}
usbi_mutex_unlock(&ctx->open_devs_lock);
return r;
}
int windows_common_init(struct libusb_context *ctx)
{
if (!windows_init_clock(ctx))
goto error_roll_back;
if (!htab_create(ctx))
goto error_roll_back;
return LIBUSB_SUCCESS;
error_roll_back:
windows_common_exit();
return LIBUSB_ERROR_NO_MEM;
}
void windows_common_exit(void)
{
htab_destroy();
windows_destroy_clock();
windows_exit_dlls();
}

View File

@ -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

View File

@ -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;
};

View File

@ -23,22 +23,12 @@
#include <config.h> #include <config.h>
#if defined(USE_USBDK)
#include <windows.h> #include <windows.h>
#include <cfgmgr32.h>
#include <stdio.h> #include <stdio.h>
#include "libusbi.h" #include "libusbi.h"
#include "windows_common.h" #include "windows_common.h"
#include "windows_nt_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" #include "windows_usbdk.h"
#if !defined(STATUS_SUCCESS) #if !defined(STATUS_SUCCESS)
@ -55,7 +45,7 @@ typedef LONG NTSTATUS;
#endif #endif
#if !defined(USBD_SUCCESS) #if !defined(USBD_SUCCESS)
typedef int32_t USBD_STATUS; typedef LONG USBD_STATUS;
#define USBD_SUCCESS(Status) ((USBD_STATUS) (Status) >= 0) #define USBD_SUCCESS(Status) ((USBD_STATUS) (Status) >= 0)
#define USBD_PENDING(Status) ((ULONG) (Status) >> 30 == 1) #define USBD_PENDING(Status) ((ULONG) (Status) >> 30 == 1)
#define USBD_ERROR(Status) ((USBD_STATUS) (Status) < 0) #define USBD_ERROR(Status) ((USBD_STATUS) (Status) < 0)
@ -66,22 +56,6 @@ typedef int32_t USBD_STATUS;
#define USBD_STATUS_CANCELED ((USBD_STATUS) 0xc0010000) #define USBD_STATUS_CANCELED ((USBD_STATUS) 0xc0010000)
#endif #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) static inline struct usbdk_device_priv *_usbdk_device_priv(struct libusb_device *dev)
{ {
return (struct usbdk_device_priv *)dev->os_priv; 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); FARPROC api_ptr = GetProcAddress(usbdk_helper.module, api_name);
if (api_ptr == NULL) 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; return api_ptr;
} }
@ -132,7 +106,7 @@ static int load_usbdk_helper_dll(struct libusb_context *ctx)
{ {
usbdk_helper.module = LoadLibraryA("UsbDkHelper"); usbdk_helper.module = LoadLibraryA("UsbDkHelper");
if (usbdk_helper.module == NULL) { 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; return LIBUSB_ERROR_NOT_FOUND;
} }
@ -198,41 +172,41 @@ error_unload:
static int usbdk_init(struct libusb_context *ctx) static int usbdk_init(struct libusb_context *ctx)
{ {
int r; SC_HANDLE managerHandle;
SC_HANDLE serviceHandle;
if (++concurrent_usage == 0) { // First init? managerHandle = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
r = load_usbdk_helper_dll(ctx); if (managerHandle == NULL) {
if (r) usbi_warn(ctx, "failed to open service control manager: %s", windows_error_str(0));
goto init_exit; return LIBUSB_ERROR_OTHER;
init_polling();
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 (r != LIBUSB_SUCCESS) serviceHandle = OpenServiceA(managerHandle, "UsbDk", GENERIC_READ);
--concurrent_usage; // Not expected to call libusb_exit if we failed. CloseServiceHandle(managerHandle);
return r; 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;
}
CloseServiceHandle(serviceHandle);
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, static int usbdk_get_session_id_for_device(struct libusb_context *ctx,
PUSB_DK_DEVICE_ID id, unsigned long *session_id) 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); usbi_warn(ctx, "cannot form device identity", id->DeviceID);
return LIBUSB_ERROR_NOT_SUPPORTED; 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->port_number = (uint8_t)info->Port;
dev->parent_dev = NULL; 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->device_address = (uint8_t)(info->Port + 1);
dev->num_configurations = info->DeviceDescriptor.bNumConfigurations; dev->num_configurations = info->DeviceDescriptor.bNumConfigurations;
dev->device_descriptor = info->DeviceDescriptor; memcpy(&dev->device_descriptor, &info->DeviceDescriptor, LIBUSB_DT_DEVICE_SIZE);
switch (info->Speed) { switch (info->Speed) {
case LowSpeed: case LowSpeed:
@ -330,7 +304,7 @@ static int usbdk_get_device_list(struct libusb_context *ctx, struct discovered_d
ULONG dev_number; ULONG dev_number;
PUSB_DK_DEVICE_INFO devices; PUSB_DK_DEVICE_INFO devices;
if(!usbdk_helper.GetDevicesList(&devices, &dev_number)) if (!usbdk_helper.GetDevicesList(&devices, &dev_number))
return LIBUSB_ERROR_OTHER; return LIBUSB_ERROR_OTHER;
for (i = 0; i < dev_number; i++) { for (i = 0; i < dev_number; i++) {
@ -371,26 +345,16 @@ func_exit:
return r; return r;
} }
static void usbdk_exit(void) static int usbdk_get_device_descriptor(struct libusb_device *dev, unsigned char *buffer)
{
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)
{ {
struct usbdk_device_priv *priv = _usbdk_device_priv(dev); struct usbdk_device_priv *priv = _usbdk_device_priv(dev);
memcpy(buffer, &priv->info.DeviceDescriptor, DEVICE_DESC_LENGTH); memcpy(buffer, &priv->info.DeviceDescriptor, DEVICE_DESC_LENGTH);
*host_endian = 0;
return LIBUSB_SUCCESS; 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); struct usbdk_device_priv *priv = _usbdk_device_priv(dev);
PUSB_CONFIGURATION_DESCRIPTOR config_header; 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); size = min(config_header->wTotalLength, len);
memcpy(buffer, config_header, size); memcpy(buffer, config_header, size);
*host_endian = 0;
return (int)size; 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, 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) 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; return LIBUSB_ERROR_OTHER;
} }
priv->system_handle = usbdk_helper.GetRedirectorSystemHandle(priv->redirector_handle);
return LIBUSB_SUCCESS; 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); struct usbdk_device_priv *priv = _usbdk_device_priv(dev_handle->dev);
if (!usbdk_helper.StopRedirect(priv->redirector_handle)) { if (!usbdk_helper.StopRedirect(priv->redirector_handle))
struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev); usbi_err(HANDLE_CTX(dev_handle), "Redirector shutdown failed");
usbi_err(ctx, "Redirector shutdown failed");
}
} }
static int usbdk_get_configuration(struct libusb_device_handle *dev_handle, int *config) 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) 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); struct usbdk_device_priv *priv = _usbdk_device_priv(dev_handle->dev);
if (!usbdk_helper.SetAltsetting(priv->redirector_handle, iface, altsetting)) { 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) 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); struct usbdk_device_priv *priv = _usbdk_device_priv(dev_handle->dev);
if (!usbdk_helper.ResetPipe(priv->redirector_handle, endpoint)) { 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) 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); struct usbdk_device_priv *priv = _usbdk_device_priv(dev_handle->dev);
if (!usbdk_helper.ResetDevice(priv->redirector_handle)) { 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; 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) static void usbdk_destroy_device(struct libusb_device *dev)
{ {
struct usbdk_device_priv* p = _usbdk_device_priv(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); 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 usbdk_transfer_priv *transfer_priv = _usbdk_transfer_priv(itransfer);
struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(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) { if (transfer->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS) {
safe_free(transfer_priv->IsochronousPacketsArray); 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 libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
struct usbdk_device_priv *priv = _usbdk_device_priv(transfer->dev_handle->dev); struct usbdk_device_priv *priv = _usbdk_device_priv(transfer->dev_handle->dev);
struct usbdk_transfer_priv *transfer_priv = _usbdk_transfer_priv(itransfer); struct usbdk_transfer_priv *transfer_priv = _usbdk_transfer_priv(itransfer);
struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev); struct libusb_context *ctx = TRANSFER_CTX(transfer);
struct winfd wfd; OVERLAPPED *overlapped = transfer_priv->pollable_fd.overlapped;
ULONG Length;
TransferResult transResult; TransferResult transResult;
HANDLE sysHandle;
sysHandle = usbdk_helper.GetRedirectorSystemHandle(priv->redirector_handle); transfer_priv->request.Buffer = (PVOID64)transfer->buffer;
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.BufferLength = transfer->length; transfer_priv->request.BufferLength = transfer->length;
transfer_priv->request.TransferType = ControlTransferType; transfer_priv->request.TransferType = ControlTransferType;
transfer_priv->pollable_fd = INVALID_WINFD;
Length = (ULONG)transfer->length;
if (IS_XFERIN(transfer)) if (transfer->buffer[0] & LIBUSB_ENDPOINT_IN)
transResult = usbdk_helper.ReadPipe(priv->redirector_handle, &transfer_priv->request, wfd.overlapped); transResult = usbdk_helper.ReadPipe(priv->redirector_handle, &transfer_priv->request, overlapped);
else 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) { switch (transResult) {
case TransferSuccess: case TransferSuccess:
wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY; windows_force_sync_completion(overlapped, (ULONG)transfer_priv->request.Result.GenResult.BytesTransferred);
wfd.overlapped->InternalHigh = (DWORD)Length;
break; break;
case TransferSuccessAsync: case TransferSuccessAsync:
break; break;
case TransferFailure: case TransferFailure:
usbi_err(ctx, "ControlTransfer failed: %s", windows_error_str(0)); usbi_err(ctx, "ControlTransfer failed: %s", windows_error_str(0));
usbi_free_fd(&wfd);
return LIBUSB_ERROR_IO; 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; 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 libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
struct usbdk_device_priv *priv = _usbdk_device_priv(transfer->dev_handle->dev); struct usbdk_device_priv *priv = _usbdk_device_priv(transfer->dev_handle->dev);
struct usbdk_transfer_priv *transfer_priv = _usbdk_transfer_priv(itransfer); struct usbdk_transfer_priv *transfer_priv = _usbdk_transfer_priv(itransfer);
struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev); struct libusb_context *ctx = TRANSFER_CTX(transfer);
struct winfd wfd; OVERLAPPED *overlapped = transfer_priv->pollable_fd.overlapped;
TransferResult transferRes; 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.BufferLength = transfer->length;
transfer_priv->request.EndpointAddress = transfer->endpoint; 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; transfer_priv->request.TransferType = BulkTransferType;
break; break;
case LIBUSB_TRANSFER_TYPE_INTERRUPT: case LIBUSB_TRANSFER_TYPE_INTERRUPT:
transfer_priv->request.TransferType = IntertuptTransferType; transfer_priv->request.TransferType = InterruptTransferType;
break; break;
default: 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; 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)) 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 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) { switch (transferRes) {
case TransferSuccess: case TransferSuccess:
wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY; windows_force_sync_completion(overlapped, (ULONG)transfer_priv->request.Result.GenResult.BytesTransferred);
break; break;
case TransferSuccessAsync: case TransferSuccessAsync:
break; break;
case TransferFailure: case TransferFailure:
usbi_err(ctx, "ReadPipe/WritePipe failed: %s", windows_error_str(0)); usbi_err(ctx, "ReadPipe/WritePipe failed: %s", windows_error_str(0));
usbi_free_fd(&wfd);
return LIBUSB_ERROR_IO; 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; 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 libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
struct usbdk_device_priv *priv = _usbdk_device_priv(transfer->dev_handle->dev); struct usbdk_device_priv *priv = _usbdk_device_priv(transfer->dev_handle->dev);
struct usbdk_transfer_priv *transfer_priv = _usbdk_transfer_priv(itransfer); struct usbdk_transfer_priv *transfer_priv = _usbdk_transfer_priv(itransfer);
struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev); struct libusb_context *ctx = TRANSFER_CTX(transfer);
struct winfd wfd; OVERLAPPED *overlapped = transfer_priv->pollable_fd.overlapped;
TransferResult transferRes; TransferResult transferRes;
int i; 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.BufferLength = transfer->length;
transfer_priv->request.EndpointAddress = transfer->endpoint; transfer_priv->request.EndpointAddress = transfer->endpoint;
transfer_priv->request.TransferType = IsochronousTransferType; transfer_priv->request.TransferType = IsochronousTransferType;
transfer_priv->request.IsochronousPacketsArraySize = transfer->num_iso_packets; transfer_priv->request.IsochronousPacketsArraySize = transfer->num_iso_packets;
transfer_priv->IsochronousPacketsArray = malloc(transfer->num_iso_packets * sizeof(ULONG64)); 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) { if (!transfer_priv->IsochronousPacketsArray) {
usbi_err(ctx, "Allocation of IsochronousPacketsArray is failed, %s", windows_error_str(0)); usbi_err(ctx, "Allocation of IsochronousPacketsArray failed");
return LIBUSB_ERROR_IO; return LIBUSB_ERROR_NO_MEM;
} }
transfer_priv->IsochronousResultsArray = malloc(transfer->num_iso_packets * sizeof(USB_DK_ISO_TRANSFER_RESULT)); 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) { if (!transfer_priv->IsochronousResultsArray) {
usbi_err(ctx, "Allocation of isochronousResultsArray is failed, %s", windows_error_str(0)); usbi_err(ctx, "Allocation of isochronousResultsArray failed");
free(transfer_priv->IsochronousPacketsArray); return LIBUSB_ERROR_NO_MEM;
return LIBUSB_ERROR_IO;
} }
for (i = 0; i < transfer->num_iso_packets; i++) for (i = 0; i < transfer->num_iso_packets; i++)
transfer_priv->IsochronousPacketsArray[i] = transfer->iso_packet_desc[i].length; 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)) 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 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) { switch (transferRes) {
case TransferSuccess: case TransferSuccess:
wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY; windows_force_sync_completion(overlapped, (ULONG)transfer_priv->request.Result.GenResult.BytesTransferred);
break; break;
case TransferSuccessAsync: case TransferSuccessAsync:
break; break;
case TransferFailure: 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_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; 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; 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) static int usbdk_submit_transfer(struct usbi_transfer *itransfer)
{ {
struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
int (*transfer_fn)(struct usbi_transfer *);
short events;
switch (transfer->type) { switch (transfer->type) {
case LIBUSB_TRANSFER_TYPE_CONTROL: 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_BULK:
case LIBUSB_TRANSFER_TYPE_INTERRUPT: case LIBUSB_TRANSFER_TYPE_INTERRUPT:
if (IS_XFEROUT(transfer) && (transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET)) 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 return LIBUSB_ERROR_NOT_SUPPORTED; //TODO: Check whether we can support this in UsbDk
else events = IS_XFERIN(transfer) ? POLLIN : POLLOUT;
return usbdk_do_bulk_transfer(itransfer); transfer_fn = usbdk_do_bulk_transfer;
break;
case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: 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: default:
usbi_err(TRANSFER_CTX(transfer), "unknown endpoint type %d", transfer->type); usbi_err(TRANSFER_CTX(transfer), "unknown endpoint type %d", transfer->type);
return LIBUSB_ERROR_INVALID_PARAM; return LIBUSB_ERROR_INVALID_PARAM;
} }
return usbdk_do_submit_transfer(itransfer, events, transfer_fn);
} }
static int usbdk_abort_transfers(struct usbi_transfer *itransfer) static int usbdk_abort_transfers(struct usbi_transfer *itransfer)
{ {
struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_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_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)) { if (pCancelIoEx != NULL) {
usbi_err(ctx, "AbortPipe failed: %s", windows_error_str(0)); // Use CancelIoEx if available to cancel just a single transfer
return LIBUSB_ERROR_NO_DEVICE; if (!pCancelIoEx(priv->system_handle, pollable_fd->overlapped)) {
usbi_err(ctx, "CancelIoEx failed: %s", windows_error_str(0));
return LIBUSB_ERROR_NO_DEVICE;
}
} else {
if (!usbdk_helper.AbortPipe(priv->redirector_handle, transfer->endpoint)) {
usbi_err(ctx, "AbortPipe failed: %s", windows_error_str(0));
return LIBUSB_ERROR_NO_DEVICE;
}
} }
return LIBUSB_SUCCESS; return LIBUSB_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; itransfer->transferred += io_size;
return LIBUSB_TRANSFER_COMPLETED; 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); struct usbdk_transfer_priv *transfer_priv = _usbdk_transfer_priv(itransfer);
return &transfer_priv->pollable_fd; return transfer_priv->pollable_fd.fd;
} }
static DWORD usbdk_translate_usbd_status(USBD_STATUS UsbdStatus) 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; return NO_ERROR;
switch (UsbdStatus) { 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: case USBD_STATUS_TIMEOUT:
return ERROR_SEM_TIMEOUT; return ERROR_SEM_TIMEOUT;
case USBD_STATUS_CANCELED: case USBD_STATUS_CANCELED:
return ERROR_OPERATION_ABORTED; return ERROR_OPERATION_ABORTED;
default: 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)
{ {
if (HasOverlappedIoCompletedSync(pollable_fd->overlapped) // Handle async requests that completed synchronously first struct usbdk_transfer_priv *transfer_priv = _usbdk_transfer_priv(itransfer);
|| GetOverlappedResult(pollable_fd->handle, pollable_fd->overlapped, io_size, false)) { // Regular async overlapped struct winfd *pollable_fd = &transfer_priv->pollable_fd;
struct libusb_transfer *ltransfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer);
struct usbdk_transfer_priv *transfer_priv = _usbdk_transfer_priv(transfer);
if (ltransfer->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS) { if (HasOverlappedIoCompletedSync(pollable_fd->overlapped) // Handle async requests that completed synchronously first
int i; || GetOverlappedResult(transfer_priv->system_handle, pollable_fd->overlapped, io_size, FALSE)) { // Regular async overlapped
struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
if (transfer->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS) {
ULONG64 i;
for (i = 0; i < transfer_priv->request.IsochronousPacketsArraySize; 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) { switch (transfer_priv->IsochronousResultsArray[i].TransferResult) {
case STATUS_SUCCESS: 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_size = (DWORD)transfer_priv->request.Result.GenResult.BytesTransferred;
*io_result = usbdk_translate_usbd_status((USBD_STATUS) transfer_priv->request.Result.GenResult.UsbdStatus); *io_result = usbdk_translate_usbd_status((USBD_STATUS)transfer_priv->request.Result.GenResult.UsbdStatus);
} } else {
else {
*io_result = GetLastError(); *io_result = GetLastError();
} }
} }
static int usbdk_clock_gettime(int clk_id, struct timespec *tp) const struct windows_backend usbdk_backend = {
{
return windows_clock_gettime(clk_id, tp);
}
const struct usbi_os_backend usbdk_backend = {
"Windows",
USBI_CAP_HAS_HID_ACCESS,
usbdk_init, usbdk_init,
usbdk_exit, usbdk_exit,
usbdk_get_device_list, usbdk_get_device_list,
NULL,
usbdk_open, usbdk_open,
usbdk_close, usbdk_close,
usbdk_get_device_descriptor, usbdk_get_device_descriptor,
usbdk_get_active_config_descriptor, usbdk_get_active_config_descriptor,
usbdk_get_config_descriptor, usbdk_get_config_descriptor,
NULL, usbdk_get_config_descriptor_by_value,
usbdk_get_configuration, usbdk_get_configuration,
usbdk_set_configuration, usbdk_set_configuration,
usbdk_claim_interface, usbdk_claim_interface,
usbdk_release_interface, usbdk_release_interface,
usbdk_set_interface_altsetting, usbdk_set_interface_altsetting,
usbdk_clear_halt, usbdk_clear_halt,
usbdk_reset_device, 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_destroy_device,
usbdk_submit_transfer, usbdk_submit_transfer,
usbdk_cancel_transfer, usbdk_cancel_transfer,
windows_clear_transfer_priv, usbdk_clear_transfer_priv,
usbdk_copy_transfer_data,
windows_handle_events, usbdk_get_transfer_fd,
NULL, usbdk_get_overlapped_result,
usbdk_clock_gettime,
#if defined(USBI_TIMERFD_AVAILABLE)
NULL,
#endif
sizeof(struct usbdk_device_priv),
0,
sizeof(struct usbdk_transfer_priv),
}; };
#endif /* USE_USBDK */

View File

@ -23,56 +23,13 @@
#pragma once #pragma once
typedef struct tag_USB_DK_DEVICE_ID { #include "windows_nt_common.h"
WCHAR DeviceID[MAX_DEVICE_ID_LEN];
WCHAR InstanceID[MAX_DEVICE_ID_LEN];
} USB_DK_DEVICE_ID, *PUSB_DK_DEVICE_ID;
static inline void UsbDkFillIDStruct(USB_DK_DEVICE_ID *ID, PCWCHAR DeviceID, PCWCHAR InstanceID) typedef struct USB_DK_CONFIG_DESCRIPTOR_REQUEST {
{
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 {
USB_DK_DEVICE_ID ID; USB_DK_DEVICE_ID ID;
ULONG64 Index; ULONG64 Index;
} USB_DK_CONFIG_DESCRIPTOR_REQUEST, *PUSB_DK_CONFIG_DESCRIPTOR_REQUEST; } 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 { typedef enum {
TransferFailure = 0, TransferFailure = 0,
TransferSuccess, TransferSuccess,
@ -90,7 +47,7 @@ typedef enum {
typedef enum { typedef enum {
ControlTransferType, ControlTransferType,
BulkTransferType, BulkTransferType,
IntertuptTransferType, InterruptTransferType,
IsochronousTransferType IsochronousTransferType
} USB_DK_TRANSFER_TYPE; } USB_DK_TRANSFER_TYPE;

File diff suppressed because it is too large Load Diff

View File

@ -36,15 +36,14 @@
#endif #endif
// Missing from MSVC6 setupapi.h // Missing from MSVC6 setupapi.h
#if !defined(SPDRP_ADDRESS) #ifndef SPDRP_ADDRESS
#define SPDRP_ADDRESS 28 #define SPDRP_ADDRESS 28
#endif #endif
#if !defined(SPDRP_INSTALL_STATE) #ifndef SPDRP_INSTALL_STATE
#define SPDRP_INSTALL_STATE 34 #define SPDRP_INSTALL_STATE 34
#endif #endif
#define MAX_CTRL_BUFFER_LENGTH 4096 #define MAX_CTRL_BUFFER_LENGTH 4096
#define MAX_USB_DEVICES 256
#define MAX_USB_STRING_LENGTH 128 #define MAX_USB_STRING_LENGTH 128
#define MAX_HID_REPORT_SIZE 1024 #define MAX_HID_REPORT_SIZE 1024
#define MAX_HID_DESCRIPTOR_SIZE 256 #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/ff545978.aspx
// http://msdn.microsoft.com/en-us/library/ff545972.aspx // http://msdn.microsoft.com/en-us/library/ff545972.aspx
// http://msdn.microsoft.com/en-us/library/ff545982.aspx // http://msdn.microsoft.com/en-us/library/ff545982.aspx
#if !defined(GUID_DEVINTERFACE_USB_HOST_CONTROLLER) #ifndef GUID_DEVINTERFACE_USB_HOST_CONTROLLER
const GUID GUID_DEVINTERFACE_USB_HOST_CONTROLLER = { 0x3ABF6F2D, 0x71C4, 0x462A, {0x8A, 0x92, 0x1E, 0x68, 0x61, 0xE6, 0xAF, 0x27} }; const GUID GUID_DEVINTERFACE_USB_HOST_CONTROLLER = {0x3ABF6F2D, 0x71C4, 0x462A, {0x8A, 0x92, 0x1E, 0x68, 0x61, 0xE6, 0xAF, 0x27}};
#endif #endif
#if !defined(GUID_DEVINTERFACE_USB_DEVICE) #ifndef GUID_DEVINTERFACE_USB_DEVICE
const GUID GUID_DEVINTERFACE_USB_DEVICE = { 0xA5DCBF10, 0x6530, 0x11D2, {0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED} }; const GUID GUID_DEVINTERFACE_USB_DEVICE = {0xA5DCBF10, 0x6530, 0x11D2, {0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED}};
#endif #endif
#if !defined(GUID_DEVINTERFACE_USB_HUB) #ifndef GUID_DEVINTERFACE_USB_HUB
const GUID GUID_DEVINTERFACE_USB_HUB = { 0xF18A0E88, 0xC30C, 0x11D0, {0x88, 0x15, 0x00, 0xA0, 0xC9, 0x06, 0xBE, 0xD8} }; const GUID GUID_DEVINTERFACE_USB_HUB = {0xF18A0E88, 0xC30C, 0x11D0, {0x88, 0x15, 0x00, 0xA0, 0xC9, 0x06, 0xBE, 0xD8}};
#endif #endif
#if !defined(GUID_DEVINTERFACE_LIBUSB0_FILTER) #ifndef GUID_DEVINTERFACE_LIBUSB0_FILTER
const GUID GUID_DEVINTERFACE_LIBUSB0_FILTER = { 0xF9F3FF14, 0xAE21, 0x48A0, {0x8A, 0x25, 0x80, 0x11, 0xA7, 0xA9, 0x31, 0xD9} }; const GUID GUID_DEVINTERFACE_LIBUSB0_FILTER = {0xF9F3FF14, 0xAE21, 0x48A0, {0x8A, 0x25, 0x80, 0x11, 0xA7, 0xA9, 0x31, 0xD9}};
#endif #endif
@ -84,8 +83,6 @@ const GUID GUID_DEVINTERFACE_LIBUSB0_FILTER = { 0xF9F3FF14, 0xAE21, 0x48A0, {0x8
#define USB_API_WINUSBX 3 #define USB_API_WINUSBX 3
#define USB_API_HID 4 #define USB_API_HID 4
#define USB_API_MAX 5 #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) // 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 // 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_WINUSB 2
#define SUB_API_MAX 3 #define SUB_API_MAX 3
#define WINUSBX_DRV_NAMES {"libusbK", "libusb0", "WinUSB"}
struct windows_usb_api_backend { struct windows_usb_api_backend {
const uint8_t id; const uint8_t id;
const char *designation; const char * const designation;
const char **driver_name_list; // Driver name, without .sys, e.g. "usbccgp" const char * const * const driver_name_list; // Driver name, without .sys, e.g. "usbccgp"
const uint8_t nb_driver_names; const uint8_t nb_driver_names;
int (*init)(int sub_api, struct libusb_context *ctx); int (*init)(struct libusb_context *ctx);
int (*exit)(int sub_api); void (*exit)(void);
int (*open)(int sub_api, struct libusb_device_handle *dev_handle); int (*open)(int sub_api, struct libusb_device_handle *dev_handle);
void (*close)(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); 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]; extern const struct windows_usb_api_backend usb_api_backend[USB_API_MAX];
#define PRINT_UNSUPPORTED_API(fname) \ #define PRINT_UNSUPPORTED_API(fname) \
usbi_dbg("unsupported API call for '" \ usbi_dbg("unsupported API call for '%s' " \
#fname "' (unrecognized device driver)"); \ "(unrecognized device driver)", #fname)
return LIBUSB_ERROR_NOT_SUPPORTED;
#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 * 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_IN(request_type) ((request_type) & LIBUSB_ENDPOINT_IN)
#define LIBUSB_REQ_OUT(request_type) (!LIBUSB_REQ_IN(request_type)) #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 // 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) \ #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) \ #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_FEATURE HID_OUT_CTL_CODE(100)
#define IOCTL_HID_GET_INPUT_REPORT HID_OUT_CTL_CODE(104) #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 input_report_size;
uint16_t output_report_size; uint16_t output_report_size;
uint16_t feature_report_size; uint16_t feature_report_size;
uint16_t usage;
uint16_t usagePage;
WCHAR string[3][MAX_USB_STRING_LENGTH]; WCHAR string[3][MAX_USB_STRING_LENGTH];
uint8_t string_index[3]; // man, prod, ser uint8_t string_index[3]; // man, prod, ser
}; };
struct windows_device_priv { static inline struct winusb_device_priv *_device_priv(struct libusb_device *dev)
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)
{ {
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; int i;
p->apib = &usb_api_backend[USB_API_UNSUPPORTED]; 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; 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; int i;
free(p->dev_id);
free(p->path); free(p->path);
if ((dev->num_configurations > 0) && (p->config_descriptor != NULL)) { if ((dev->num_configurations > 0) && (p->config_descriptor != NULL)) {
for (i = 0; i < dev->num_configurations; i++) 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 { static inline struct winusb_device_handle_priv *_device_handle_priv(
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(
struct libusb_device_handle *handle) 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 // used to match a device driver (including filter drivers) against a supported API
struct driver_lookup { struct driver_lookup {
char list[MAX_KEY_LENGTH + 1]; // REG_MULTI_SZ list of services (driver) names char list[MAX_KEY_LENGTH + 1]; // REG_MULTI_SZ list of services (driver) names
@ -289,34 +254,6 @@ struct driver_lookup {
const char* designation; // internal designation (for debug output) const char* designation; // internal designation (for debug output)
}; };
/* 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));
/* SetupAPI dependencies */
DLL_DECLARE_HANDLE(SetupAPI);
DLL_DECLARE_FUNC_PREFIXED(WINAPI, HDEVINFO, p, SetupDiGetClassDevsA, (const GUID*, 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));
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, 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 * Windows DDK API definitions. Most of it copied from MinGW's includes
*/ */
@ -325,58 +262,64 @@ typedef DEVNODE *PDEVNODE, *PDEVINST;
typedef DWORD RETURN_TYPE; typedef DWORD RETURN_TYPE;
typedef RETURN_TYPE CONFIGRET; typedef RETURN_TYPE CONFIGRET;
#define CR_SUCCESS 0x00000000 #define CR_SUCCESS 0x00000000
#define CR_NO_SUCH_DEVNODE 0x0000000D
#define USB_DEVICE_DESCRIPTOR_TYPE LIBUSB_DT_DEVICE /* Cfgmgr32 dependencies */
#define USB_CONFIGURATION_DESCRIPTOR_TYPE LIBUSB_DT_CONFIG DLL_DECLARE_HANDLE(Cfgmgr32);
#define USB_STRING_DESCRIPTOR_TYPE LIBUSB_DT_STRING DLL_DECLARE_FUNC(WINAPI, CONFIGRET, CM_Get_Parent, (PDEVINST, DEVINST, ULONG));
#define USB_INTERFACE_DESCRIPTOR_TYPE LIBUSB_DT_INTERFACE DLL_DECLARE_FUNC(WINAPI, CONFIGRET, CM_Get_Child, (PDEVINST, DEVINST, ULONG));
#define USB_ENDPOINT_DESCRIPTOR_TYPE LIBUSB_DT_ENDPOINT
#define USB_REQUEST_GET_STATUS LIBUSB_REQUEST_GET_STATUS /* AdvAPI32 dependencies */
#define USB_REQUEST_CLEAR_FEATURE LIBUSB_REQUEST_CLEAR_FEATURE DLL_DECLARE_HANDLE(AdvAPI32);
#define USB_REQUEST_SET_FEATURE LIBUSB_REQUEST_SET_FEATURE DLL_DECLARE_FUNC_PREFIXED(WINAPI, LONG, p, RegQueryValueExW, (HKEY, LPCWSTR, LPDWORD, LPDWORD, LPBYTE, LPDWORD));
#define USB_REQUEST_SET_ADDRESS LIBUSB_REQUEST_SET_ADDRESS DLL_DECLARE_FUNC_PREFIXED(WINAPI, LONG, p, RegCloseKey, (HKEY));
#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 /* OLE32 dependency */
#define USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION 260 DLL_DECLARE_HANDLE(OLE32);
#define USB_GET_NODE_CONNECTION_NAME 261 DLL_DECLARE_FUNC_PREFIXED(WINAPI, HRESULT, p, IIDFromString, (LPCOLESTR, LPIID));
#define USB_GET_HUB_CAPABILITIES 271
#if !defined(USB_GET_NODE_CONNECTION_INFORMATION_EX) /* SetupAPI dependencies */
#define USB_GET_NODE_CONNECTION_INFORMATION_EX 274 DLL_DECLARE_HANDLE(SetupAPI);
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,
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, 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));
#ifndef USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION
#define USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION 260
#endif #endif
#if !defined(USB_GET_HUB_CAPABILITIES_EX) #ifndef USB_GET_NODE_CONNECTION_INFORMATION_EX
#define USB_GET_HUB_CAPABILITIES_EX 276 #define USB_GET_NODE_CONNECTION_INFORMATION_EX 274
#endif #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 #define USB_GET_NODE_CONNECTION_INFORMATION_EX_V2 279
#endif #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 #ifndef FILE_DEVICE_USB
#define FILE_DEVICE_USB FILE_DEVICE_UNKNOWN #define FILE_DEVICE_USB FILE_DEVICE_UNKNOWN
#endif #endif
#ifndef CTL_CODE #define USB_CTL_CODE(id) \
#define CTL_CODE(DeviceType, Function, Method, Access) \ CTL_CODE(FILE_DEVICE_USB, (id), METHOD_BUFFERED, FILE_ANY_ACCESS)
(((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method))
#endif #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 { typedef enum USB_CONNECTION_STATUS {
NoDeviceConnected, NoDeviceConnected,
@ -395,42 +338,25 @@ typedef enum USB_HUB_NODE {
UsbMIParent UsbMIParent
} USB_HUB_NODE; } 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 // Most of the structures below need to be packed
#pragma pack(push, 1) #include <pshpack1.h>
typedef struct _USB_DESCRIPTOR_REQUEST {
ULONG ConnectionIndex;
struct {
UCHAR bmRequest;
UCHAR bRequest;
USHORT wValue;
USHORT wIndex;
USHORT wLength;
} SetupPacket;
// UCHAR Data[0];
} USB_DESCRIPTOR_REQUEST, *PUSB_DESCRIPTOR_REQUEST;
typedef struct _USB_CONFIGURATION_DESCRIPTOR_SHORT {
USB_DESCRIPTOR_REQUEST req;
USB_CONFIGURATION_DESCRIPTOR desc;
} USB_CONFIGURATION_DESCRIPTOR_SHORT;
typedef struct USB_INTERFACE_DESCRIPTOR { typedef struct USB_INTERFACE_DESCRIPTOR {
UCHAR bLength; UCHAR bLength;
@ -444,103 +370,7 @@ typedef struct USB_INTERFACE_DESCRIPTOR {
UCHAR iInterface; UCHAR iInterface;
} USB_INTERFACE_DESCRIPTOR, *PUSB_INTERFACE_DESCRIPTOR; } USB_INTERFACE_DESCRIPTOR, *PUSB_INTERFACE_DESCRIPTOR;
typedef struct USB_CONFIGURATION_DESCRIPTOR_SHORT { typedef struct _USB_NODE_CONNECTION_INFORMATION_EX {
struct {
ULONG ConnectionIndex;
struct {
UCHAR bmRequest;
UCHAR bRequest;
USHORT wValue;
USHORT wIndex;
USHORT wLength;
} SetupPacket;
} req;
USB_CONFIGURATION_DESCRIPTOR data;
} USB_CONFIGURATION_DESCRIPTOR_SHORT;
typedef struct USB_ENDPOINT_DESCRIPTOR {
UCHAR bLength;
UCHAR bDescriptorType;
UCHAR bEndpointAddress;
UCHAR bmAttributes;
USHORT wMaxPacketSize;
UCHAR bInterval;
} USB_ENDPOINT_DESCRIPTOR, *PUSB_ENDPOINT_DESCRIPTOR;
typedef struct USB_DESCRIPTOR_REQUEST {
ULONG ConnectionIndex;
struct {
UCHAR bmRequest;
UCHAR bRequest;
USHORT wValue;
USHORT wIndex;
USHORT wLength;
} SetupPacket;
// UCHAR Data[0];
} USB_DESCRIPTOR_REQUEST, *PUSB_DESCRIPTOR_REQUEST;
typedef struct USB_HUB_DESCRIPTOR {
UCHAR bDescriptorLength;
UCHAR bDescriptorType;
UCHAR bNumberOfPorts;
USHORT wHubCharacteristics;
UCHAR bPowerOnToPowerGood;
UCHAR bHubControlCurrent;
UCHAR bRemoveAndPowerMask[64];
} USB_HUB_DESCRIPTOR, *PUSB_HUB_DESCRIPTOR;
typedef struct USB_ROOT_HUB_NAME {
ULONG ActualLength;
WCHAR RootHubName[1];
} USB_ROOT_HUB_NAME, *PUSB_ROOT_HUB_NAME;
typedef struct USB_ROOT_HUB_NAME_FIXED {
ULONG ActualLength;
WCHAR RootHubName[MAX_PATH_LENGTH];
} USB_ROOT_HUB_NAME_FIXED;
typedef struct USB_NODE_CONNECTION_NAME {
ULONG ConnectionIndex;
ULONG ActualLength;
WCHAR NodeName[1];
} USB_NODE_CONNECTION_NAME, *PUSB_NODE_CONNECTION_NAME;
typedef struct USB_NODE_CONNECTION_NAME_FIXED {
ULONG ConnectionIndex;
ULONG ActualLength;
WCHAR NodeName[MAX_PATH_LENGTH];
} USB_NODE_CONNECTION_NAME_FIXED;
typedef struct USB_HUB_NAME_FIXED {
union {
USB_ROOT_HUB_NAME_FIXED root;
USB_NODE_CONNECTION_NAME_FIXED node;
} u;
} USB_HUB_NAME_FIXED;
typedef struct USB_HUB_INFORMATION {
USB_HUB_DESCRIPTOR HubDescriptor;
BOOLEAN HubIsBusPowered;
} USB_HUB_INFORMATION, *PUSB_HUB_INFORMATION;
typedef struct USB_MI_PARENT_INFORMATION {
ULONG NumberOfInterfaces;
} USB_MI_PARENT_INFORMATION, *PUSB_MI_PARENT_INFORMATION;
typedef struct USB_NODE_INFORMATION {
USB_HUB_NODE NodeType;
union {
USB_HUB_INFORMATION HubInformation;
USB_MI_PARENT_INFORMATION MiParentInformation;
} u;
} USB_NODE_INFORMATION, *PUSB_NODE_INFORMATION;
typedef struct USB_PIPE_INFO {
USB_ENDPOINT_DESCRIPTOR EndpointDescriptor;
ULONG ScheduleOffset;
} USB_PIPE_INFO, *PUSB_PIPE_INFO;
typedef struct USB_NODE_CONNECTION_INFORMATION_EX {
ULONG ConnectionIndex; ULONG ConnectionIndex;
USB_DEVICE_DESCRIPTOR DeviceDescriptor; USB_DEVICE_DESCRIPTOR DeviceDescriptor;
UCHAR CurrentConfigurationValue; 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_FLAGS Flags;
} USB_NODE_CONNECTION_INFORMATION_EX_V2, *PUSB_NODE_CONNECTION_INFORMATION_EX_V2; } USB_NODE_CONNECTION_INFORMATION_EX_V2, *PUSB_NODE_CONNECTION_INFORMATION_EX_V2;
typedef struct USB_HUB_CAP_FLAGS { #include <poppack.h>
ULONG HubIsHighSpeedCapable:1;
ULONG HubIsHighSpeed:1;
ULONG HubIsMultiTtCapable:1;
ULONG HubIsMultiTt:1;
ULONG HubIsRoot:1;
ULONG HubIsArmedWakeOnConnect:1;
ULONG ReservedMBZ:26;
} USB_HUB_CAP_FLAGS, *PUSB_HUB_CAP_FLAGS;
typedef struct USB_HUB_CAPABILITIES {
ULONG HubIs2xCapable:1;
} USB_HUB_CAPABILITIES, *PUSB_HUB_CAPABILITIES;
typedef struct USB_HUB_CAPABILITIES_EX {
USB_HUB_CAP_FLAGS CapabilityFlags;
} USB_HUB_CAPABILITIES_EX, *PUSB_HUB_CAPABILITIES_EX;
#pragma pack(pop)
/* winusb.dll interface */ /* winusb.dll interface */
@ -608,36 +420,25 @@ typedef struct USB_HUB_CAPABILITIES_EX {
#define AUTO_FLUSH 0x06 #define AUTO_FLUSH 0x06
#define RAW_IO 0x07 #define RAW_IO 0x07
#define MAXIMUM_TRANSFER_SIZE 0x08 #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, UsbdPipeTypeControl,
UsbdPipeTypeIsochronous, UsbdPipeTypeIsochronous,
UsbdPipeTypeBulk, UsbdPipeTypeBulk,
UsbdPipeTypeInterrupt UsbdPipeTypeInterrupt
} USBD_PIPE_TYPE; } USBD_PIPE_TYPE;
typedef struct { #include <pshpack1.h>
USBD_PIPE_TYPE PipeType;
UCHAR PipeId;
USHORT MaximumPacketSize;
UCHAR Interval;
} WINUSB_PIPE_INFORMATION, *PWINUSB_PIPE_INFORMATION;
#pragma pack(1) typedef struct _WINUSB_SETUP_PACKET {
typedef struct { UCHAR RequestType;
UCHAR request_type; UCHAR Request;
UCHAR request; USHORT Value;
USHORT value; USHORT Index;
USHORT index; USHORT Length;
USHORT length;
} WINUSB_SETUP_PACKET, *PWINUSB_SETUP_PACKET; } WINUSB_SETUP_PACKET, *PWINUSB_SETUP_PACKET;
#pragma pack()
#include <poppack.h>
typedef void *WINUSB_INTERFACE_HANDLE, *PWINUSB_INTERFACE_HANDLE; typedef void *WINUSB_INTERFACE_HANDLE, *PWINUSB_INTERFACE_HANDLE;
@ -665,59 +466,10 @@ typedef BOOL (WINAPI *WinUsb_GetAssociatedInterface_t)(
UCHAR AssociatedInterfaceIndex, UCHAR AssociatedInterfaceIndex,
PWINUSB_INTERFACE_HANDLE AssociatedInterfaceHandle 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)( typedef BOOL (WINAPI *WinUsb_Initialize_t)(
HANDLE DeviceHandle, HANDLE DeviceHandle,
PWINUSB_INTERFACE_HANDLE InterfaceHandle 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)( typedef BOOL (WINAPI *WinUsb_ReadPipe_t)(
WINUSB_INTERFACE_HANDLE InterfaceHandle, WINUSB_INTERFACE_HANDLE InterfaceHandle,
UCHAR PipeID, UCHAR PipeID,
@ -726,6 +478,9 @@ typedef BOOL (WINAPI *WinUsb_ReadPipe_t)(
PULONG LengthTransferred, PULONG LengthTransferred,
LPOVERLAPPED Overlapped LPOVERLAPPED Overlapped
); );
typedef BOOL (WINAPI *WinUsb_ResetDevice_t)(
WINUSB_INTERFACE_HANDLE InterfaceHandle
);
typedef BOOL (WINAPI *WinUsb_ResetPipe_t)( typedef BOOL (WINAPI *WinUsb_ResetPipe_t)(
WINUSB_INTERFACE_HANDLE InterfaceHandle, WINUSB_INTERFACE_HANDLE InterfaceHandle,
UCHAR PipeID UCHAR PipeID
@ -741,12 +496,6 @@ typedef BOOL (WINAPI *WinUsb_SetPipePolicy_t)(
ULONG ValueLength, ULONG ValueLength,
PVOID Value PVOID Value
); );
typedef BOOL (WINAPI *WinUsb_SetPowerPolicy_t)(
WINUSB_INTERFACE_HANDLE InterfaceHandle,
ULONG PolicyType,
ULONG ValueLength,
PVOID Value
);
typedef BOOL (WINAPI *WinUsb_WritePipe_t)( typedef BOOL (WINAPI *WinUsb_WritePipe_t)(
WINUSB_INTERFACE_HANDLE InterfaceHandle, WINUSB_INTERFACE_HANDLE InterfaceHandle,
UCHAR PipeID, UCHAR PipeID,
@ -755,9 +504,6 @@ typedef BOOL (WINAPI *WinUsb_WritePipe_t)(
PULONG LengthTransferred, PULONG LengthTransferred,
LPOVERLAPPED Overlapped LPOVERLAPPED Overlapped
); );
typedef BOOL (WINAPI *WinUsb_ResetDevice_t)(
WINUSB_INTERFACE_HANDLE InterfaceHandle
);
/* /!\ These must match the ones from the official libusbk.h */ /* /!\ These must match the ones from the official libusbk.h */
typedef enum _KUSB_FNID { typedef enum _KUSB_FNID {
@ -803,8 +549,7 @@ typedef struct _KLIB_VERSION {
INT Minor; INT Minor;
INT Micro; INT Micro;
INT Nano; INT Nano;
} KLIB_VERSION; } KLIB_VERSION, *PKLIB_VERSION;
typedef KLIB_VERSION* PKLIB_VERSION;
typedef BOOL (WINAPI *LibK_GetProcAddress_t)( typedef BOOL (WINAPI *LibK_GetProcAddress_t)(
PVOID *ProcAddress, PVOID *ProcAddress,
@ -816,29 +561,63 @@ typedef VOID (WINAPI *LibK_GetVersion_t)(
PKLIB_VERSION Version 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 { struct winusb_interface {
bool initialized; bool initialized;
bool CancelIoEx_supported;
WinUsb_AbortPipe_t AbortPipe; WinUsb_AbortPipe_t AbortPipe;
WinUsb_ControlTransfer_t ControlTransfer; WinUsb_ControlTransfer_t ControlTransfer;
WinUsb_FlushPipe_t FlushPipe; WinUsb_FlushPipe_t FlushPipe;
WinUsb_Free_t Free; WinUsb_Free_t Free;
WinUsb_GetAssociatedInterface_t GetAssociatedInterface; 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_Initialize_t Initialize;
WinUsb_QueryDeviceInformation_t QueryDeviceInformation;
WinUsb_QueryInterfaceSettings_t QueryInterfaceSettings;
WinUsb_QueryPipe_t QueryPipe;
WinUsb_ReadPipe_t ReadPipe; WinUsb_ReadPipe_t ReadPipe;
WinUsb_ResetDevice_t ResetDevice;
WinUsb_ResetPipe_t ResetPipe; WinUsb_ResetPipe_t ResetPipe;
WinUsb_SetCurrentAlternateSetting_t SetCurrentAlternateSetting; WinUsb_SetCurrentAlternateSetting_t SetCurrentAlternateSetting;
WinUsb_SetPipePolicy_t SetPipePolicy; WinUsb_SetPipePolicy_t SetPipePolicy;
WinUsb_SetPowerPolicy_t SetPowerPolicy;
WinUsb_WritePipe_t WritePipe; WinUsb_WritePipe_t WritePipe;
WinUsb_ResetDevice_t ResetDevice; WinUsb_IsoReadPipe_t IsoReadPipe;
WinUsb_IsoWritePipe_t IsoWritePipe;
}; };
/* hid.dll interface */ /* hid.dll interface */
@ -846,34 +625,10 @@ struct winusb_interface {
#define HIDP_STATUS_SUCCESS 0x110000 #define HIDP_STATUS_SUCCESS 0x110000
typedef void * PHIDP_PREPARSED_DATA; typedef void * PHIDP_PREPARSED_DATA;
#pragma pack(1) #include <pshpack1.h>
typedef struct { #include <poppack.h>
ULONG Size;
USHORT VendorID;
USHORT ProductID;
USHORT VersionNumber;
} HIDD_ATTRIBUTES, *PHIDD_ATTRIBUTES;
#pragma pack()
typedef USHORT USAGE; 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 { typedef enum _HIDP_REPORT_TYPE {
HidP_Input, HidP_Input,
@ -919,19 +674,7 @@ typedef struct _HIDP_VALUE_CAPS {
} HIDP_VALUE_CAPS, *PHIDP_VALUE_CAPS; } HIDP_VALUE_CAPS, *PHIDP_VALUE_CAPS;
DLL_DECLARE_HANDLE(hid); 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, 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_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, 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));

View File

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

View File

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

42
vendor/github.com/karalabe/usb/raw_disabled.go generated vendored Normal file
View File

@ -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
vendor/github.com/karalabe/usb/raw_enabled.go generated vendored Normal file
View File

@ -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
vendor/github.com/karalabe/usb/raw_errors.go generated vendored Normal file
View File

@ -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
vendor/github.com/karalabe/usb/usb.go generated vendored Normal file
View File

@ -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
vendor/github.com/karalabe/usb/usb_disabled.go generated vendored Normal file
View File

@ -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
vendor/github.com/karalabe/usb/usb_enabled.go generated vendored Normal file
View File

@ -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)
}

View File

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

8
vendor/vendor.json vendored
View File

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