forked from cerc-io/plugeth
accounts/usbwallet, vendor: switch from HID to generic USB lib
This commit is contained in:
parent
b4cc7b660c
commit
5d68400cad
@ -25,7 +25,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/karalabe/hid"
|
||||
"github.com/karalabe/usb"
|
||||
)
|
||||
|
||||
// LedgerScheme is the protocol scheme prefixing account and wallet URLs.
|
||||
@ -84,20 +84,20 @@ func NewLedgerHub() (*Hub, error) {
|
||||
}, 0xffa0, 0, newLedgerDriver)
|
||||
}
|
||||
|
||||
// NewTrezorHub creates a new hardware wallet manager for Trezor devices.
|
||||
func NewTrezorHub() (*Hub, error) {
|
||||
return newHub(TrezorScheme, 0x534c, []uint16{0x0001 /* Trezor 1 */}, 0xff00, 0, newTrezorDriver)
|
||||
// NewTrezorHubWithHID creates a new hardware wallet manager for Trezor devices.
|
||||
func NewTrezorHubWithHID() (*Hub, error) {
|
||||
return newHub(TrezorScheme, 0x534c, []uint16{0x0001 /* Trezor HID */}, 0xff00, 0, newTrezorDriver)
|
||||
}
|
||||
|
||||
// NewWebUSBTrezorHub creates a new hardware wallet manager for Trezor devices with
|
||||
// NewTrezorHubWithWebUSB creates a new hardware wallet manager for Trezor devices with
|
||||
// firmware version > 1.8.0
|
||||
func NewWebUSBTrezorHub() (*Hub, error) {
|
||||
return newHub(TrezorScheme, 0x1209, []uint16{0x53c1 /* Trezor 1 WebUSB */}, 0, 0, newTrezorDriver)
|
||||
func NewTrezorHubWithWebUSB() (*Hub, error) {
|
||||
return newHub(TrezorScheme, 0x1209, []uint16{0x53c1 /* Trezor WebUSB */}, 0xffff /* No usage id on webusb, don't match unset (0) */, 0, newTrezorDriver)
|
||||
}
|
||||
|
||||
// newHub creates a new hardware wallet manager for generic USB devices.
|
||||
func newHub(scheme string, vendorID uint16, productIDs []uint16, usageID uint16, endpointID int, makeDriver func(log.Logger) driver) (*Hub, error) {
|
||||
if !hid.Supported() {
|
||||
if !usb.Supported() {
|
||||
return nil, errors.New("unsupported platform")
|
||||
}
|
||||
hub := &Hub{
|
||||
@ -139,7 +139,7 @@ func (hub *Hub) refreshWallets() {
|
||||
return
|
||||
}
|
||||
// Retrieve the current list of USB wallet devices
|
||||
var devices []hid.DeviceInfo
|
||||
var devices []usb.DeviceInfo
|
||||
|
||||
if runtime.GOOS == "linux" {
|
||||
// hidapi on Linux opens the device during enumeration to retrieve some infos,
|
||||
@ -154,7 +154,7 @@ func (hub *Hub) refreshWallets() {
|
||||
return
|
||||
}
|
||||
}
|
||||
infos, err := hid.Enumerate(hub.vendorID, 0)
|
||||
infos, err := usb.Enumerate(hub.vendorID, 0)
|
||||
if err != nil {
|
||||
if runtime.GOOS == "linux" {
|
||||
// See rationale before the enumeration why this is needed and only on Linux.
|
||||
@ -165,8 +165,8 @@ func (hub *Hub) refreshWallets() {
|
||||
}
|
||||
for _, info := range infos {
|
||||
for _, id := range hub.productIDs {
|
||||
_, pid, endpoint, _ /* FIXME usageID */ := info.IDs()
|
||||
if pid == id && ( /* FIXME usageID == hub.usageID || */ endpoint == hub.endpointID) {
|
||||
// Windows and Macos use UsageID matching, Linux uses Interface matching
|
||||
if info.ProductID == id && (info.UsagePage == hub.usageID || info.Interface == hub.endpointID) {
|
||||
devices = append(devices, info)
|
||||
break
|
||||
}
|
||||
@ -185,7 +185,7 @@ func (hub *Hub) refreshWallets() {
|
||||
)
|
||||
|
||||
for _, device := range devices {
|
||||
url := accounts.URL{Scheme: hub.scheme, Path: device.GetPath()}
|
||||
url := accounts.URL{Scheme: hub.scheme, Path: device.Path}
|
||||
|
||||
// Drop wallets in front of the next device or those that failed for some reason
|
||||
for len(hub.wallets) > 0 {
|
||||
|
@ -36,8 +36,6 @@ import (
|
||||
"github.com/golang/protobuf/proto"
|
||||
)
|
||||
|
||||
var ErrInvalidDeviceType = errors.New("trezor: invalid device type")
|
||||
|
||||
// ErrTrezorPINNeeded is returned if opening the trezor requires a PIN code. In
|
||||
// this case, the calling application should display a pinpad and send back the
|
||||
// encoded passphrase.
|
||||
|
@ -31,7 +31,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/karalabe/hid"
|
||||
"github.com/karalabe/usb"
|
||||
)
|
||||
|
||||
// Maximum time between wallet health checks to detect USB unplugs.
|
||||
@ -77,8 +77,8 @@ type wallet struct {
|
||||
driver driver // Hardware implementation of the low level device operations
|
||||
url *accounts.URL // Textual URL uniquely identifying this wallet
|
||||
|
||||
info hid.DeviceInfo // Known USB device infos about the wallet
|
||||
device hid.Device // USB device advertising itself as a hardware wallet
|
||||
info usb.DeviceInfo // Known USB device infos about the wallet
|
||||
device usb.Device // USB device advertising itself as a hardware wallet
|
||||
|
||||
accounts []accounts.Account // List of derive accounts pinned on the hardware wallet
|
||||
paths map[common.Address]accounts.DerivationPath // Known derivation paths for signing operations
|
||||
|
@ -501,15 +501,15 @@ func makeAccountManager(conf *Config) (*accounts.Manager, string, error) {
|
||||
} else {
|
||||
backends = append(backends, ledgerhub)
|
||||
}
|
||||
// Start a USB hub for Trezor hardware wallets
|
||||
if trezorhub, err := usbwallet.NewTrezorHub(); err != nil {
|
||||
log.Warn(fmt.Sprintf("Failed to start Trezor hub, disabling: %v", err))
|
||||
// Start a USB hub for Trezor hardware wallets (HID version)
|
||||
if trezorhub, err := usbwallet.NewTrezorHubWithHID(); err != nil {
|
||||
log.Warn(fmt.Sprintf("Failed to start HID Trezor hub, disabling: %v", err))
|
||||
} else {
|
||||
backends = append(backends, trezorhub)
|
||||
}
|
||||
// Start a USB hub for Trezor hardware wallets (WebUSB version)
|
||||
if trezorhub, err := usbwallet.NewWebUSBTrezorHub(); err != nil {
|
||||
log.Warn(fmt.Sprintf("Failed to start Trezor hub, disabling: %v", err))
|
||||
if trezorhub, err := usbwallet.NewTrezorHubWithWebUSB(); err != nil {
|
||||
log.Warn(fmt.Sprintf("Failed to start WebUSB Trezor hub, disabling: %v", err))
|
||||
} else {
|
||||
backends = append(backends, trezorhub)
|
||||
}
|
||||
|
@ -144,19 +144,19 @@ func StartClefAccountManager(ksLocation string, nousb, lightKDF bool) *accounts.
|
||||
backends = append(backends, ledgerhub)
|
||||
log.Debug("Ledger support enabled")
|
||||
}
|
||||
// Start a USB hub for Trezor hardware wallets
|
||||
if trezorhub, err := usbwallet.NewTrezorHub(); err != nil {
|
||||
log.Warn(fmt.Sprintf("Failed to start Trezor hub, disabling: %v", err))
|
||||
// Start a USB hub for Trezor hardware wallets (HID version)
|
||||
if trezorhub, err := usbwallet.NewTrezorHubWithHID(); err != nil {
|
||||
log.Warn(fmt.Sprintf("Failed to start HID Trezor hub, disabling: %v", err))
|
||||
} else {
|
||||
backends = append(backends, trezorhub)
|
||||
log.Debug("Trezor support enabled")
|
||||
log.Debug("Trezor support enabled via HID")
|
||||
}
|
||||
// Start a USB hub for Trezor hardware wallets (WebUSB version)
|
||||
if trezorhub, err := usbwallet.NewWebUSBTrezorHub(); err != nil {
|
||||
log.Warn(fmt.Sprintf("Failed to start Trezor hub, disabling: %v", err))
|
||||
if trezorhub, err := usbwallet.NewTrezorHubWithWebUSB(); err != nil {
|
||||
log.Warn(fmt.Sprintf("Failed to start WebUSB Trezor hub, disabling: %v", err))
|
||||
} else {
|
||||
backends = append(backends, trezorhub)
|
||||
log.Debug("Trezor support enabled")
|
||||
log.Debug("Trezor support enabled via WebUSB")
|
||||
}
|
||||
}
|
||||
// Clef doesn't allow insecure http account unlock.
|
||||
|
8
vendor/github.com/karalabe/hid/LICENSE.md
generated
vendored
8
vendor/github.com/karalabe/hid/LICENSE.md
generated
vendored
@ -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.
|
53
vendor/github.com/karalabe/hid/README.md
generated
vendored
53
vendor/github.com/karalabe/hid/README.md
generated
vendored
@ -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.
|
54
vendor/github.com/karalabe/hid/generic.go
generated
vendored
54
vendor/github.com/karalabe/hid/generic.go
generated
vendored
@ -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
|
||||
}
|
52
vendor/github.com/karalabe/hid/hid.go
generated
vendored
52
vendor/github.com/karalabe/hid/hid.go
generated
vendored
@ -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
|
||||
}
|
76
vendor/github.com/karalabe/hid/hid_disabled.go
generated
vendored
76
vendor/github.com/karalabe/hid/hid_disabled.go
generated
vendored
@ -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
|
||||
}
|
459
vendor/github.com/karalabe/hid/hid_enabled.go
generated
vendored
459
vendor/github.com/karalabe/hid/hid_enabled.go
generated
vendored
@ -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
|
||||
}
|
728
vendor/github.com/karalabe/hid/libusb/libusb/os/poll_windows.c
generated
vendored
728
vendor/github.com/karalabe/hid/libusb/libusb/os/poll_windows.c
generated
vendored
@ -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;
|
||||
}
|
55
vendor/github.com/karalabe/hid/libusb/libusb/os/threads_posix.h
generated
vendored
55
vendor/github.com/karalabe/hid/libusb/libusb/os/threads_posix.h
generated
vendored
@ -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 */
|
259
vendor/github.com/karalabe/hid/libusb/libusb/os/threads_windows.c
generated
vendored
259
vendor/github.com/karalabe/hid/libusb/libusb/os/threads_windows.c
generated
vendored
@ -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();
|
||||
}
|
63
vendor/github.com/karalabe/hid/libusb/libusb/os/windows_nt_common.h
generated
vendored
63
vendor/github.com/karalabe/hid/libusb/libusb/os/windows_nt_common.h
generated
vendored
@ -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
|
4290
vendor/github.com/karalabe/hid/libusb/libusb/os/windows_winusb.c
generated
vendored
4290
vendor/github.com/karalabe/hid/libusb/libusb/os/windows_winusb.c
generated
vendored
File diff suppressed because it is too large
Load Diff
1
vendor/github.com/karalabe/hid/libusb/libusb/version_nano.h
generated
vendored
1
vendor/github.com/karalabe/hid/libusb/libusb/version_nano.h
generated
vendored
@ -1 +0,0 @@
|
||||
#define LIBUSB_NANO 11182
|
53
vendor/github.com/karalabe/hid/usb.go
generated
vendored
53
vendor/github.com/karalabe/hid/usb.go
generated
vendored
@ -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
6
vendor/github.com/karalabe/usb/AUTHORS
generated
vendored
Normal 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
165
vendor/github.com/karalabe/usb/LICENSE
generated
vendored
Normal 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
47
vendor/github.com/karalabe/usb/README.md
generated
vendored
Normal 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).
|
@ -1,7 +1,7 @@
|
||||
os: Visual Studio 2015
|
||||
|
||||
# Clone directly into GOPATH.
|
||||
clone_folder: C:\gopath\src\github.com\karalabe\hid
|
||||
clone_folder: C:\gopath\src\github.com\karalabe\usb
|
||||
clone_depth: 1
|
||||
version: "{branch}.{build}"
|
||||
environment:
|
||||
@ -22,8 +22,8 @@ environment:
|
||||
|
||||
install:
|
||||
- rmdir C:\go /s /q
|
||||
- appveyor DownloadFile https://storage.googleapis.com/golang/go1.10.1.windows-%GOARCH%.zip
|
||||
- 7z x go1.10.1.windows-%GOARCH%.zip -y -oC:\ > NUL
|
||||
- appveyor DownloadFile https://storage.googleapis.com/golang/go1.12.5.windows-%GOARCH%.zip
|
||||
- 7z x go1.12.5.windows-%GOARCH%.zip -y -oC:\ > NUL
|
||||
- go version
|
||||
- gcc --version
|
||||
|
76
vendor/github.com/karalabe/usb/demo.go
generated
vendored
Normal file
76
vendor/github.com/karalabe/usb/demo.go
generated
vendored
Normal 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
3
vendor/github.com/karalabe/usb/go.mod
generated
vendored
Normal 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
42
vendor/github.com/karalabe/usb/hid_disabled.go
generated
vendored
Normal 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
187
vendor/github.com/karalabe/usb/hid_enabled.go
generated
vendored
Normal 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
|
||||
}
|
0
vendor/github.com/karalabe/hid/hidapi/windows/hid.c → vendor/github.com/karalabe/usb/hidapi/windows/hid.c
generated
vendored
Executable file → Normal file
0
vendor/github.com/karalabe/hid/hidapi/windows/hid.c → vendor/github.com/karalabe/usb/hidapi/windows/hid.c
generated
vendored
Executable file → Normal file
74
vendor/github.com/karalabe/usb/libs.go
generated
vendored
Normal file
74
vendor/github.com/karalabe/usb/libs.go
generated
vendored
Normal 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"
|
@ -8,14 +8,19 @@ Copyright © 2010-2012 Michael Plante <michael.plante@gmail.com>
|
||||
Copyright © 2011-2013 Hans de Goede <hdegoede@redhat.com>
|
||||
Copyright © 2012-2013 Martin Pieuchot <mpi@openbsd.org>
|
||||
Copyright © 2012-2013 Toby Gray <toby.gray@realvnc.com>
|
||||
Copyright © 2013-2015 Chris Dickens <christopher.a.dickens@gmail.com>
|
||||
Copyright © 2013-2018 Chris Dickens <christopher.a.dickens@gmail.com>
|
||||
|
||||
Other contributors:
|
||||
Adrian Bunk
|
||||
Akshay Jaggi
|
||||
Alan Ott
|
||||
Alan Stern
|
||||
Alex Vatchenko
|
||||
Andrew Fernandes
|
||||
Andy Chunyu
|
||||
Andy McFadden
|
||||
Angus Gratton
|
||||
Anil Nair
|
||||
Anthony Clay
|
||||
Antonio Ospite
|
||||
Artem Egorkine
|
||||
@ -23,12 +28,17 @@ Aurelien Jarno
|
||||
Bastien Nocera
|
||||
Bei Zhang
|
||||
Benjamin Dobell
|
||||
Brent Rector
|
||||
Carl Karsten
|
||||
Christophe Zeitouny
|
||||
Colin Walters
|
||||
Dave Camarillo
|
||||
David Engraf
|
||||
David Moore
|
||||
Davidlohr Bueso
|
||||
Dmitry Fleytman
|
||||
Doug Johnston
|
||||
Evan Hunter
|
||||
Federico Manzan
|
||||
Felipe Balbi
|
||||
Florian Albrechtskirchinger
|
||||
@ -41,23 +51,34 @@ Hans Ulrich Niedermann
|
||||
Hector Martin
|
||||
Hoi-Ho Chan
|
||||
Ilya Konstantinov
|
||||
Jakub Klama
|
||||
James Hanko
|
||||
Jeffrey Nichols
|
||||
Johann Richard
|
||||
John Sheu
|
||||
Jonathon Jongsma
|
||||
Joost Muller
|
||||
Josh Gao
|
||||
Joshua Blake
|
||||
Justin Bischoff
|
||||
KIMURA Masaru
|
||||
Karsten Koenig
|
||||
Konrad Rzepecki
|
||||
Kuangye Guo
|
||||
Lars Kanis
|
||||
Lars Wirzenius
|
||||
Lei Chen
|
||||
Luca Longinotti
|
||||
Marcus Meissner
|
||||
Markus Heidelberg
|
||||
Martin Ettl
|
||||
Martin Koegler
|
||||
Matthew Stapleton
|
||||
Matthias Bolte
|
||||
Michel Zou
|
||||
Mike Frysinger
|
||||
Mikhail Gusarov
|
||||
Morgan Leborgne
|
||||
Moritz Fischer
|
||||
Ларионов Даниил
|
||||
Nicholas Corgan
|
||||
@ -66,10 +87,17 @@ Orin Eman
|
||||
Paul Fertser
|
||||
Pekka Nikander
|
||||
Rob Walker
|
||||
Romain Vimont
|
||||
Roman Kalashnikov
|
||||
Sameeh Jubran
|
||||
Sean McBride
|
||||
Sebastian Pipping
|
||||
Sergey Serb
|
||||
Simon Haggett
|
||||
Simon Newton
|
||||
Stefan Agner
|
||||
Stefan Tauner
|
||||
Steinar H. Gunderson
|
||||
Thomas Röfer
|
||||
Tim Hutt
|
||||
Tim Roberts
|
||||
@ -81,9 +109,11 @@ Uri Lublin
|
||||
Vasily Khoruzhick
|
||||
Vegard Storheil Eriksen
|
||||
Venkatesh Shukla
|
||||
Vianney le Clément de Saint-Marcq
|
||||
Victor Toso
|
||||
Vitali Lovich
|
||||
William Skellenger
|
||||
Xiaofan Chen
|
||||
Zoltán Kovács
|
||||
Роман Донченко
|
||||
parafin
|
||||
xantares
|
@ -44,32 +44,6 @@
|
||||
#include "libusbi.h"
|
||||
#include "hotplug.h"
|
||||
|
||||
#if defined(OS_LINUX)
|
||||
const struct usbi_os_backend * const usbi_backend = &linux_usbfs_backend;
|
||||
#elif defined(OS_DARWIN)
|
||||
const struct usbi_os_backend * const usbi_backend = &darwin_backend;
|
||||
#elif defined(OS_OPENBSD)
|
||||
const struct usbi_os_backend * const usbi_backend = &openbsd_backend;
|
||||
#elif defined(OS_NETBSD)
|
||||
const struct usbi_os_backend * const usbi_backend = &netbsd_backend;
|
||||
#elif defined(OS_WINDOWS)
|
||||
|
||||
#if defined(USE_USBDK)
|
||||
const struct usbi_os_backend * const usbi_backend = &usbdk_backend;
|
||||
#else
|
||||
const struct usbi_os_backend * const usbi_backend = &windows_backend;
|
||||
#endif
|
||||
|
||||
#elif defined(OS_WINCE)
|
||||
const struct usbi_os_backend * const usbi_backend = &wince_backend;
|
||||
#elif defined(OS_HAIKU)
|
||||
const struct usbi_os_backend * const usbi_backend = &haiku_usb_raw_backend;
|
||||
#elif defined (OS_SUNOS)
|
||||
const struct usbi_os_backend * const usbi_backend = &sunos_backend;
|
||||
#else
|
||||
#error "Unsupported OS"
|
||||
#endif
|
||||
|
||||
struct libusb_context *usbi_default_context = NULL;
|
||||
static const struct libusb_version libusb_version_internal =
|
||||
{ LIBUSB_MAJOR, LIBUSB_MINOR, LIBUSB_MICRO, LIBUSB_NANO,
|
||||
@ -142,15 +116,17 @@ struct list_head active_contexts_list;
|
||||
* libusb uses stderr for all logging. By default, logging is set to NONE,
|
||||
* which means that no output will be produced. However, unless the library
|
||||
* has been compiled with logging disabled, then any application calls to
|
||||
* libusb_set_debug(), or the setting of the environmental variable
|
||||
* LIBUSB_DEBUG outside of the application, can result in logging being
|
||||
* produced. Your application should therefore not close stderr, but instead
|
||||
* direct it to the null device if its output is undesirable.
|
||||
* libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, level), or the setting of the
|
||||
* environmental variable LIBUSB_DEBUG outside of the application, can result
|
||||
* in logging being produced. Your application should therefore not close
|
||||
* stderr, but instead direct it to the null device if its output is
|
||||
* undesirable.
|
||||
*
|
||||
* The libusb_set_debug() function can be used to enable logging of certain
|
||||
* messages. Under standard configuration, libusb doesn't really log much
|
||||
* so you are advised to use this function to enable all error/warning/
|
||||
* informational messages. It will help debug problems with your software.
|
||||
* The libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, level) function can be
|
||||
* used to enable logging of certain messages. Under standard configuration,
|
||||
* libusb doesn't really log much so you are advised to use this function
|
||||
* to enable all error/warning/ informational messages. It will help debug
|
||||
* problems with your software.
|
||||
*
|
||||
* The logged messages are unstructured. There is no one-to-one correspondence
|
||||
* between messages being logged and success or failure return codes from
|
||||
@ -165,18 +141,20 @@ struct list_head active_contexts_list;
|
||||
*
|
||||
* The LIBUSB_DEBUG environment variable can be used to enable message logging
|
||||
* at run-time. This environment variable should be set to a log level number,
|
||||
* which is interpreted the same as the libusb_set_debug() parameter. When this
|
||||
* which is interpreted the same as the
|
||||
* libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, level) parameter. When this
|
||||
* environment variable is set, the message logging verbosity level is fixed
|
||||
* and libusb_set_debug() effectively does nothing.
|
||||
* and libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, level) effectively does
|
||||
* nothing.
|
||||
*
|
||||
* libusb can be compiled without any logging functions, useful for embedded
|
||||
* systems. In this case, libusb_set_debug() and the LIBUSB_DEBUG environment
|
||||
* variable have no effects.
|
||||
* systems. In this case, libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, level)
|
||||
* and the LIBUSB_DEBUG environment variable have no effects.
|
||||
*
|
||||
* libusb can also be compiled with verbose debugging messages always. When
|
||||
* the library is compiled in this way, all messages of all verbosities are
|
||||
* always logged. libusb_set_debug() and the LIBUSB_DEBUG environment variable
|
||||
* have no effects.
|
||||
* always logged. libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, level) and
|
||||
* the LIBUSB_DEBUG environment variable have no effects.
|
||||
*
|
||||
* \section remarks Other remarks
|
||||
*
|
||||
@ -187,6 +165,20 @@ struct list_head active_contexts_list;
|
||||
/**
|
||||
* \page libusb_caveats Caveats
|
||||
*
|
||||
* \section fork Fork considerations
|
||||
*
|
||||
* libusb is <em>not</em> designed to work across fork() calls. Depending on
|
||||
* the platform, there may be resources in the parent process that are not
|
||||
* available to the child (e.g. the hotplug monitor thread on Linux). In
|
||||
* addition, since the parent and child will share libusb's internal file
|
||||
* descriptors, using libusb in any way from the child could cause the parent
|
||||
* process's \ref libusb_context to get into an inconsistent state.
|
||||
*
|
||||
* On Linux, libusb's file descriptors will be marked as CLOEXEC, which means
|
||||
* that it is safe to fork() and exec() without worrying about the child
|
||||
* process needing to clean up state or having access to these file descriptors.
|
||||
* Other platforms may not be so forgiving, so consider yourself warned!
|
||||
*
|
||||
* \section devresets Device resets
|
||||
*
|
||||
* The libusb_reset_device() function allows you to reset a device. If your
|
||||
@ -291,7 +283,6 @@ if (cfg != desired)
|
||||
* information about the end of the short packet, and the user probably wanted
|
||||
* that surplus data to arrive in the next logical transfer.
|
||||
*
|
||||
*
|
||||
* \section zlp Zero length packets
|
||||
*
|
||||
* - libusb is able to send a packet of zero length to an endpoint simply by
|
||||
@ -310,7 +301,7 @@ if (cfg != desired)
|
||||
* developed modules may both use libusb.
|
||||
*
|
||||
* libusb is written to allow for these multiple user scenarios. The two
|
||||
* "instances" of libusb will not interfere: libusb_set_debug() calls
|
||||
* "instances" of libusb will not interfere: libusb_set_option() calls
|
||||
* from one user will not affect the same settings for other users, other
|
||||
* users can continue using libusb after one of them calls libusb_exit(), etc.
|
||||
*
|
||||
@ -435,6 +426,7 @@ if (cfg != desired)
|
||||
* - libusb_set_debug()
|
||||
* - libusb_set_interface_alt_setting()
|
||||
* - libusb_set_iso_packet_lengths()
|
||||
* - libusb_set_option()
|
||||
* - libusb_setlocale()
|
||||
* - libusb_set_pollfd_notifiers()
|
||||
* - libusb_strerror()
|
||||
@ -478,6 +470,7 @@ if (cfg != desired)
|
||||
* - \ref libusb_iso_sync_type
|
||||
* - \ref libusb_iso_usage_type
|
||||
* - \ref libusb_log_level
|
||||
* - \ref libusb_option
|
||||
* - \ref libusb_request_recipient
|
||||
* - \ref libusb_request_type
|
||||
* - \ref libusb_speed
|
||||
@ -680,7 +673,7 @@ struct discovered_devs *discovered_devs_append(
|
||||
struct libusb_device *usbi_alloc_device(struct libusb_context *ctx,
|
||||
unsigned long session_id)
|
||||
{
|
||||
size_t priv_size = usbi_backend->device_priv_size;
|
||||
size_t priv_size = usbi_backend.device_priv_size;
|
||||
struct libusb_device *dev = calloc(1, sizeof(*dev) + priv_size);
|
||||
int r;
|
||||
|
||||
@ -824,8 +817,8 @@ ssize_t API_EXPORTED libusb_get_device_list(libusb_context *ctx,
|
||||
/* backend provides hotplug support */
|
||||
struct libusb_device *dev;
|
||||
|
||||
if (usbi_backend->hotplug_poll)
|
||||
usbi_backend->hotplug_poll();
|
||||
if (usbi_backend.hotplug_poll)
|
||||
usbi_backend.hotplug_poll();
|
||||
|
||||
usbi_mutex_lock(&ctx->usb_devs_lock);
|
||||
list_for_each_entry(dev, &ctx->usb_devs, list, struct libusb_device) {
|
||||
@ -839,7 +832,7 @@ ssize_t API_EXPORTED libusb_get_device_list(libusb_context *ctx,
|
||||
usbi_mutex_unlock(&ctx->usb_devs_lock);
|
||||
} else {
|
||||
/* backend does not provide hotplug support */
|
||||
r = usbi_backend->get_device_list(ctx, &discdevs);
|
||||
r = usbi_backend.get_device_list(ctx, &discdevs);
|
||||
}
|
||||
|
||||
if (r < 0) {
|
||||
@ -1167,8 +1160,8 @@ void API_EXPORTED libusb_unref_device(libusb_device *dev)
|
||||
|
||||
libusb_unref_device(dev->parent_dev);
|
||||
|
||||
if (usbi_backend->destroy_device)
|
||||
usbi_backend->destroy_device(dev);
|
||||
if (usbi_backend.destroy_device)
|
||||
usbi_backend.destroy_device(dev);
|
||||
|
||||
if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
|
||||
/* backend does not support hotplug */
|
||||
@ -1242,7 +1235,7 @@ int API_EXPORTED libusb_open(libusb_device *dev,
|
||||
{
|
||||
struct libusb_context *ctx = DEVICE_CTX(dev);
|
||||
struct libusb_device_handle *_dev_handle;
|
||||
size_t priv_size = usbi_backend->device_handle_priv_size;
|
||||
size_t priv_size = usbi_backend.device_handle_priv_size;
|
||||
int r;
|
||||
usbi_dbg("open %d.%d", dev->bus_number, dev->device_address);
|
||||
|
||||
@ -1265,7 +1258,7 @@ int API_EXPORTED libusb_open(libusb_device *dev,
|
||||
_dev_handle->claimed_interfaces = 0;
|
||||
memset(&_dev_handle->os_priv, 0, priv_size);
|
||||
|
||||
r = usbi_backend->open(_dev_handle);
|
||||
r = usbi_backend.open(_dev_handle);
|
||||
if (r < 0) {
|
||||
usbi_dbg("open %d.%d returns %d", dev->bus_number, dev->device_address, r);
|
||||
libusb_unref_device(dev);
|
||||
@ -1382,7 +1375,7 @@ static void do_close(struct libusb_context *ctx,
|
||||
list_del(&dev_handle->list);
|
||||
usbi_mutex_unlock(&ctx->open_devs_lock);
|
||||
|
||||
usbi_backend->close(dev_handle);
|
||||
usbi_backend.close(dev_handle);
|
||||
libusb_unref_device(dev_handle->dev);
|
||||
usbi_mutex_destroy(&dev_handle->lock);
|
||||
free(dev_handle);
|
||||
@ -1491,8 +1484,8 @@ int API_EXPORTED libusb_get_configuration(libusb_device_handle *dev_handle,
|
||||
int r = LIBUSB_ERROR_NOT_SUPPORTED;
|
||||
|
||||
usbi_dbg("");
|
||||
if (usbi_backend->get_configuration)
|
||||
r = usbi_backend->get_configuration(dev_handle, config);
|
||||
if (usbi_backend.get_configuration)
|
||||
r = usbi_backend.get_configuration(dev_handle, config);
|
||||
|
||||
if (r == LIBUSB_ERROR_NOT_SUPPORTED) {
|
||||
uint8_t tmp = 0;
|
||||
@ -1567,7 +1560,7 @@ int API_EXPORTED libusb_set_configuration(libusb_device_handle *dev_handle,
|
||||
int configuration)
|
||||
{
|
||||
usbi_dbg("configuration %d", configuration);
|
||||
return usbi_backend->set_configuration(dev_handle, configuration);
|
||||
return usbi_backend.set_configuration(dev_handle, configuration);
|
||||
}
|
||||
|
||||
/** \ingroup libusb_dev
|
||||
@ -1614,7 +1607,7 @@ int API_EXPORTED libusb_claim_interface(libusb_device_handle *dev_handle,
|
||||
if (dev_handle->claimed_interfaces & (1 << interface_number))
|
||||
goto out;
|
||||
|
||||
r = usbi_backend->claim_interface(dev_handle, interface_number);
|
||||
r = usbi_backend.claim_interface(dev_handle, interface_number);
|
||||
if (r == 0)
|
||||
dev_handle->claimed_interfaces |= 1 << interface_number;
|
||||
|
||||
@ -1657,7 +1650,7 @@ int API_EXPORTED libusb_release_interface(libusb_device_handle *dev_handle,
|
||||
goto out;
|
||||
}
|
||||
|
||||
r = usbi_backend->release_interface(dev_handle, interface_number);
|
||||
r = usbi_backend.release_interface(dev_handle, interface_number);
|
||||
if (r == 0)
|
||||
dev_handle->claimed_interfaces &= ~(1 << interface_number);
|
||||
|
||||
@ -1707,7 +1700,7 @@ int API_EXPORTED libusb_set_interface_alt_setting(libusb_device_handle *dev_hand
|
||||
}
|
||||
usbi_mutex_unlock(&dev_handle->lock);
|
||||
|
||||
return usbi_backend->set_interface_altsetting(dev_handle, interface_number,
|
||||
return usbi_backend.set_interface_altsetting(dev_handle, interface_number,
|
||||
alternate_setting);
|
||||
}
|
||||
|
||||
@ -1734,7 +1727,7 @@ int API_EXPORTED libusb_clear_halt(libusb_device_handle *dev_handle,
|
||||
if (!dev_handle->dev->attached)
|
||||
return LIBUSB_ERROR_NO_DEVICE;
|
||||
|
||||
return usbi_backend->clear_halt(dev_handle, endpoint);
|
||||
return usbi_backend.clear_halt(dev_handle, endpoint);
|
||||
}
|
||||
|
||||
/** \ingroup libusb_dev
|
||||
@ -1762,7 +1755,7 @@ int API_EXPORTED libusb_reset_device(libusb_device_handle *dev_handle)
|
||||
if (!dev_handle->dev->attached)
|
||||
return LIBUSB_ERROR_NO_DEVICE;
|
||||
|
||||
return usbi_backend->reset_device(dev_handle);
|
||||
return usbi_backend.reset_device(dev_handle);
|
||||
}
|
||||
|
||||
/** \ingroup libusb_asyncio
|
||||
@ -1794,8 +1787,8 @@ int API_EXPORTED libusb_alloc_streams(libusb_device_handle *dev_handle,
|
||||
if (!dev_handle->dev->attached)
|
||||
return LIBUSB_ERROR_NO_DEVICE;
|
||||
|
||||
if (usbi_backend->alloc_streams)
|
||||
return usbi_backend->alloc_streams(dev_handle, num_streams, endpoints,
|
||||
if (usbi_backend.alloc_streams)
|
||||
return usbi_backend.alloc_streams(dev_handle, num_streams, endpoints,
|
||||
num_endpoints);
|
||||
else
|
||||
return LIBUSB_ERROR_NOT_SUPPORTED;
|
||||
@ -1821,8 +1814,8 @@ int API_EXPORTED libusb_free_streams(libusb_device_handle *dev_handle,
|
||||
if (!dev_handle->dev->attached)
|
||||
return LIBUSB_ERROR_NO_DEVICE;
|
||||
|
||||
if (usbi_backend->free_streams)
|
||||
return usbi_backend->free_streams(dev_handle, endpoints,
|
||||
if (usbi_backend.free_streams)
|
||||
return usbi_backend.free_streams(dev_handle, endpoints,
|
||||
num_endpoints);
|
||||
else
|
||||
return LIBUSB_ERROR_NOT_SUPPORTED;
|
||||
@ -1859,8 +1852,8 @@ unsigned char * LIBUSB_CALL libusb_dev_mem_alloc(libusb_device_handle *dev_handl
|
||||
if (!dev_handle->dev->attached)
|
||||
return NULL;
|
||||
|
||||
if (usbi_backend->dev_mem_alloc)
|
||||
return usbi_backend->dev_mem_alloc(dev_handle, length);
|
||||
if (usbi_backend.dev_mem_alloc)
|
||||
return usbi_backend.dev_mem_alloc(dev_handle, length);
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
@ -1876,8 +1869,8 @@ unsigned char * LIBUSB_CALL libusb_dev_mem_alloc(libusb_device_handle *dev_handl
|
||||
int API_EXPORTED libusb_dev_mem_free(libusb_device_handle *dev_handle,
|
||||
unsigned char *buffer, size_t length)
|
||||
{
|
||||
if (usbi_backend->dev_mem_free)
|
||||
return usbi_backend->dev_mem_free(dev_handle, buffer, length);
|
||||
if (usbi_backend.dev_mem_free)
|
||||
return usbi_backend.dev_mem_free(dev_handle, buffer, length);
|
||||
else
|
||||
return LIBUSB_ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
@ -1907,8 +1900,8 @@ int API_EXPORTED libusb_kernel_driver_active(libusb_device_handle *dev_handle,
|
||||
if (!dev_handle->dev->attached)
|
||||
return LIBUSB_ERROR_NO_DEVICE;
|
||||
|
||||
if (usbi_backend->kernel_driver_active)
|
||||
return usbi_backend->kernel_driver_active(dev_handle, interface_number);
|
||||
if (usbi_backend.kernel_driver_active)
|
||||
return usbi_backend.kernel_driver_active(dev_handle, interface_number);
|
||||
else
|
||||
return LIBUSB_ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
@ -1942,8 +1935,8 @@ int API_EXPORTED libusb_detach_kernel_driver(libusb_device_handle *dev_handle,
|
||||
if (!dev_handle->dev->attached)
|
||||
return LIBUSB_ERROR_NO_DEVICE;
|
||||
|
||||
if (usbi_backend->detach_kernel_driver)
|
||||
return usbi_backend->detach_kernel_driver(dev_handle, interface_number);
|
||||
if (usbi_backend.detach_kernel_driver)
|
||||
return usbi_backend.detach_kernel_driver(dev_handle, interface_number);
|
||||
else
|
||||
return LIBUSB_ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
@ -1976,8 +1969,8 @@ int API_EXPORTED libusb_attach_kernel_driver(libusb_device_handle *dev_handle,
|
||||
if (!dev_handle->dev->attached)
|
||||
return LIBUSB_ERROR_NO_DEVICE;
|
||||
|
||||
if (usbi_backend->attach_kernel_driver)
|
||||
return usbi_backend->attach_kernel_driver(dev_handle, interface_number);
|
||||
if (usbi_backend.attach_kernel_driver)
|
||||
return usbi_backend.attach_kernel_driver(dev_handle, interface_number);
|
||||
else
|
||||
return LIBUSB_ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
@ -2007,7 +2000,7 @@ int API_EXPORTED libusb_attach_kernel_driver(libusb_device_handle *dev_handle,
|
||||
int API_EXPORTED libusb_set_auto_detach_kernel_driver(
|
||||
libusb_device_handle *dev_handle, int enable)
|
||||
{
|
||||
if (!(usbi_backend->caps & USBI_CAP_SUPPORTS_DETACH_KERNEL_DRIVER))
|
||||
if (!(usbi_backend.caps & USBI_CAP_SUPPORTS_DETACH_KERNEL_DRIVER))
|
||||
return LIBUSB_ERROR_NOT_SUPPORTED;
|
||||
|
||||
dev_handle->auto_detach_kernel_driver = enable;
|
||||
@ -2015,36 +2008,99 @@ int API_EXPORTED libusb_set_auto_detach_kernel_driver(
|
||||
}
|
||||
|
||||
/** \ingroup libusb_lib
|
||||
* Set log message verbosity.
|
||||
*
|
||||
* The default level is LIBUSB_LOG_LEVEL_NONE, which means no messages are ever
|
||||
* printed. If you choose to increase the message verbosity level, ensure
|
||||
* that your application does not close the stdout/stderr file descriptors.
|
||||
*
|
||||
* 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
|
||||
* \deprecated Use libusb_set_option() instead using the
|
||||
* \ref LIBUSB_OPTION_LOG_LEVEL option.
|
||||
*/
|
||||
void API_EXPORTED libusb_set_debug(libusb_context *ctx, int level)
|
||||
{
|
||||
#if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING)
|
||||
USBI_GET_CONTEXT(ctx);
|
||||
if (!ctx->debug_fixed)
|
||||
ctx->debug = level;
|
||||
if (!ctx->debug_fixed) {
|
||||
level = CLAMP(level, LIBUSB_LOG_LEVEL_NONE, LIBUSB_LOG_LEVEL_DEBUG);
|
||||
ctx->debug = (enum libusb_log_level)level;
|
||||
}
|
||||
#else
|
||||
UNUSED(ctx);
|
||||
UNUSED(level);
|
||||
#endif
|
||||
}
|
||||
|
||||
/** \ingroup libusb_lib
|
||||
* Set an option in the library.
|
||||
*
|
||||
* 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
|
||||
* Initialize libusb. This function must be called before calling any other
|
||||
@ -2062,7 +2118,7 @@ void API_EXPORTED libusb_set_debug(libusb_context *ctx, int level)
|
||||
int API_EXPORTED libusb_init(libusb_context **context)
|
||||
{
|
||||
struct libusb_device *dev, *next;
|
||||
char *dbg = getenv("LIBUSB_DEBUG");
|
||||
size_t priv_size = usbi_backend.context_priv_size;
|
||||
struct libusb_context *ctx;
|
||||
static int first_init = 1;
|
||||
int r = 0;
|
||||
@ -2070,7 +2126,7 @@ int API_EXPORTED libusb_init(libusb_context **context)
|
||||
usbi_mutex_static_lock(&default_context_lock);
|
||||
|
||||
if (!timestamp_origin.tv_sec) {
|
||||
usbi_backend->clock_gettime(USBI_CLOCK_REALTIME, ×tamp_origin);
|
||||
usbi_backend.clock_gettime(USBI_CLOCK_REALTIME, ×tamp_origin);
|
||||
}
|
||||
|
||||
if (!context && usbi_default_context) {
|
||||
@ -2080,21 +2136,17 @@ int API_EXPORTED libusb_init(libusb_context **context)
|
||||
return 0;
|
||||
}
|
||||
|
||||
ctx = calloc(1, sizeof(*ctx));
|
||||
ctx = calloc(1, sizeof(*ctx) + priv_size);
|
||||
if (!ctx) {
|
||||
r = LIBUSB_ERROR_NO_MEM;
|
||||
goto err_unlock;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_DEBUG_LOGGING
|
||||
ctx->debug = LIBUSB_LOG_LEVEL_DEBUG;
|
||||
#endif
|
||||
|
||||
if (dbg) {
|
||||
ctx->debug = atoi(dbg);
|
||||
if (ctx->debug)
|
||||
#if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING)
|
||||
ctx->debug = get_env_debug_level();
|
||||
if (ctx->debug != LIBUSB_LOG_LEVEL_NONE)
|
||||
ctx->debug_fixed = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* default context should be initialized before calling usbi_dbg */
|
||||
if (!usbi_default_context) {
|
||||
@ -2112,6 +2164,7 @@ int API_EXPORTED libusb_init(libusb_context **context)
|
||||
list_init(&ctx->usb_devs);
|
||||
list_init(&ctx->open_devs);
|
||||
list_init(&ctx->hotplug_cbs);
|
||||
ctx->next_hotplug_cb_handle = 1;
|
||||
|
||||
usbi_mutex_static_lock(&active_contexts_lock);
|
||||
if (first_init) {
|
||||
@ -2121,8 +2174,8 @@ int API_EXPORTED libusb_init(libusb_context **context)
|
||||
list_add (&ctx->list, &active_contexts_list);
|
||||
usbi_mutex_static_unlock(&active_contexts_lock);
|
||||
|
||||
if (usbi_backend->init) {
|
||||
r = usbi_backend->init(ctx);
|
||||
if (usbi_backend.init) {
|
||||
r = usbi_backend.init(ctx);
|
||||
if (r)
|
||||
goto err_free_ctx;
|
||||
}
|
||||
@ -2139,8 +2192,8 @@ int API_EXPORTED libusb_init(libusb_context **context)
|
||||
return 0;
|
||||
|
||||
err_backend_exit:
|
||||
if (usbi_backend->exit)
|
||||
usbi_backend->exit();
|
||||
if (usbi_backend.exit)
|
||||
usbi_backend.exit(ctx);
|
||||
err_free_ctx:
|
||||
if (ctx == usbi_default_context) {
|
||||
usbi_default_context = NULL;
|
||||
@ -2200,7 +2253,7 @@ void API_EXPORTED libusb_exit(struct libusb_context *ctx)
|
||||
usbi_mutex_static_unlock(&active_contexts_lock);
|
||||
|
||||
if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
|
||||
usbi_hotplug_deregister_all(ctx);
|
||||
usbi_hotplug_deregister(ctx, 1);
|
||||
|
||||
/*
|
||||
* Ensure any pending unplug events are read from the hotplug
|
||||
@ -2230,8 +2283,8 @@ void API_EXPORTED libusb_exit(struct libusb_context *ctx)
|
||||
usbi_warn(ctx, "application left some devices open");
|
||||
|
||||
usbi_io_exit(ctx);
|
||||
if (usbi_backend->exit)
|
||||
usbi_backend->exit();
|
||||
if (usbi_backend.exit)
|
||||
usbi_backend.exit(ctx);
|
||||
|
||||
usbi_mutex_destroy(&ctx->open_devs_lock);
|
||||
usbi_mutex_destroy(&ctx->usb_devs_lock);
|
||||
@ -2253,15 +2306,17 @@ int API_EXPORTED libusb_has_capability(uint32_t capability)
|
||||
case LIBUSB_CAP_HAS_CAPABILITY:
|
||||
return 1;
|
||||
case LIBUSB_CAP_HAS_HOTPLUG:
|
||||
return !(usbi_backend->get_device_list);
|
||||
return !(usbi_backend.get_device_list);
|
||||
case LIBUSB_CAP_HAS_HID_ACCESS:
|
||||
return (usbi_backend->caps & USBI_CAP_HAS_HID_ACCESS);
|
||||
return (usbi_backend.caps & USBI_CAP_HAS_HID_ACCESS);
|
||||
case LIBUSB_CAP_SUPPORTS_DETACH_KERNEL_DRIVER:
|
||||
return (usbi_backend->caps & USBI_CAP_SUPPORTS_DETACH_KERNEL_DRIVER);
|
||||
return (usbi_backend.caps & USBI_CAP_SUPPORTS_DETACH_KERNEL_DRIVER);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_LOGGING
|
||||
|
||||
/* this is defined in libusbi.h if needed */
|
||||
#ifdef LIBUSB_PRINTF_WIN32
|
||||
/*
|
||||
@ -2301,10 +2356,9 @@ int usbi_vsnprintf(char *str, size_t size, const char *format, va_list ap)
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
#endif /* LIBUSB_PRINTF_WIN32 */
|
||||
|
||||
static void usbi_log_str(struct libusb_context *ctx,
|
||||
enum libusb_log_level level, const char * str)
|
||||
static void usbi_log_str(enum libusb_log_level level, const char *str)
|
||||
{
|
||||
#if defined(USE_SYSTEM_LOGGING_FACILITY)
|
||||
#if defined(OS_WINDOWS)
|
||||
@ -2317,18 +2371,20 @@ static void usbi_log_str(struct libusb_context *ctx,
|
||||
#elif defined(__ANDROID__)
|
||||
int priority = ANDROID_LOG_UNKNOWN;
|
||||
switch (level) {
|
||||
case LIBUSB_LOG_LEVEL_INFO: priority = ANDROID_LOG_INFO; break;
|
||||
case LIBUSB_LOG_LEVEL_WARNING: priority = ANDROID_LOG_WARN; break;
|
||||
case LIBUSB_LOG_LEVEL_NONE: return;
|
||||
case LIBUSB_LOG_LEVEL_ERROR: priority = ANDROID_LOG_ERROR; break;
|
||||
case LIBUSB_LOG_LEVEL_WARNING: priority = ANDROID_LOG_WARN; break;
|
||||
case LIBUSB_LOG_LEVEL_INFO: priority = ANDROID_LOG_INFO; break;
|
||||
case LIBUSB_LOG_LEVEL_DEBUG: priority = ANDROID_LOG_DEBUG; break;
|
||||
}
|
||||
__android_log_write(priority, "libusb", str);
|
||||
#elif defined(HAVE_SYSLOG_FUNC)
|
||||
int syslog_level = LOG_INFO;
|
||||
switch (level) {
|
||||
case LIBUSB_LOG_LEVEL_INFO: syslog_level = LOG_INFO; break;
|
||||
case LIBUSB_LOG_LEVEL_WARNING: syslog_level = LOG_WARNING; break;
|
||||
case LIBUSB_LOG_LEVEL_NONE: return;
|
||||
case LIBUSB_LOG_LEVEL_ERROR: syslog_level = LOG_ERR; break;
|
||||
case LIBUSB_LOG_LEVEL_WARNING: syslog_level = LOG_WARNING; break;
|
||||
case LIBUSB_LOG_LEVEL_INFO: syslog_level = LOG_INFO; break;
|
||||
case LIBUSB_LOG_LEVEL_DEBUG: syslog_level = LOG_DEBUG; break;
|
||||
}
|
||||
syslog(syslog_level, "%s", str);
|
||||
@ -2339,14 +2395,13 @@ static void usbi_log_str(struct libusb_context *ctx,
|
||||
#else
|
||||
fputs(str, stderr);
|
||||
#endif /* USE_SYSTEM_LOGGING_FACILITY */
|
||||
UNUSED(ctx);
|
||||
UNUSED(level);
|
||||
}
|
||||
|
||||
void usbi_log_v(struct libusb_context *ctx, enum libusb_log_level level,
|
||||
const char *function, const char *format, va_list args)
|
||||
{
|
||||
const char *prefix = "";
|
||||
const char *prefix;
|
||||
char buf[USBI_MAX_LOG_LEN];
|
||||
struct timespec now;
|
||||
int global_debug, header_len, text_len;
|
||||
@ -2356,18 +2411,15 @@ void usbi_log_v(struct libusb_context *ctx, enum libusb_log_level level,
|
||||
global_debug = 1;
|
||||
UNUSED(ctx);
|
||||
#else
|
||||
int ctx_level = 0;
|
||||
enum libusb_log_level ctx_level = LIBUSB_LOG_LEVEL_NONE;
|
||||
|
||||
USBI_GET_CONTEXT(ctx);
|
||||
if (ctx) {
|
||||
if (ctx)
|
||||
ctx_level = ctx->debug;
|
||||
} else {
|
||||
char *dbg = getenv("LIBUSB_DEBUG");
|
||||
if (dbg)
|
||||
ctx_level = atoi(dbg);
|
||||
}
|
||||
global_debug = (ctx_level == LIBUSB_LOG_LEVEL_DEBUG);
|
||||
if (!ctx_level)
|
||||
else
|
||||
ctx_level = get_env_debug_level();
|
||||
|
||||
if (ctx_level == LIBUSB_LOG_LEVEL_NONE)
|
||||
return;
|
||||
if (level == LIBUSB_LOG_LEVEL_WARNING && ctx_level < LIBUSB_LOG_LEVEL_WARNING)
|
||||
return;
|
||||
@ -2375,13 +2427,15 @@ void usbi_log_v(struct libusb_context *ctx, enum libusb_log_level level,
|
||||
return;
|
||||
if (level == LIBUSB_LOG_LEVEL_DEBUG && ctx_level < LIBUSB_LOG_LEVEL_DEBUG)
|
||||
return;
|
||||
|
||||
global_debug = (ctx_level == LIBUSB_LOG_LEVEL_DEBUG);
|
||||
#endif
|
||||
|
||||
usbi_backend->clock_gettime(USBI_CLOCK_REALTIME, &now);
|
||||
usbi_backend.clock_gettime(USBI_CLOCK_REALTIME, &now);
|
||||
if ((global_debug) && (!has_debug_header_been_displayed)) {
|
||||
has_debug_header_been_displayed = 1;
|
||||
usbi_log_str(ctx, LIBUSB_LOG_LEVEL_DEBUG, "[timestamp] [threadID] facility level [function call] <message>" USBI_LOG_LINE_END);
|
||||
usbi_log_str(ctx, LIBUSB_LOG_LEVEL_DEBUG, "--------------------------------------------------------------------------------" USBI_LOG_LINE_END);
|
||||
usbi_log_str(LIBUSB_LOG_LEVEL_DEBUG, "[timestamp] [threadID] facility level [function call] <message>" USBI_LOG_LINE_END);
|
||||
usbi_log_str(LIBUSB_LOG_LEVEL_DEBUG, "--------------------------------------------------------------------------------" USBI_LOG_LINE_END);
|
||||
}
|
||||
if (now.tv_nsec < timestamp_origin.tv_nsec) {
|
||||
now.tv_sec--;
|
||||
@ -2391,20 +2445,20 @@ void usbi_log_v(struct libusb_context *ctx, enum libusb_log_level level,
|
||||
now.tv_nsec -= timestamp_origin.tv_nsec;
|
||||
|
||||
switch (level) {
|
||||
case LIBUSB_LOG_LEVEL_INFO:
|
||||
prefix = "info";
|
||||
case LIBUSB_LOG_LEVEL_NONE:
|
||||
return;
|
||||
case LIBUSB_LOG_LEVEL_ERROR:
|
||||
prefix = "error";
|
||||
break;
|
||||
case LIBUSB_LOG_LEVEL_WARNING:
|
||||
prefix = "warning";
|
||||
break;
|
||||
case LIBUSB_LOG_LEVEL_ERROR:
|
||||
prefix = "error";
|
||||
case LIBUSB_LOG_LEVEL_INFO:
|
||||
prefix = "info";
|
||||
break;
|
||||
case LIBUSB_LOG_LEVEL_DEBUG:
|
||||
prefix = "debug";
|
||||
break;
|
||||
case LIBUSB_LOG_LEVEL_NONE:
|
||||
return;
|
||||
default:
|
||||
prefix = "unknown";
|
||||
break;
|
||||
@ -2439,7 +2493,7 @@ void usbi_log_v(struct libusb_context *ctx, enum libusb_log_level level,
|
||||
}
|
||||
strcpy(buf + header_len + text_len, USBI_LOG_LINE_END);
|
||||
|
||||
usbi_log_str(ctx, level, buf);
|
||||
usbi_log_str(level, buf);
|
||||
}
|
||||
|
||||
void usbi_log(struct libusb_context *ctx, enum libusb_log_level level,
|
||||
@ -2452,6 +2506,8 @@ void usbi_log(struct libusb_context *ctx, enum libusb_log_level level,
|
||||
va_end (args);
|
||||
}
|
||||
|
||||
#endif /* ENABLE_LOGGING */
|
||||
|
||||
/** \ingroup libusb_misc
|
||||
* Returns a constant NULL-terminated string with the ASCII name of a libusb
|
||||
* error or transfer status code. The caller must not free() the returned
|
@ -333,7 +333,7 @@ static int parse_interface(libusb_context *ctx,
|
||||
goto err;
|
||||
if (r == 0) {
|
||||
ifp->bNumEndpoints = (uint8_t)i;
|
||||
break;;
|
||||
break;
|
||||
}
|
||||
|
||||
buffer += r;
|
||||
@ -513,7 +513,7 @@ int usbi_device_cache_descriptor(libusb_device *dev)
|
||||
{
|
||||
int r, host_endian = 0;
|
||||
|
||||
r = usbi_backend->get_device_descriptor(dev, (unsigned char *) &dev->device_descriptor,
|
||||
r = usbi_backend.get_device_descriptor(dev, (unsigned char *) &dev->device_descriptor,
|
||||
&host_endian);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@ -572,7 +572,7 @@ int API_EXPORTED libusb_get_active_config_descriptor(libusb_device *dev,
|
||||
int host_endian = 0;
|
||||
int r;
|
||||
|
||||
r = usbi_backend->get_active_config_descriptor(dev, tmp,
|
||||
r = usbi_backend.get_active_config_descriptor(dev, tmp,
|
||||
LIBUSB_DT_CONFIG_SIZE, &host_endian);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@ -587,7 +587,7 @@ int API_EXPORTED libusb_get_active_config_descriptor(libusb_device *dev,
|
||||
if (!buf)
|
||||
return LIBUSB_ERROR_NO_MEM;
|
||||
|
||||
r = usbi_backend->get_active_config_descriptor(dev, buf,
|
||||
r = usbi_backend.get_active_config_descriptor(dev, buf,
|
||||
_config.wTotalLength, &host_endian);
|
||||
if (r >= 0)
|
||||
r = raw_desc_to_config(dev->ctx, buf, r, host_endian, config);
|
||||
@ -625,7 +625,7 @@ int API_EXPORTED libusb_get_config_descriptor(libusb_device *dev,
|
||||
if (config_index >= dev->num_configurations)
|
||||
return LIBUSB_ERROR_NOT_FOUND;
|
||||
|
||||
r = usbi_backend->get_config_descriptor(dev, config_index, tmp,
|
||||
r = usbi_backend.get_config_descriptor(dev, config_index, tmp,
|
||||
LIBUSB_DT_CONFIG_SIZE, &host_endian);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@ -640,7 +640,7 @@ int API_EXPORTED libusb_get_config_descriptor(libusb_device *dev,
|
||||
if (!buf)
|
||||
return LIBUSB_ERROR_NO_MEM;
|
||||
|
||||
r = usbi_backend->get_config_descriptor(dev, config_index, buf,
|
||||
r = usbi_backend.get_config_descriptor(dev, config_index, buf,
|
||||
_config.wTotalLength, &host_endian);
|
||||
if (r >= 0)
|
||||
r = raw_desc_to_config(dev->ctx, buf, r, host_endian, config);
|
||||
@ -663,7 +663,7 @@ int usbi_get_config_index_by_value(struct libusb_device *dev,
|
||||
for (i = 0; i < dev->num_configurations; i++) {
|
||||
unsigned char tmp[6];
|
||||
int host_endian;
|
||||
int r = usbi_backend->get_config_descriptor(dev, i, tmp, sizeof(tmp),
|
||||
int r = usbi_backend.get_config_descriptor(dev, i, tmp, sizeof(tmp),
|
||||
&host_endian);
|
||||
if (r < 0) {
|
||||
*idx = -1;
|
||||
@ -702,8 +702,8 @@ int API_EXPORTED libusb_get_config_descriptor_by_value(libusb_device *dev,
|
||||
int r, idx, host_endian;
|
||||
unsigned char *buf = NULL;
|
||||
|
||||
if (usbi_backend->get_config_descriptor_by_value) {
|
||||
r = usbi_backend->get_config_descriptor_by_value(dev,
|
||||
if (usbi_backend.get_config_descriptor_by_value) {
|
||||
r = usbi_backend.get_config_descriptor_by_value(dev,
|
||||
bConfigurationValue, &buf, &host_endian);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@ -1176,7 +1176,8 @@ int API_EXPORTED libusb_get_string_descriptor_ascii(libusb_device_handle *dev_ha
|
||||
if (tbuf[0] > r)
|
||||
return LIBUSB_ERROR_IO;
|
||||
|
||||
for (di = 0, si = 2; si < tbuf[0]; si += 2) {
|
||||
di = 0;
|
||||
for (si = 2; si < tbuf[0]; si += 2) {
|
||||
if (di >= (length - 1))
|
||||
break;
|
||||
|
@ -158,27 +158,21 @@ static int usbi_hotplug_match_cb (struct libusb_context *ctx,
|
||||
struct libusb_device *dev, libusb_hotplug_event event,
|
||||
struct libusb_hotplug_callback *hotplug_cb)
|
||||
{
|
||||
/* Handle lazy deregistration of callback */
|
||||
if (hotplug_cb->needs_free) {
|
||||
/* Free callback */
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!(hotplug_cb->events & event)) {
|
||||
if (!(hotplug_cb->flags & event)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (LIBUSB_HOTPLUG_MATCH_ANY != hotplug_cb->vendor_id &&
|
||||
if ((hotplug_cb->flags & USBI_HOTPLUG_VENDOR_ID_VALID) &&
|
||||
hotplug_cb->vendor_id != dev->device_descriptor.idVendor) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (LIBUSB_HOTPLUG_MATCH_ANY != hotplug_cb->product_id &&
|
||||
if ((hotplug_cb->flags & USBI_HOTPLUG_PRODUCT_ID_VALID) &&
|
||||
hotplug_cb->product_id != dev->device_descriptor.idProduct) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (LIBUSB_HOTPLUG_MATCH_ANY != hotplug_cb->dev_class &&
|
||||
if ((hotplug_cb->flags & USBI_HOTPLUG_DEV_CLASS_VALID) &&
|
||||
hotplug_cb->dev_class != dev->device_descriptor.bDeviceClass) {
|
||||
return 0;
|
||||
}
|
||||
@ -195,6 +189,11 @@ void usbi_hotplug_match(struct libusb_context *ctx, struct libusb_device *dev,
|
||||
usbi_mutex_lock(&ctx->hotplug_cbs_lock);
|
||||
|
||||
list_for_each_entry_safe(hotplug_cb, next, &ctx->hotplug_cbs, list, struct libusb_hotplug_callback) {
|
||||
if (hotplug_cb->flags & USBI_HOTPLUG_NEEDS_FREE) {
|
||||
/* process deregistration in usbi_hotplug_deregister() */
|
||||
continue;
|
||||
}
|
||||
|
||||
usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
|
||||
ret = usbi_hotplug_match_cb(ctx, dev, event, hotplug_cb);
|
||||
usbi_mutex_lock(&ctx->hotplug_cbs_lock);
|
||||
@ -206,15 +205,13 @@ void usbi_hotplug_match(struct libusb_context *ctx, struct libusb_device *dev,
|
||||
}
|
||||
|
||||
usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
|
||||
|
||||
/* the backend is expected to call the callback for each active transfer */
|
||||
}
|
||||
|
||||
void usbi_hotplug_notification(struct libusb_context *ctx, struct libusb_device *dev,
|
||||
libusb_hotplug_event event)
|
||||
{
|
||||
int pending_events;
|
||||
libusb_hotplug_message *message = calloc(1, sizeof(*message));
|
||||
struct libusb_hotplug_message *message = calloc(1, sizeof(*message));
|
||||
|
||||
if (!message) {
|
||||
usbi_err(ctx, "error allocating hotplug message");
|
||||
@ -240,59 +237,70 @@ int API_EXPORTED libusb_hotplug_register_callback(libusb_context *ctx,
|
||||
libusb_hotplug_callback_fn cb_fn, void *user_data,
|
||||
libusb_hotplug_callback_handle *callback_handle)
|
||||
{
|
||||
libusb_hotplug_callback *new_callback;
|
||||
static int handle_id = 1;
|
||||
|
||||
/* check for hotplug support */
|
||||
if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
|
||||
return LIBUSB_ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
struct libusb_hotplug_callback *new_callback;
|
||||
|
||||
/* check for sane values */
|
||||
if ((LIBUSB_HOTPLUG_MATCH_ANY != vendor_id && (~0xffff & vendor_id)) ||
|
||||
if ((!events || (~(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) & events)) ||
|
||||
(flags && (~LIBUSB_HOTPLUG_ENUMERATE & flags)) ||
|
||||
(LIBUSB_HOTPLUG_MATCH_ANY != vendor_id && (~0xffff & vendor_id)) ||
|
||||
(LIBUSB_HOTPLUG_MATCH_ANY != product_id && (~0xffff & product_id)) ||
|
||||
(LIBUSB_HOTPLUG_MATCH_ANY != dev_class && (~0xff & dev_class)) ||
|
||||
!cb_fn) {
|
||||
return LIBUSB_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
/* check for hotplug support */
|
||||
if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
|
||||
return LIBUSB_ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
USBI_GET_CONTEXT(ctx);
|
||||
|
||||
new_callback = (libusb_hotplug_callback *)calloc(1, sizeof (*new_callback));
|
||||
new_callback = calloc(1, sizeof(*new_callback));
|
||||
if (!new_callback) {
|
||||
return LIBUSB_ERROR_NO_MEM;
|
||||
}
|
||||
|
||||
new_callback->ctx = ctx;
|
||||
new_callback->vendor_id = vendor_id;
|
||||
new_callback->product_id = product_id;
|
||||
new_callback->dev_class = dev_class;
|
||||
new_callback->flags = flags;
|
||||
new_callback->events = events;
|
||||
new_callback->flags = (uint8_t)events;
|
||||
if (LIBUSB_HOTPLUG_MATCH_ANY != vendor_id) {
|
||||
new_callback->flags |= USBI_HOTPLUG_VENDOR_ID_VALID;
|
||||
new_callback->vendor_id = (uint16_t)vendor_id;
|
||||
}
|
||||
if (LIBUSB_HOTPLUG_MATCH_ANY != product_id) {
|
||||
new_callback->flags |= USBI_HOTPLUG_PRODUCT_ID_VALID;
|
||||
new_callback->product_id = (uint16_t)product_id;
|
||||
}
|
||||
if (LIBUSB_HOTPLUG_MATCH_ANY != dev_class) {
|
||||
new_callback->flags |= USBI_HOTPLUG_DEV_CLASS_VALID;
|
||||
new_callback->dev_class = (uint8_t)dev_class;
|
||||
}
|
||||
new_callback->cb = cb_fn;
|
||||
new_callback->user_data = user_data;
|
||||
new_callback->needs_free = 0;
|
||||
|
||||
usbi_mutex_lock(&ctx->hotplug_cbs_lock);
|
||||
|
||||
/* protect the handle by the context hotplug lock. it doesn't matter if the same handle
|
||||
* is used for different contexts only that the handle is unique for this context */
|
||||
new_callback->handle = handle_id++;
|
||||
/* protect the handle by the context hotplug lock */
|
||||
new_callback->handle = ctx->next_hotplug_cb_handle++;
|
||||
|
||||
/* handle the unlikely case of overflow */
|
||||
if (ctx->next_hotplug_cb_handle < 0)
|
||||
ctx->next_hotplug_cb_handle = 1;
|
||||
|
||||
list_add(&new_callback->list, &ctx->hotplug_cbs);
|
||||
|
||||
usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
|
||||
|
||||
usbi_dbg("new hotplug cb %p with handle %d", new_callback, new_callback->handle);
|
||||
|
||||
if (flags & LIBUSB_HOTPLUG_ENUMERATE) {
|
||||
int i, len;
|
||||
if ((flags & LIBUSB_HOTPLUG_ENUMERATE) && (events & LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED)) {
|
||||
ssize_t i, len;
|
||||
struct libusb_device **devs;
|
||||
|
||||
len = (int) libusb_get_device_list(ctx, &devs);
|
||||
len = libusb_get_device_list(ctx, &devs);
|
||||
if (len < 0) {
|
||||
libusb_hotplug_deregister_callback(ctx,
|
||||
new_callback->handle);
|
||||
return len;
|
||||
return (int)len;
|
||||
}
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
@ -315,6 +323,7 @@ void API_EXPORTED libusb_hotplug_deregister_callback (struct libusb_context *ctx
|
||||
libusb_hotplug_callback_handle callback_handle)
|
||||
{
|
||||
struct libusb_hotplug_callback *hotplug_cb;
|
||||
int deregistered = 0;
|
||||
|
||||
/* check for hotplug support */
|
||||
if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
|
||||
@ -323,28 +332,42 @@ void API_EXPORTED libusb_hotplug_deregister_callback (struct libusb_context *ctx
|
||||
|
||||
USBI_GET_CONTEXT(ctx);
|
||||
|
||||
usbi_dbg("deregister hotplug cb %d", callback_handle);
|
||||
|
||||
usbi_mutex_lock(&ctx->hotplug_cbs_lock);
|
||||
list_for_each_entry(hotplug_cb, &ctx->hotplug_cbs, list,
|
||||
struct libusb_hotplug_callback) {
|
||||
list_for_each_entry(hotplug_cb, &ctx->hotplug_cbs, list, struct libusb_hotplug_callback) {
|
||||
if (callback_handle == hotplug_cb->handle) {
|
||||
/* Mark this callback for deregistration */
|
||||
hotplug_cb->needs_free = 1;
|
||||
hotplug_cb->flags |= USBI_HOTPLUG_NEEDS_FREE;
|
||||
deregistered = 1;
|
||||
}
|
||||
}
|
||||
usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
|
||||
|
||||
usbi_hotplug_notification(ctx, NULL, 0);
|
||||
if (deregistered) {
|
||||
int pending_events;
|
||||
|
||||
usbi_mutex_lock(&ctx->event_data_lock);
|
||||
pending_events = usbi_pending_events(ctx);
|
||||
ctx->event_flags |= USBI_EVENT_HOTPLUG_CB_DEREGISTERED;
|
||||
if (!pending_events)
|
||||
usbi_signal_event(ctx);
|
||||
usbi_mutex_unlock(&ctx->event_data_lock);
|
||||
}
|
||||
}
|
||||
|
||||
void usbi_hotplug_deregister_all(struct libusb_context *ctx) {
|
||||
void usbi_hotplug_deregister(struct libusb_context *ctx, int forced)
|
||||
{
|
||||
struct libusb_hotplug_callback *hotplug_cb, *next;
|
||||
|
||||
usbi_mutex_lock(&ctx->hotplug_cbs_lock);
|
||||
list_for_each_entry_safe(hotplug_cb, next, &ctx->hotplug_cbs, list,
|
||||
struct libusb_hotplug_callback) {
|
||||
list_for_each_entry_safe(hotplug_cb, next, &ctx->hotplug_cbs, list, struct libusb_hotplug_callback) {
|
||||
if (forced || (hotplug_cb->flags & USBI_HOTPLUG_NEEDS_FREE)) {
|
||||
usbi_dbg("freeing hotplug cb %p with handle %d", hotplug_cb,
|
||||
hotplug_cb->handle);
|
||||
list_del(&hotplug_cb->list);
|
||||
free(hotplug_cb);
|
||||
}
|
||||
|
||||
}
|
||||
usbi_mutex_unlock(&ctx->hotplug_cbs_lock);
|
||||
}
|
@ -19,12 +19,34 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#if !defined(USBI_HOTPLUG_H)
|
||||
#ifndef USBI_HOTPLUG_H
|
||||
#define USBI_HOTPLUG_H
|
||||
|
||||
#ifndef LIBUSBI_H
|
||||
#include "libusbi.h"
|
||||
#endif
|
||||
|
||||
enum usbi_hotplug_flags {
|
||||
/* This callback is interested in device arrivals */
|
||||
USBI_HOTPLUG_DEVICE_ARRIVED = LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED,
|
||||
|
||||
/* This callback is interested in device removals */
|
||||
USBI_HOTPLUG_DEVICE_LEFT = LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT,
|
||||
|
||||
/* IMPORTANT: The values for the below entries must start *after*
|
||||
* the highest value of the above entries!!!
|
||||
*/
|
||||
|
||||
/* The vendor_id field is valid for matching */
|
||||
USBI_HOTPLUG_VENDOR_ID_VALID = (1 << 3),
|
||||
|
||||
/* The product_id field is valid for matching */
|
||||
USBI_HOTPLUG_PRODUCT_ID_VALID = (1 << 4),
|
||||
|
||||
/* The dev_class field is valid for matching */
|
||||
USBI_HOTPLUG_DEV_CLASS_VALID = (1 << 5),
|
||||
|
||||
/* This callback has been unregistered and needs to be freed */
|
||||
USBI_HOTPLUG_NEEDS_FREE = (1 << 6),
|
||||
};
|
||||
|
||||
/** \ingroup hotplug
|
||||
* The hotplug callback structure. The user populates this structure with
|
||||
@ -32,23 +54,17 @@
|
||||
* to receive notification of hotplug events.
|
||||
*/
|
||||
struct libusb_hotplug_callback {
|
||||
/** Context this callback is associated with */
|
||||
struct libusb_context *ctx;
|
||||
/** Flags that control how this callback behaves */
|
||||
uint8_t flags;
|
||||
|
||||
/** Vendor ID to match or LIBUSB_HOTPLUG_MATCH_ANY */
|
||||
int vendor_id;
|
||||
/** Vendor ID to match (if flags says this is valid) */
|
||||
uint16_t vendor_id;
|
||||
|
||||
/** Product ID to match or LIBUSB_HOTPLUG_MATCH_ANY */
|
||||
int product_id;
|
||||
/** Product ID to match (if flags says this is valid) */
|
||||
uint16_t product_id;
|
||||
|
||||
/** Device class to match or LIBUSB_HOTPLUG_MATCH_ANY */
|
||||
int dev_class;
|
||||
|
||||
/** Hotplug callback flags */
|
||||
libusb_hotplug_flag flags;
|
||||
|
||||
/** Event(s) that will trigger this callback */
|
||||
libusb_hotplug_event events;
|
||||
/** Device class to match (if flags says this is valid) */
|
||||
uint8_t dev_class;
|
||||
|
||||
/** Callback function to invoke for matching event/device */
|
||||
libusb_hotplug_callback_fn cb;
|
||||
@ -59,15 +75,10 @@ struct libusb_hotplug_callback {
|
||||
/** User data that will be passed to the callback function */
|
||||
void *user_data;
|
||||
|
||||
/** Callback is marked for deletion */
|
||||
int needs_free;
|
||||
|
||||
/** List this callback is registered in (ctx->hotplug_cbs) */
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
typedef struct libusb_hotplug_callback libusb_hotplug_callback;
|
||||
|
||||
struct libusb_hotplug_message {
|
||||
/** The hotplug event that occurred */
|
||||
libusb_hotplug_event event;
|
||||
@ -79,9 +90,7 @@ struct libusb_hotplug_message {
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
typedef struct libusb_hotplug_message libusb_hotplug_message;
|
||||
|
||||
void usbi_hotplug_deregister_all(struct libusb_context *ctx);
|
||||
void usbi_hotplug_deregister(struct libusb_context *ctx, int forced);
|
||||
void usbi_hotplug_match(struct libusb_context *ctx, struct libusb_device *dev,
|
||||
libusb_hotplug_event event);
|
||||
void usbi_hotplug_notification(struct libusb_context *ctx, struct libusb_device *dev,
|
@ -1144,8 +1144,8 @@ int usbi_io_init(struct libusb_context *ctx)
|
||||
goto err_close_pipe;
|
||||
|
||||
#ifdef USBI_TIMERFD_AVAILABLE
|
||||
ctx->timerfd = timerfd_create(usbi_backend->get_timerfd_clockid(),
|
||||
TFD_NONBLOCK);
|
||||
ctx->timerfd = timerfd_create(usbi_backend.get_timerfd_clockid(),
|
||||
TFD_NONBLOCK | TFD_CLOEXEC);
|
||||
if (ctx->timerfd >= 0) {
|
||||
usbi_dbg("using timerfd for timeouts");
|
||||
r = usbi_add_pollfd(ctx, ctx->timerfd, POLLIN);
|
||||
@ -1205,10 +1205,12 @@ static int calculate_timeout(struct usbi_transfer *transfer)
|
||||
unsigned int timeout =
|
||||
USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer)->timeout;
|
||||
|
||||
if (!timeout)
|
||||
if (!timeout) {
|
||||
timerclear(&transfer->timeout);
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = usbi_backend->clock_gettime(USBI_CLOCK_MONOTONIC, ¤t_time);
|
||||
r = usbi_backend.clock_gettime(USBI_CLOCK_MONOTONIC, ¤t_time);
|
||||
if (r < 0) {
|
||||
usbi_err(ITRANSFER_CTX(transfer),
|
||||
"failed to read monotonic clock, errno=%d", errno);
|
||||
@ -1255,7 +1257,7 @@ struct libusb_transfer * LIBUSB_CALL libusb_alloc_transfer(
|
||||
int iso_packets)
|
||||
{
|
||||
struct libusb_transfer *transfer;
|
||||
size_t os_alloc_size = usbi_backend->transfer_priv_size;
|
||||
size_t os_alloc_size = usbi_backend.transfer_priv_size;
|
||||
size_t alloc_size = sizeof(struct usbi_transfer)
|
||||
+ sizeof(struct libusb_transfer)
|
||||
+ (sizeof(struct libusb_iso_packet_descriptor) * iso_packets)
|
||||
@ -1526,7 +1528,7 @@ int API_EXPORTED libusb_submit_transfer(struct libusb_transfer *transfer)
|
||||
*/
|
||||
usbi_mutex_unlock(&ctx->flying_transfers_lock);
|
||||
|
||||
r = usbi_backend->submit_transfer(itransfer);
|
||||
r = usbi_backend.submit_transfer(itransfer);
|
||||
if (r == LIBUSB_SUCCESS) {
|
||||
itransfer->state_flags |= USBI_TRANSFER_IN_FLIGHT;
|
||||
/* keep a reference to this device */
|
||||
@ -1567,7 +1569,7 @@ int API_EXPORTED libusb_cancel_transfer(struct libusb_transfer *transfer)
|
||||
r = LIBUSB_ERROR_NOT_FOUND;
|
||||
goto out;
|
||||
}
|
||||
r = usbi_backend->cancel_transfer(itransfer);
|
||||
r = usbi_backend.cancel_transfer(itransfer);
|
||||
if (r < 0) {
|
||||
if (r != LIBUSB_ERROR_NOT_FOUND &&
|
||||
r != LIBUSB_ERROR_NO_DEVICE)
|
||||
@ -2004,7 +2006,7 @@ static int handle_timeouts_locked(struct libusb_context *ctx)
|
||||
return 0;
|
||||
|
||||
/* get current time */
|
||||
r = usbi_backend->clock_gettime(USBI_CLOCK_MONOTONIC, &systime_ts);
|
||||
r = usbi_backend.clock_gettime(USBI_CLOCK_MONOTONIC, &systime_ts);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -2077,7 +2079,6 @@ static int handle_events(struct libusb_context *ctx, struct timeval *tv)
|
||||
struct pollfd *fds = NULL;
|
||||
int i = -1;
|
||||
int timeout_ms;
|
||||
int special_event;
|
||||
|
||||
/* prevent attempts to recursively handle events (e.g. calling into
|
||||
* libusb_handle_events() from within a hotplug or transfer callback) */
|
||||
@ -2146,32 +2147,30 @@ static int handle_events(struct libusb_context *ctx, struct timeval *tv)
|
||||
if (tv->tv_usec % 1000)
|
||||
timeout_ms++;
|
||||
|
||||
redo_poll:
|
||||
usbi_dbg("poll() %d fds with timeout in %dms", nfds, timeout_ms);
|
||||
r = usbi_poll(fds, nfds, timeout_ms);
|
||||
usbi_dbg("poll() returned %d", r);
|
||||
if (r == 0) {
|
||||
r = handle_timeouts(ctx);
|
||||
goto done;
|
||||
}
|
||||
else if (r == -1 && errno == EINTR) {
|
||||
} else if (r == -1 && errno == EINTR) {
|
||||
r = LIBUSB_ERROR_INTERRUPTED;
|
||||
goto done;
|
||||
}
|
||||
else if (r < 0) {
|
||||
} else if (r < 0) {
|
||||
usbi_err(ctx, "poll failed %d err=%d", r, errno);
|
||||
r = LIBUSB_ERROR_IO;
|
||||
goto done;
|
||||
}
|
||||
|
||||
special_event = 0;
|
||||
|
||||
/* fds[0] is always the event pipe */
|
||||
if (fds[0].revents) {
|
||||
libusb_hotplug_message *message = NULL;
|
||||
struct list_head hotplug_msgs;
|
||||
struct usbi_transfer *itransfer;
|
||||
int hotplug_cb_deregistered = 0;
|
||||
int ret = 0;
|
||||
|
||||
list_init(&hotplug_msgs);
|
||||
|
||||
usbi_dbg("caught a fish on the event pipe");
|
||||
|
||||
/* take the the event data lock while processing events */
|
||||
@ -2186,6 +2185,12 @@ redo_poll:
|
||||
ctx->event_flags &= ~USBI_EVENT_USER_INTERRUPT;
|
||||
}
|
||||
|
||||
if (ctx->event_flags & USBI_EVENT_HOTPLUG_CB_DEREGISTERED) {
|
||||
usbi_dbg("someone unregistered a hotplug cb");
|
||||
ctx->event_flags &= ~USBI_EVENT_HOTPLUG_CB_DEREGISTERED;
|
||||
hotplug_cb_deregistered = 1;
|
||||
}
|
||||
|
||||
/* check if someone is closing a device */
|
||||
if (ctx->device_close)
|
||||
usbi_dbg("someone is closing a device");
|
||||
@ -2193,9 +2198,7 @@ redo_poll:
|
||||
/* check for any pending hotplug messages */
|
||||
if (!list_empty(&ctx->hotplug_msgs)) {
|
||||
usbi_dbg("hotplug message received");
|
||||
special_event = 1;
|
||||
message = list_first_entry(&ctx->hotplug_msgs, libusb_hotplug_message, list);
|
||||
list_del(&message->list);
|
||||
list_cut(&hotplug_msgs, &ctx->hotplug_msgs);
|
||||
}
|
||||
|
||||
/* complete any pending transfers */
|
||||
@ -2203,7 +2206,7 @@ redo_poll:
|
||||
itransfer = list_first_entry(&ctx->completed_transfers, struct usbi_transfer, completed_list);
|
||||
list_del(&itransfer->completed_list);
|
||||
usbi_mutex_unlock(&ctx->event_data_lock);
|
||||
ret = usbi_backend->handle_transfer_completion(itransfer);
|
||||
ret = usbi_backend.handle_transfer_completion(itransfer);
|
||||
if (ret)
|
||||
usbi_err(ctx, "backend handle_transfer_completion failed with error %d", ret);
|
||||
usbi_mutex_lock(&ctx->event_data_lock);
|
||||
@ -2215,14 +2218,21 @@ redo_poll:
|
||||
|
||||
usbi_mutex_unlock(&ctx->event_data_lock);
|
||||
|
||||
/* process the hotplug message, if any */
|
||||
if (message) {
|
||||
if (hotplug_cb_deregistered)
|
||||
usbi_hotplug_deregister(ctx, 0);
|
||||
|
||||
/* process the hotplug messages, if any */
|
||||
while (!list_empty(&hotplug_msgs)) {
|
||||
struct libusb_hotplug_message *message =
|
||||
list_first_entry(&hotplug_msgs, struct libusb_hotplug_message, list);
|
||||
|
||||
usbi_hotplug_match(ctx, message->device, message->event);
|
||||
|
||||
/* the device left, dereference the device */
|
||||
if (LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT == message->event)
|
||||
libusb_unref_device(message->device);
|
||||
|
||||
list_del(&message->list);
|
||||
free(message);
|
||||
}
|
||||
|
||||
@ -2233,7 +2243,7 @@ redo_poll:
|
||||
}
|
||||
|
||||
if (0 == --r)
|
||||
goto handled;
|
||||
goto done;
|
||||
}
|
||||
|
||||
#ifdef USBI_TIMERFD_AVAILABLE
|
||||
@ -2242,7 +2252,6 @@ redo_poll:
|
||||
/* timerfd indicates that a timeout has expired */
|
||||
int ret;
|
||||
usbi_dbg("timerfd triggered");
|
||||
special_event = 1;
|
||||
|
||||
ret = handle_timerfd_trigger(ctx);
|
||||
if (ret < 0) {
|
||||
@ -2252,20 +2261,14 @@ redo_poll:
|
||||
}
|
||||
|
||||
if (0 == --r)
|
||||
goto handled;
|
||||
goto done;
|
||||
}
|
||||
#endif
|
||||
|
||||
r = usbi_backend->handle_events(ctx, fds + internal_nfds, nfds - internal_nfds, r);
|
||||
r = usbi_backend.handle_events(ctx, fds + internal_nfds, nfds - internal_nfds, r);
|
||||
if (r)
|
||||
usbi_err(ctx, "backend handle_events failed with error %d", r);
|
||||
|
||||
handled:
|
||||
if (r == 0 && special_event) {
|
||||
timeout_ms = 0;
|
||||
goto redo_poll;
|
||||
}
|
||||
|
||||
done:
|
||||
usbi_end_event_handling(ctx);
|
||||
return r;
|
||||
@ -2583,7 +2586,7 @@ int API_EXPORTED libusb_get_next_timeout(libusb_context *ctx,
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = usbi_backend->clock_gettime(USBI_CLOCK_MONOTONIC, &cur_ts);
|
||||
r = usbi_backend.clock_gettime(USBI_CLOCK_MONOTONIC, &cur_ts);
|
||||
if (r < 0) {
|
||||
usbi_err(ctx, "failed to read monotonic clock, errno=%d", errno);
|
||||
return 0;
|
||||
@ -2811,7 +2814,7 @@ void usbi_handle_disconnect(struct libusb_device_handle *dev_handle)
|
||||
USBI_TRANSFER_TO_LIBUSB_TRANSFER(to_cancel));
|
||||
|
||||
usbi_mutex_lock(&to_cancel->lock);
|
||||
usbi_backend->clear_transfer_priv(to_cancel);
|
||||
usbi_backend.clear_transfer_priv(to_cancel);
|
||||
usbi_mutex_unlock(&to_cancel->lock);
|
||||
usbi_handle_transfer_completion(to_cancel, LIBUSB_TRANSFER_NO_DEVICE);
|
||||
}
|
@ -54,13 +54,19 @@ typedef unsigned __int32 uint32_t;
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
|
||||
#if defined(__linux) || defined(__APPLE__) || defined(__CYGWIN__) || defined(__HAIKU__)
|
||||
#if defined(__linux__) || defined(__APPLE__) || defined(__CYGWIN__) || defined(__HAIKU__)
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#include <time.h>
|
||||
#include <limits.h>
|
||||
|
||||
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
|
||||
#define ZERO_SIZED_ARRAY /* [] - valid C99 code */
|
||||
#else
|
||||
#define ZERO_SIZED_ARRAY 0 /* [0] - non-standard, but usually working code */
|
||||
#endif
|
||||
|
||||
/* 'interface' might be defined as a macro on Windows, so we need to
|
||||
* undefine it so as not to break the current libusb API, because
|
||||
* libusb_config_descriptor has an 'interface' member
|
||||
@ -79,6 +85,8 @@ typedef unsigned __int32 uint32_t;
|
||||
#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
|
||||
#define LIBUSB_DEPRECATED_FOR(f) \
|
||||
__attribute__((deprecated("Use " #f " instead")))
|
||||
#elif __GNUC__ >= 3
|
||||
#define LIBUSB_DEPRECATED_FOR(f) __attribute__((deprecated))
|
||||
#else
|
||||
#define LIBUSB_DEPRECATED_FOR(f)
|
||||
#endif /* __GNUC__ */
|
||||
@ -141,7 +149,7 @@ typedef unsigned __int32 uint32_t;
|
||||
* Internally, LIBUSB_API_VERSION is defined as follows:
|
||||
* (libusb major << 24) | (libusb minor << 16) | (16 bit incremental)
|
||||
*/
|
||||
#define LIBUSB_API_VERSION 0x01000105
|
||||
#define LIBUSB_API_VERSION 0x01000106
|
||||
|
||||
/* The following is kept for compatibility, but will be deprecated in the future */
|
||||
#define LIBUSBX_API_VERSION LIBUSB_API_VERSION
|
||||
@ -729,13 +737,7 @@ struct libusb_bos_dev_capability_descriptor {
|
||||
/** Device Capability type */
|
||||
uint8_t bDevCapabilityType;
|
||||
/** Device Capability data (bLength - 3 bytes) */
|
||||
uint8_t dev_capability_data
|
||||
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
|
||||
[] /* valid C99 code */
|
||||
#else
|
||||
[0] /* non-standard, but usually working code */
|
||||
#endif
|
||||
;
|
||||
uint8_t dev_capability_data[ZERO_SIZED_ARRAY];
|
||||
};
|
||||
|
||||
/** \ingroup libusb_desc
|
||||
@ -760,13 +762,7 @@ struct libusb_bos_descriptor {
|
||||
uint8_t bNumDeviceCaps;
|
||||
|
||||
/** bNumDeviceCap Device Capability Descriptors */
|
||||
struct libusb_bos_dev_capability_descriptor *dev_capability
|
||||
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
|
||||
[] /* valid C99 code */
|
||||
#else
|
||||
[0] /* non-standard, but usually working code */
|
||||
#endif
|
||||
;
|
||||
struct libusb_bos_dev_capability_descriptor *dev_capability[ZERO_SIZED_ARRAY];
|
||||
};
|
||||
|
||||
/** \ingroup libusb_desc
|
||||
@ -927,7 +923,7 @@ struct libusb_version {
|
||||
* sessions allows for your program to use two libraries (or dynamically
|
||||
* load two modules) which both independently use libusb. This will prevent
|
||||
* interference between the individual libusb users - for example
|
||||
* libusb_set_debug() will not affect the other user of the library, and
|
||||
* libusb_set_option() will not affect the other user of the library, and
|
||||
* libusb_exit() will not destroy resources that the other user is still
|
||||
* using.
|
||||
*
|
||||
@ -987,6 +983,9 @@ enum libusb_speed {
|
||||
|
||||
/** The device is operating at super speed (5000MBit/s). */
|
||||
LIBUSB_SPEED_SUPER = 4,
|
||||
|
||||
/** The device is operating at super speed plus (10000MBit/s). */
|
||||
LIBUSB_SPEED_SUPER_PLUS = 5,
|
||||
};
|
||||
|
||||
/** \ingroup libusb_dev
|
||||
@ -1256,13 +1255,7 @@ struct libusb_transfer {
|
||||
int num_iso_packets;
|
||||
|
||||
/** Isochronous packet descriptors, for isochronous transfers only. */
|
||||
struct libusb_iso_packet_descriptor iso_packet_desc
|
||||
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
|
||||
[] /* valid C99 code */
|
||||
#else
|
||||
[0] /* non-standard, but usually working code */
|
||||
#endif
|
||||
;
|
||||
struct libusb_iso_packet_descriptor iso_packet_desc[ZERO_SIZED_ARRAY];
|
||||
};
|
||||
|
||||
/** \ingroup libusb_misc
|
||||
@ -1290,21 +1283,20 @@ enum libusb_capability {
|
||||
* - LIBUSB_LOG_LEVEL_NONE (0) : no messages ever printed by the library (default)
|
||||
* - LIBUSB_LOG_LEVEL_ERROR (1) : error messages are printed to stderr
|
||||
* - LIBUSB_LOG_LEVEL_WARNING (2) : warning and error messages are printed to stderr
|
||||
* - LIBUSB_LOG_LEVEL_INFO (3) : informational messages are printed to stdout, warning
|
||||
* and error messages are printed to stderr
|
||||
* - LIBUSB_LOG_LEVEL_DEBUG (4) : debug and informational messages are printed to stdout,
|
||||
* warnings and errors to stderr
|
||||
* - LIBUSB_LOG_LEVEL_INFO (3) : informational messages are printed to stderr
|
||||
* - LIBUSB_LOG_LEVEL_DEBUG (4) : debug and informational messages are printed to stderr
|
||||
*/
|
||||
enum libusb_log_level {
|
||||
LIBUSB_LOG_LEVEL_NONE = 0,
|
||||
LIBUSB_LOG_LEVEL_ERROR,
|
||||
LIBUSB_LOG_LEVEL_WARNING,
|
||||
LIBUSB_LOG_LEVEL_INFO,
|
||||
LIBUSB_LOG_LEVEL_DEBUG,
|
||||
LIBUSB_LOG_LEVEL_ERROR = 1,
|
||||
LIBUSB_LOG_LEVEL_WARNING = 2,
|
||||
LIBUSB_LOG_LEVEL_INFO = 3,
|
||||
LIBUSB_LOG_LEVEL_DEBUG = 4,
|
||||
};
|
||||
|
||||
int LIBUSB_CALL libusb_init(libusb_context **ctx);
|
||||
void LIBUSB_CALL libusb_exit(libusb_context *ctx);
|
||||
LIBUSB_DEPRECATED_FOR(libusb_set_option)
|
||||
void LIBUSB_CALL libusb_set_debug(libusb_context *ctx, int level);
|
||||
const struct libusb_version * LIBUSB_CALL libusb_get_version(void);
|
||||
int LIBUSB_CALL libusb_has_capability(uint32_t capability);
|
||||
@ -2001,6 +1993,45 @@ int LIBUSB_CALL libusb_hotplug_register_callback(libusb_context *ctx,
|
||||
void LIBUSB_CALL libusb_hotplug_deregister_callback(libusb_context *ctx,
|
||||
libusb_hotplug_callback_handle callback_handle);
|
||||
|
||||
/** \ingroup libusb_lib
|
||||
* Available option values for libusb_set_option().
|
||||
*/
|
||||
enum libusb_option {
|
||||
/** Set the log message verbosity.
|
||||
*
|
||||
* The default level is LIBUSB_LOG_LEVEL_NONE, which means no messages are ever
|
||||
* printed. If you choose to increase the message verbosity level, ensure
|
||||
* that your application does not close the stderr file descriptor.
|
||||
*
|
||||
* You are advised to use level LIBUSB_LOG_LEVEL_WARNING. libusb is conservative
|
||||
* with its message logging and most of the time, will only log messages that
|
||||
* explain error conditions and other oddities. This will help you debug
|
||||
* your software.
|
||||
*
|
||||
* If the LIBUSB_DEBUG environment variable was set when libusb was
|
||||
* initialized, this function does nothing: the message verbosity is fixed
|
||||
* to the value in the environment variable.
|
||||
*
|
||||
* If libusb was compiled without any message logging, this function does
|
||||
* nothing: you'll never get any messages.
|
||||
*
|
||||
* If libusb was compiled with verbose debug message logging, this function
|
||||
* does nothing: you'll always get messages from all levels.
|
||||
*/
|
||||
LIBUSB_OPTION_LOG_LEVEL,
|
||||
|
||||
/** Use the UsbDk backend for a specific context, if available.
|
||||
*
|
||||
* This option should be set immediately after calling libusb_init(), otherwise
|
||||
* unspecified behavior may occur.
|
||||
*
|
||||
* Only valid on Windows.
|
||||
*/
|
||||
LIBUSB_OPTION_USE_USBDK,
|
||||
};
|
||||
|
||||
int LIBUSB_CALL libusb_set_option(libusb_context *ctx, enum libusb_option option, ...);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -39,6 +39,20 @@
|
||||
#include "libusb.h"
|
||||
#include "version.h"
|
||||
|
||||
/* Attribute to ensure that a structure member is aligned to a natural
|
||||
* pointer alignment. Used for os_priv member. */
|
||||
#if defined(_MSC_VER)
|
||||
#if defined(_WIN64)
|
||||
#define PTR_ALIGNED __declspec(align(8))
|
||||
#else
|
||||
#define PTR_ALIGNED __declspec(align(4))
|
||||
#endif
|
||||
#elif defined(__GNUC__)
|
||||
#define PTR_ALIGNED __attribute__((aligned(sizeof(void *))))
|
||||
#else
|
||||
#define PTR_ALIGNED
|
||||
#endif
|
||||
|
||||
/* Inside the libusb code, mark all public functions as follows:
|
||||
* return_type API_EXPORTED function_name(params) { ... }
|
||||
* But if the function returns a pointer, mark it as follows:
|
||||
@ -139,6 +153,19 @@ static inline void list_del(struct list_head *entry)
|
||||
entry->next = entry->prev = NULL;
|
||||
}
|
||||
|
||||
static inline void list_cut(struct list_head *list, struct list_head *head)
|
||||
{
|
||||
if (list_empty(head))
|
||||
return;
|
||||
|
||||
list->next = head->next;
|
||||
list->next->prev = list;
|
||||
list->prev = head->prev;
|
||||
list->prev->next = list;
|
||||
|
||||
list_init(head);
|
||||
}
|
||||
|
||||
static inline void *usbi_reallocf(void *ptr, size_t size)
|
||||
{
|
||||
void *ret = realloc(ptr, size);
|
||||
@ -151,6 +178,9 @@ static inline void *usbi_reallocf(void *ptr, size_t size)
|
||||
const typeof( ((type *)0)->member ) *mptr = (ptr); \
|
||||
(type *)( (char *)mptr - offsetof(type,member) );})
|
||||
|
||||
#ifndef CLAMP
|
||||
#define CLAMP(val, min, max) ((val) < (min) ? (min) : ((val) > (max) ? (max) : (val)))
|
||||
#endif
|
||||
#ifndef MIN
|
||||
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||
#endif
|
||||
@ -175,29 +205,33 @@ static inline void *usbi_reallocf(void *ptr, size_t size)
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_LOGGING
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER < 1900)
|
||||
#define snprintf usbi_snprintf
|
||||
#define vsnprintf usbi_vsnprintf
|
||||
int usbi_snprintf(char *dst, size_t size, const char *format, ...);
|
||||
int usbi_vsnprintf(char *dst, size_t size, const char *format, va_list ap);
|
||||
#define LIBUSB_PRINTF_WIN32
|
||||
#endif /* defined(_MSC_VER) && (_MSC_VER < 1900) */
|
||||
|
||||
void usbi_log(struct libusb_context *ctx, enum libusb_log_level level,
|
||||
const char *function, const char *format, ...);
|
||||
|
||||
void usbi_log_v(struct libusb_context *ctx, enum libusb_log_level level,
|
||||
const char *function, const char *format, va_list args);
|
||||
|
||||
#if !defined(_MSC_VER) || _MSC_VER >= 1400
|
||||
#if !defined(_MSC_VER) || (_MSC_VER >= 1400)
|
||||
|
||||
#ifdef ENABLE_LOGGING
|
||||
#define _usbi_log(ctx, level, ...) usbi_log(ctx, level, __FUNCTION__, __VA_ARGS__)
|
||||
#define usbi_dbg(...) _usbi_log(NULL, LIBUSB_LOG_LEVEL_DEBUG, __VA_ARGS__)
|
||||
#else
|
||||
#define _usbi_log(ctx, level, ...) do { (void)(ctx); } while(0)
|
||||
#define usbi_dbg(...) do {} while(0)
|
||||
#endif
|
||||
|
||||
#define usbi_info(ctx, ...) _usbi_log(ctx, LIBUSB_LOG_LEVEL_INFO, __VA_ARGS__)
|
||||
#define usbi_warn(ctx, ...) _usbi_log(ctx, LIBUSB_LOG_LEVEL_WARNING, __VA_ARGS__)
|
||||
#define usbi_err(ctx, ...) _usbi_log(ctx, LIBUSB_LOG_LEVEL_ERROR, __VA_ARGS__)
|
||||
#define usbi_warn(ctx, ...) _usbi_log(ctx, LIBUSB_LOG_LEVEL_WARNING, __VA_ARGS__)
|
||||
#define usbi_info(ctx, ...) _usbi_log(ctx, LIBUSB_LOG_LEVEL_INFO, __VA_ARGS__)
|
||||
#define usbi_dbg(...) _usbi_log(NULL, LIBUSB_LOG_LEVEL_DEBUG, __VA_ARGS__)
|
||||
|
||||
#else /* !defined(_MSC_VER) || _MSC_VER >= 1400 */
|
||||
#else /* !defined(_MSC_VER) || (_MSC_VER >= 1400) */
|
||||
|
||||
#ifdef ENABLE_LOGGING
|
||||
#define LOG_BODY(ctxt, level) \
|
||||
{ \
|
||||
va_list args; \
|
||||
@ -205,24 +239,26 @@ void usbi_log_v(struct libusb_context *ctx, enum libusb_log_level level,
|
||||
usbi_log_v(ctxt, level, "", format, args); \
|
||||
va_end(args); \
|
||||
}
|
||||
#else
|
||||
#define LOG_BODY(ctxt, level) \
|
||||
{ \
|
||||
(void)(ctxt); \
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline void usbi_info(struct libusb_context *ctx, const char *format, ...)
|
||||
LOG_BODY(ctx, LIBUSB_LOG_LEVEL_INFO)
|
||||
static inline void usbi_warn(struct libusb_context *ctx, const char *format, ...)
|
||||
LOG_BODY(ctx, LIBUSB_LOG_LEVEL_WARNING)
|
||||
static inline void usbi_err(struct libusb_context *ctx, const char *format, ...)
|
||||
LOG_BODY(ctx, LIBUSB_LOG_LEVEL_ERROR)
|
||||
|
||||
static inline void usbi_warn(struct libusb_context *ctx, const char *format, ...)
|
||||
LOG_BODY(ctx, LIBUSB_LOG_LEVEL_WARNING)
|
||||
static inline void usbi_info(struct libusb_context *ctx, const char *format, ...)
|
||||
LOG_BODY(ctx, LIBUSB_LOG_LEVEL_INFO)
|
||||
static inline void usbi_dbg(const char *format, ...)
|
||||
LOG_BODY(NULL, LIBUSB_LOG_LEVEL_DEBUG)
|
||||
|
||||
#endif /* !defined(_MSC_VER) || _MSC_VER >= 1400 */
|
||||
#endif /* !defined(_MSC_VER) || (_MSC_VER >= 1400) */
|
||||
|
||||
#else /* ENABLE_LOGGING */
|
||||
|
||||
#define usbi_err(ctx, ...) do { (void)ctx; } while (0)
|
||||
#define usbi_warn(ctx, ...) do { (void)ctx; } while (0)
|
||||
#define usbi_info(ctx, ...) do { (void)ctx; } while (0)
|
||||
#define usbi_dbg(...) do {} while (0)
|
||||
|
||||
#endif /* ENABLE_LOGGING */
|
||||
|
||||
#define USBI_GET_CONTEXT(ctx) \
|
||||
do { \
|
||||
@ -254,8 +290,10 @@ extern struct libusb_context *usbi_default_context;
|
||||
struct pollfd;
|
||||
|
||||
struct libusb_context {
|
||||
int debug;
|
||||
#if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING)
|
||||
enum libusb_log_level debug;
|
||||
int debug_fixed;
|
||||
#endif
|
||||
|
||||
/* internal event pipe, used for signalling occurrence of an internal event. */
|
||||
int event_pipe[2];
|
||||
@ -270,6 +308,7 @@ struct libusb_context {
|
||||
|
||||
/* A list of registered hotplug callbacks */
|
||||
struct list_head hotplug_cbs;
|
||||
libusb_hotplug_callback_handle next_hotplug_cb_handle;
|
||||
usbi_mutex_t hotplug_cbs_lock;
|
||||
|
||||
/* this is a list of in-flight transfer handles, sorted by timeout
|
||||
@ -331,6 +370,8 @@ struct libusb_context {
|
||||
#endif
|
||||
|
||||
struct list_head list;
|
||||
|
||||
PTR_ALIGNED unsigned char os_priv[ZERO_SIZED_ARRAY];
|
||||
};
|
||||
|
||||
enum usbi_event_flags {
|
||||
@ -339,6 +380,9 @@ enum usbi_event_flags {
|
||||
|
||||
/* The user has interrupted the event handler */
|
||||
USBI_EVENT_USER_INTERRUPT = 1 << 1,
|
||||
|
||||
/* A hotplug callback deregistration is pending */
|
||||
USBI_EVENT_HOTPLUG_CB_DEREGISTERED = 1 << 2,
|
||||
};
|
||||
|
||||
/* Macros for managing event handling state */
|
||||
@ -383,17 +427,7 @@ struct libusb_device {
|
||||
struct libusb_device_descriptor device_descriptor;
|
||||
int attached;
|
||||
|
||||
unsigned char os_priv
|
||||
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
|
||||
[] /* valid C99 code */
|
||||
#else
|
||||
[0] /* non-standard, but usually working code */
|
||||
#endif
|
||||
#if defined(OS_SUNOS)
|
||||
__attribute__ ((aligned (8)));
|
||||
#else
|
||||
;
|
||||
#endif
|
||||
PTR_ALIGNED unsigned char os_priv[ZERO_SIZED_ARRAY];
|
||||
};
|
||||
|
||||
struct libusb_device_handle {
|
||||
@ -404,17 +438,8 @@ struct libusb_device_handle {
|
||||
struct list_head list;
|
||||
struct libusb_device *dev;
|
||||
int auto_detach_kernel_driver;
|
||||
unsigned char os_priv
|
||||
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
|
||||
[] /* valid C99 code */
|
||||
#else
|
||||
[0] /* non-standard, but usually working code */
|
||||
#endif
|
||||
#if defined(OS_SUNOS)
|
||||
__attribute__ ((aligned (8)));
|
||||
#else
|
||||
;
|
||||
#endif
|
||||
|
||||
PTR_ALIGNED unsigned char os_priv[ZERO_SIZED_ARRAY];
|
||||
};
|
||||
|
||||
enum {
|
||||
@ -540,14 +565,6 @@ int usbi_clear_event(struct libusb_context *ctx);
|
||||
#include "os/poll_windows.h"
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER < 1900)
|
||||
#define snprintf usbi_snprintf
|
||||
#define vsnprintf usbi_vsnprintf
|
||||
int usbi_snprintf(char *dst, size_t size, const char *format, ...);
|
||||
int usbi_vsnprintf(char *dst, size_t size, const char *format, va_list ap);
|
||||
#define LIBUSB_PRINTF_WIN32
|
||||
#endif
|
||||
|
||||
struct usbi_pollfd {
|
||||
/* must come first */
|
||||
struct libusb_pollfd pollfd;
|
||||
@ -568,13 +585,7 @@ void usbi_remove_pollfd(struct libusb_context *ctx, int fd);
|
||||
struct discovered_devs {
|
||||
size_t len;
|
||||
size_t capacity;
|
||||
struct libusb_device *devices
|
||||
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
|
||||
[] /* valid C99 code */
|
||||
#else
|
||||
[0] /* non-standard, but usually working code */
|
||||
#endif
|
||||
;
|
||||
struct libusb_device *devices[ZERO_SIZED_ARRAY];
|
||||
};
|
||||
|
||||
struct discovered_devs *discovered_devs_append(
|
||||
@ -607,7 +618,17 @@ struct usbi_os_backend {
|
||||
*
|
||||
* This function is called when the user deinitializes the library.
|
||||
*/
|
||||
void (*exit)(void);
|
||||
void (*exit)(struct libusb_context *ctx);
|
||||
|
||||
/* Set a backend-specific option. Optional.
|
||||
*
|
||||
* This function is called when the user calls libusb_set_option() and
|
||||
* the option is not handled by the core library.
|
||||
*
|
||||
* Return 0 on success, or a LIBUSB_ERROR code on failure.
|
||||
*/
|
||||
int (*set_option)(struct libusb_context *ctx, enum libusb_option option,
|
||||
va_list args);
|
||||
|
||||
/* Enumerate all the USB devices on the system, returning them in a list
|
||||
* of discovered devices.
|
||||
@ -1110,6 +1131,11 @@ struct usbi_os_backend {
|
||||
clockid_t (*get_timerfd_clockid)(void);
|
||||
#endif
|
||||
|
||||
/* Number of bytes to reserve for per-context private backend data.
|
||||
* This private data area is accessible through the "os_priv" field of
|
||||
* struct libusb_context. */
|
||||
size_t context_priv_size;
|
||||
|
||||
/* Number of bytes to reserve for per-device private backend data.
|
||||
* This private data area is accessible through the "os_priv" field of
|
||||
* struct libusb_device. */
|
||||
@ -1127,17 +1153,7 @@ struct usbi_os_backend {
|
||||
size_t transfer_priv_size;
|
||||
};
|
||||
|
||||
extern const struct usbi_os_backend * const usbi_backend;
|
||||
|
||||
extern const struct usbi_os_backend linux_usbfs_backend;
|
||||
extern const struct usbi_os_backend darwin_backend;
|
||||
extern const struct usbi_os_backend openbsd_backend;
|
||||
extern const struct usbi_os_backend netbsd_backend;
|
||||
extern const struct usbi_os_backend windows_backend;
|
||||
extern const struct usbi_os_backend usbdk_backend;
|
||||
extern const struct usbi_os_backend wince_backend;
|
||||
extern const struct usbi_os_backend haiku_usb_raw_backend;
|
||||
extern const struct usbi_os_backend sunos_backend;
|
||||
extern const struct usbi_os_backend usbi_backend;
|
||||
|
||||
extern struct list_head active_contexts_list;
|
||||
extern usbi_mutex_static_t active_contexts_lock;
|
@ -1,7 +1,7 @@
|
||||
/* -*- Mode: C; indent-tabs-mode:nil -*- */
|
||||
/*
|
||||
* darwin backend for libusb 1.0
|
||||
* Copyright © 2008-2016 Nathan Hjelm <hjelmn@users.sourceforge.net>
|
||||
* Copyright © 2008-2017 Nathan Hjelm <hjelmn@users.sourceforge.net>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
@ -36,6 +36,10 @@
|
||||
#include <mach/mach_host.h>
|
||||
#include <mach/mach_port.h>
|
||||
|
||||
/* Suppress warnings about the use of the deprecated objc_registerThreadWithCollector
|
||||
* function. Its use is also conditionalized to only older deployment targets. */
|
||||
#define OBJC_SILENCE_GC_DEPRECATIONS 1
|
||||
|
||||
#include <AvailabilityMacros.h>
|
||||
#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 && MAC_OS_X_VERSION_MIN_REQUIRED < 101200
|
||||
#include <objc/objc-auto.h>
|
||||
@ -55,6 +59,14 @@ _Atomic int32_t initCount = ATOMIC_VAR_INIT(0);
|
||||
#define libusb_darwin_atomic_fetch_add(x, y) (OSAtomicAdd32Barrier(y, x) - y)
|
||||
|
||||
static volatile int32_t initCount = 0;
|
||||
|
||||
#endif
|
||||
|
||||
/* On 10.12 and later, use newly available clock_*() functions */
|
||||
#if MAC_OS_X_VERSION_MIN_REQUIRED >= 101200
|
||||
#define OSX_USE_CLOCK_GETTIME 1
|
||||
#else
|
||||
#define OSX_USE_CLOCK_GETTIME 0
|
||||
#endif
|
||||
|
||||
#include "darwin_usb.h"
|
||||
@ -65,15 +77,17 @@ static pthread_cond_t libusb_darwin_at_cond = PTHREAD_COND_INITIALIZER;
|
||||
|
||||
static pthread_once_t darwin_init_once = PTHREAD_ONCE_INIT;
|
||||
|
||||
#if !OSX_USE_CLOCK_GETTIME
|
||||
static clock_serv_t clock_realtime;
|
||||
static clock_serv_t clock_monotonic;
|
||||
#endif
|
||||
|
||||
static CFRunLoopRef libusb_darwin_acfl = NULL; /* event cf loop */
|
||||
static CFRunLoopSourceRef libusb_darwin_acfls = NULL; /* shutdown signal for event cf loop */
|
||||
|
||||
static usbi_mutex_t darwin_cached_devices_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
static struct list_head darwin_cached_devices = {&darwin_cached_devices, &darwin_cached_devices};
|
||||
static char *darwin_device_class = kIOUSBDeviceClassName;
|
||||
static const char *darwin_device_class = kIOUSBDeviceClassName;
|
||||
|
||||
#define DARWIN_CACHED_DEVICE(a) ((struct darwin_cached_device *) (((struct darwin_device_priv *)((a)->os_priv))->dev))
|
||||
|
||||
@ -219,20 +233,21 @@ static int usb_setup_device_iterator (io_iterator_t *deviceIterator, UInt32 loca
|
||||
&kCFTypeDictionaryKeyCallBacks,
|
||||
&kCFTypeDictionaryValueCallBacks);
|
||||
|
||||
if (propertyMatchDict) {
|
||||
/* there are no unsigned CFNumber types so treat the value as signed. the os seems to do this
|
||||
internally (CFNumberType of locationID is 3) */
|
||||
/* there are no unsigned CFNumber types so treat the value as signed. the OS seems to do this
|
||||
internally (CFNumberType of locationID is kCFNumberSInt32Type) */
|
||||
CFTypeRef locationCF = CFNumberCreate (NULL, kCFNumberSInt32Type, &location);
|
||||
|
||||
if (propertyMatchDict && locationCF) {
|
||||
CFDictionarySetValue (propertyMatchDict, CFSTR(kUSBDevicePropertyLocationID), locationCF);
|
||||
/* release our reference to the CFNumber (CFDictionarySetValue retains it) */
|
||||
CFRelease (locationCF);
|
||||
|
||||
CFDictionarySetValue (matchingDict, CFSTR(kIOPropertyMatchKey), propertyMatchDict);
|
||||
/* release out reference to the CFMutableDictionaryRef (CFDictionarySetValue retains it) */
|
||||
CFRelease (propertyMatchDict);
|
||||
}
|
||||
/* else we can still proceed as long as the caller accounts for the possibility of other devices in the iterator */
|
||||
|
||||
/* release our references as per the Create Rule */
|
||||
if (propertyMatchDict)
|
||||
CFRelease (propertyMatchDict);
|
||||
if (locationCF)
|
||||
CFRelease (locationCF);
|
||||
}
|
||||
|
||||
return IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, deviceIterator);
|
||||
@ -300,6 +315,7 @@ static usb_device_t **darwin_device_from_service (io_service_t service)
|
||||
}
|
||||
|
||||
static void darwin_devices_attached (void *ptr, io_iterator_t add_devices) {
|
||||
UNUSED(ptr);
|
||||
struct libusb_context *ctx;
|
||||
io_service_t service;
|
||||
|
||||
@ -308,7 +324,7 @@ static void darwin_devices_attached (void *ptr, io_iterator_t add_devices) {
|
||||
while ((service = IOIteratorNext(add_devices))) {
|
||||
/* add this device to each active context's device list */
|
||||
list_for_each_entry(ctx, &active_contexts_list, list, struct libusb_context) {
|
||||
process_new_device (ctx, service);;
|
||||
process_new_device (ctx, service);
|
||||
}
|
||||
|
||||
IOObjectRelease(service);
|
||||
@ -318,6 +334,7 @@ static void darwin_devices_attached (void *ptr, io_iterator_t add_devices) {
|
||||
}
|
||||
|
||||
static void darwin_devices_detached (void *ptr, io_iterator_t rem_devices) {
|
||||
UNUSED(ptr);
|
||||
struct libusb_device *dev = NULL;
|
||||
struct libusb_context *ctx;
|
||||
struct darwin_cached_device *old_device;
|
||||
@ -516,7 +533,6 @@ static void darwin_check_version (void) {
|
||||
}
|
||||
|
||||
static int darwin_init(struct libusb_context *ctx) {
|
||||
host_name_port_t host_self;
|
||||
int rc;
|
||||
|
||||
rc = pthread_once (&darwin_init_once, darwin_check_version);
|
||||
@ -530,12 +546,15 @@ static int darwin_init(struct libusb_context *ctx) {
|
||||
}
|
||||
|
||||
if (libusb_darwin_atomic_fetch_add (&initCount, 1) == 0) {
|
||||
/* create the clocks that will be used */
|
||||
#if !OSX_USE_CLOCK_GETTIME
|
||||
/* create the clocks that will be used if clock_gettime() is not available */
|
||||
host_name_port_t host_self;
|
||||
|
||||
host_self = mach_host_self();
|
||||
host_get_clock_service(host_self, CALENDAR_CLOCK, &clock_realtime);
|
||||
host_get_clock_service(host_self, SYSTEM_CLOCK, &clock_monotonic);
|
||||
mach_port_deallocate(mach_task_self(), host_self);
|
||||
#endif
|
||||
|
||||
pthread_create (&libusb_darwin_at, NULL, darwin_event_thread_main, ctx);
|
||||
|
||||
@ -548,10 +567,13 @@ static int darwin_init(struct libusb_context *ctx) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void darwin_exit (void) {
|
||||
static void darwin_exit (struct libusb_context *ctx) {
|
||||
UNUSED(ctx);
|
||||
if (libusb_darwin_atomic_fetch_add (&initCount, -1) == 1) {
|
||||
#if !OSX_USE_CLOCK_GETTIME
|
||||
mach_port_deallocate(mach_task_self(), clock_realtime);
|
||||
mach_port_deallocate(mach_task_self(), clock_monotonic);
|
||||
#endif
|
||||
|
||||
/* stop the event runloop and wait for the thread to terminate. */
|
||||
CFRunLoopSourceSignal(libusb_darwin_acfls);
|
||||
@ -866,14 +888,29 @@ static int get_device_port (io_service_t service, UInt8 *port) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int get_device_parent_sessionID(io_service_t service, UInt64 *parent_sessionID) {
|
||||
kern_return_t result;
|
||||
io_service_t parent;
|
||||
|
||||
/* Walk up the tree in the IOService plane until we find a parent that has a sessionID */
|
||||
parent = service;
|
||||
while((result = IORegistryEntryGetParentEntry (parent, kIOServicePlane, &parent)) == kIOReturnSuccess) {
|
||||
if (get_ioregistry_value_number (parent, CFSTR("sessionID"), kCFNumberSInt64Type, parent_sessionID)) {
|
||||
/* Success */
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* We ran out of parents */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int darwin_get_cached_device(struct libusb_context *ctx, io_service_t service,
|
||||
struct darwin_cached_device **cached_out) {
|
||||
struct darwin_cached_device *new_device;
|
||||
UInt64 sessionID = 0, parent_sessionID = 0;
|
||||
int ret = LIBUSB_SUCCESS;
|
||||
usb_device_t **device;
|
||||
io_service_t parent;
|
||||
kern_return_t result;
|
||||
UInt8 port = 0;
|
||||
|
||||
/* get some info from the io registry */
|
||||
@ -884,11 +921,8 @@ static int darwin_get_cached_device(struct libusb_context *ctx, io_service_t ser
|
||||
|
||||
usbi_dbg("finding cached device for sessionID 0x%" PRIx64, sessionID);
|
||||
|
||||
result = IORegistryEntryGetParentEntry (service, kIOUSBPlane, &parent);
|
||||
|
||||
if (kIOReturnSuccess == result) {
|
||||
(void) get_ioregistry_value_number (parent, CFSTR("sessionID"), kCFNumberSInt64Type, &parent_sessionID);
|
||||
IOObjectRelease(parent);
|
||||
if (get_device_parent_sessionID(service, &parent_sessionID)) {
|
||||
usbi_dbg("parent sessionID: 0x%" PRIx64, parent_sessionID);
|
||||
}
|
||||
|
||||
usbi_mutex_lock(&darwin_cached_devices_lock);
|
||||
@ -1005,8 +1039,11 @@ static int process_new_device (struct libusb_context *ctx, io_service_t service)
|
||||
case kUSBDeviceSpeedLow: dev->speed = LIBUSB_SPEED_LOW; break;
|
||||
case kUSBDeviceSpeedFull: dev->speed = LIBUSB_SPEED_FULL; break;
|
||||
case kUSBDeviceSpeedHigh: dev->speed = LIBUSB_SPEED_HIGH; break;
|
||||
#if DeviceVersion >= 500
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
|
||||
case kUSBDeviceSpeedSuper: dev->speed = LIBUSB_SPEED_SUPER; break;
|
||||
#endif
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101200
|
||||
case kUSBDeviceSpeedSuperPlus: dev->speed = LIBUSB_SPEED_SUPER_PLUS; break;
|
||||
#endif
|
||||
default:
|
||||
usbi_warn (ctx, "Got unknown device speed %d", devSpeed);
|
||||
@ -1216,9 +1253,9 @@ static int get_endpoints (struct libusb_device_handle *dev_handle, int iface) {
|
||||
|
||||
kern_return_t kresult;
|
||||
|
||||
u_int8_t numep, direction, number;
|
||||
u_int8_t dont_care1, dont_care3;
|
||||
u_int16_t dont_care2;
|
||||
UInt8 numep, direction, number;
|
||||
UInt8 dont_care1, dont_care3;
|
||||
UInt16 dont_care2;
|
||||
int rc;
|
||||
|
||||
usbi_dbg ("building table of endpoints.");
|
||||
@ -1965,6 +2002,7 @@ static int darwin_handle_transfer_completion (struct usbi_transfer *itransfer) {
|
||||
}
|
||||
|
||||
static int darwin_clock_gettime(int clk_id, struct timespec *tp) {
|
||||
#if !OSX_USE_CLOCK_GETTIME
|
||||
mach_timespec_t sys_time;
|
||||
clock_serv_t clock_ref;
|
||||
|
||||
@ -1987,6 +2025,16 @@ static int darwin_clock_gettime(int clk_id, struct timespec *tp) {
|
||||
tp->tv_nsec = sys_time.tv_nsec;
|
||||
|
||||
return 0;
|
||||
#else
|
||||
switch (clk_id) {
|
||||
case USBI_CLOCK_MONOTONIC:
|
||||
return clock_gettime(CLOCK_MONOTONIC, tp);
|
||||
case USBI_CLOCK_REALTIME:
|
||||
return clock_gettime(CLOCK_REALTIME, tp);
|
||||
default:
|
||||
return LIBUSB_ERROR_INVALID_PARAM;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if InterfaceVersion >= 550
|
||||
@ -2047,7 +2095,7 @@ static int darwin_free_streams (struct libusb_device_handle *dev_handle, unsigne
|
||||
}
|
||||
#endif
|
||||
|
||||
const struct usbi_os_backend darwin_backend = {
|
||||
const struct usbi_os_backend usbi_backend = {
|
||||
.name = "Darwin",
|
||||
.caps = 0,
|
||||
.init = darwin_init,
|
@ -28,37 +28,58 @@
|
||||
#include <IOKit/IOCFPlugIn.h>
|
||||
|
||||
/* IOUSBInterfaceInferface */
|
||||
#if defined (kIOUSBInterfaceInterfaceID700) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_9
|
||||
|
||||
/* New in OS 10.12.0. */
|
||||
#if defined (kIOUSBInterfaceInterfaceID800) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 101200)
|
||||
|
||||
#define usb_interface_t IOUSBInterfaceInterface800
|
||||
#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID800
|
||||
#define InterfaceVersion 800
|
||||
|
||||
/* New in OS 10.10.0. */
|
||||
#elif defined (kIOUSBInterfaceInterfaceID700) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 101000)
|
||||
|
||||
#define usb_interface_t IOUSBInterfaceInterface700
|
||||
#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID700
|
||||
#define InterfaceVersion 700
|
||||
|
||||
#elif defined (kIOUSBInterfaceInterfaceID550) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_9
|
||||
/* New in OS 10.9.0. */
|
||||
#elif defined (kIOUSBInterfaceInterfaceID650) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1090)
|
||||
|
||||
#define usb_interface_t IOUSBInterfaceInterface650
|
||||
#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID650
|
||||
#define InterfaceVersion 650
|
||||
|
||||
/* New in OS 10.8.2 but can't test deployment target to that granularity, so round up. */
|
||||
#elif defined (kIOUSBInterfaceInterfaceID550) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1090)
|
||||
|
||||
#define usb_interface_t IOUSBInterfaceInterface550
|
||||
#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID550
|
||||
#define InterfaceVersion 550
|
||||
|
||||
#elif defined (kIOUSBInterfaceInterfaceID500)
|
||||
/* New in OS 10.7.3 but can't test deployment target to that granularity, so round up. */
|
||||
#elif defined (kIOUSBInterfaceInterfaceID500) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1080)
|
||||
|
||||
#define usb_interface_t IOUSBInterfaceInterface500
|
||||
#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID500
|
||||
#define InterfaceVersion 500
|
||||
|
||||
#elif defined (kIOUSBInterfaceInterfaceID300)
|
||||
/* New in OS 10.5.0. */
|
||||
#elif defined (kIOUSBInterfaceInterfaceID300) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1050)
|
||||
|
||||
#define usb_interface_t IOUSBInterfaceInterface300
|
||||
#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID300
|
||||
#define InterfaceVersion 300
|
||||
|
||||
#elif defined (kIOUSBInterfaceInterfaceID245)
|
||||
/* New in OS 10.4.5 (or 10.4.6?) but can't test deployment target to that granularity, so round up. */
|
||||
#elif defined (kIOUSBInterfaceInterfaceID245) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1050)
|
||||
|
||||
#define usb_interface_t IOUSBInterfaceInterface245
|
||||
#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID245
|
||||
#define InterfaceVersion 245
|
||||
|
||||
#elif defined (kIOUSBInterfaceInterfaceID220)
|
||||
/* New in OS 10.4.0. */
|
||||
#elif defined (kIOUSBInterfaceInterfaceID220) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1040)
|
||||
|
||||
#define usb_interface_t IOUSBInterfaceInterface220
|
||||
#define InterfaceInterfaceID kIOUSBInterfaceInterfaceID220
|
||||
@ -66,43 +87,57 @@
|
||||
|
||||
#else
|
||||
|
||||
#error "IOUSBFamily is too old. Please upgrade your OS"
|
||||
#error "IOUSBFamily is too old. Please upgrade your SDK and/or deployment target"
|
||||
|
||||
#endif
|
||||
|
||||
/* IOUSBDeviceInterface */
|
||||
#if defined (kIOUSBDeviceInterfaceID500) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_9
|
||||
|
||||
/* New in OS 10.9.0. */
|
||||
#if defined (kIOUSBDeviceInterfaceID650) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1090)
|
||||
|
||||
#define usb_device_t IOUSBDeviceInterface650
|
||||
#define DeviceInterfaceID kIOUSBDeviceInterfaceID650
|
||||
#define DeviceVersion 650
|
||||
|
||||
/* New in OS 10.7.3 but can't test deployment target to that granularity, so round up. */
|
||||
#elif defined (kIOUSBDeviceInterfaceID500) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1080)
|
||||
|
||||
#define usb_device_t IOUSBDeviceInterface500
|
||||
#define DeviceInterfaceID kIOUSBDeviceInterfaceID500
|
||||
#define DeviceVersion 500
|
||||
|
||||
#elif defined (kIOUSBDeviceInterfaceID320)
|
||||
/* New in OS 10.5.4 but can't test deployment target to that granularity, so round up. */
|
||||
#elif defined (kIOUSBDeviceInterfaceID320) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1060)
|
||||
|
||||
#define usb_device_t IOUSBDeviceInterface320
|
||||
#define DeviceInterfaceID kIOUSBDeviceInterfaceID320
|
||||
#define DeviceVersion 320
|
||||
|
||||
#elif defined (kIOUSBDeviceInterfaceID300)
|
||||
/* New in OS 10.5.0. */
|
||||
#elif defined (kIOUSBDeviceInterfaceID300) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1050)
|
||||
|
||||
#define usb_device_t IOUSBDeviceInterface300
|
||||
#define DeviceInterfaceID kIOUSBDeviceInterfaceID300
|
||||
#define DeviceVersion 300
|
||||
|
||||
#elif defined (kIOUSBDeviceInterfaceID245)
|
||||
/* New in OS 10.4.5 (or 10.4.6?) but can't test deployment target to that granularity, so round up. */
|
||||
#elif defined (kIOUSBDeviceInterfaceID245) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1050)
|
||||
|
||||
#define usb_device_t IOUSBDeviceInterface245
|
||||
#define DeviceInterfaceID kIOUSBDeviceInterfaceID245
|
||||
#define DeviceVersion 245
|
||||
|
||||
#elif defined (kIOUSBDeviceInterfaceID220)
|
||||
/* New in OS 10.2.3 but can't test deployment target to that granularity, so round up. */
|
||||
#elif defined (kIOUSBDeviceInterfaceID197) && (MAC_OS_X_VERSION_MIN_REQUIRED >= 1030)
|
||||
|
||||
#define usb_device_t IOUSBDeviceInterface197
|
||||
#define DeviceInterfaceID kIOUSBDeviceInterfaceID197
|
||||
#define DeviceVersion 197
|
||||
|
||||
#else
|
||||
|
||||
#error "IOUSBFamily is too old. Please upgrade your OS"
|
||||
#error "IOUSBFamily is too old. Please upgrade your SDK and/or deployment target"
|
||||
|
||||
#endif
|
||||
|
@ -38,8 +38,9 @@ haiku_init(struct libusb_context *ctx)
|
||||
}
|
||||
|
||||
static void
|
||||
haiku_exit(void)
|
||||
haiku_exit(struct libusb_context *ctx)
|
||||
{
|
||||
UNUSED(ctx);
|
||||
if (atomic_add(&gInitCount, -1) == 1)
|
||||
gUsbRoster.Stop();
|
||||
}
|
||||
@ -195,11 +196,12 @@ haiku_clock_gettime(int clkid, struct timespec *tp)
|
||||
return LIBUSB_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
const struct usbi_os_backend haiku_usb_raw_backend = {
|
||||
const struct usbi_os_backend usbi_backend = {
|
||||
/*.name =*/ "Haiku usbfs",
|
||||
/*.caps =*/ 0,
|
||||
/*.init =*/ haiku_init,
|
||||
/*.exit =*/ haiku_exit,
|
||||
/*.set_option =*/ NULL,
|
||||
/*.get_device_list =*/ NULL,
|
||||
/*.hotplug_poll =*/ NULL,
|
||||
/*.open =*/ haiku_open,
|
||||
@ -244,6 +246,7 @@ const struct usbi_os_backend haiku_usb_raw_backend = {
|
||||
/*.get_timerfd_clockid =*/ NULL,
|
||||
#endif
|
||||
|
||||
/*.context_priv_size=*/ 0,
|
||||
/*.device_priv_size =*/ sizeof(USBDevice *),
|
||||
/*.device_handle_priv_size =*/ sizeof(USBDeviceHandle *),
|
||||
/*.transfer_priv_size =*/ sizeof(USBTransfer *),
|
@ -45,24 +45,33 @@
|
||||
|
||||
#define NL_GROUP_KERNEL 1
|
||||
|
||||
#ifndef SOCK_CLOEXEC
|
||||
#define SOCK_CLOEXEC 0
|
||||
#endif
|
||||
|
||||
#ifndef SOCK_NONBLOCK
|
||||
#define SOCK_NONBLOCK 0
|
||||
#endif
|
||||
|
||||
static int linux_netlink_socket = -1;
|
||||
static int netlink_control_pipe[2] = { -1, -1 };
|
||||
static pthread_t libusb_linux_event_thread;
|
||||
|
||||
static void *linux_netlink_event_thread_main(void *arg);
|
||||
|
||||
static int set_fd_cloexec_nb(int fd)
|
||||
static int set_fd_cloexec_nb(int fd, int socktype)
|
||||
{
|
||||
int flags;
|
||||
|
||||
#if defined(FD_CLOEXEC)
|
||||
/* Make sure the netlink socket file descriptor is marked as CLOEXEC */
|
||||
if (!(socktype & SOCK_CLOEXEC)) {
|
||||
flags = fcntl(fd, F_GETFD);
|
||||
if (flags == -1) {
|
||||
usbi_err(NULL, "failed to get netlink fd flags (%d)", errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!(flags & FD_CLOEXEC)) {
|
||||
if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) {
|
||||
usbi_err(NULL, "failed to set netlink fd flags (%d)", errno);
|
||||
return -1;
|
||||
@ -70,13 +79,14 @@ static int set_fd_cloexec_nb(int fd)
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Make sure the netlink socket is non-blocking */
|
||||
if (!(socktype & SOCK_NONBLOCK)) {
|
||||
flags = fcntl(fd, F_GETFL);
|
||||
if (flags == -1) {
|
||||
usbi_err(NULL, "failed to get netlink fd status flags (%d)", errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!(flags & O_NONBLOCK)) {
|
||||
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
|
||||
usbi_err(NULL, "failed to set netlink fd status flags (%d)", errno);
|
||||
return -1;
|
||||
@ -89,21 +99,15 @@ static int set_fd_cloexec_nb(int fd)
|
||||
int linux_netlink_start_event_monitor(void)
|
||||
{
|
||||
struct sockaddr_nl sa_nl = { .nl_family = AF_NETLINK, .nl_groups = NL_GROUP_KERNEL };
|
||||
int socktype = SOCK_RAW;
|
||||
int socktype = SOCK_RAW | SOCK_NONBLOCK | SOCK_CLOEXEC;
|
||||
int opt = 1;
|
||||
int ret;
|
||||
|
||||
#if defined(SOCK_CLOEXEC)
|
||||
socktype |= SOCK_CLOEXEC;
|
||||
#endif
|
||||
#if defined(SOCK_NONBLOCK)
|
||||
socktype |= SOCK_NONBLOCK;
|
||||
#endif
|
||||
|
||||
linux_netlink_socket = socket(PF_NETLINK, socktype, NETLINK_KOBJECT_UEVENT);
|
||||
if (linux_netlink_socket == -1 && errno == EINVAL) {
|
||||
usbi_dbg("failed to create netlink socket of type %d, attempting SOCK_RAW", socktype);
|
||||
linux_netlink_socket = socket(PF_NETLINK, SOCK_RAW, NETLINK_KOBJECT_UEVENT);
|
||||
socktype = SOCK_RAW;
|
||||
linux_netlink_socket = socket(PF_NETLINK, socktype, NETLINK_KOBJECT_UEVENT);
|
||||
}
|
||||
|
||||
if (linux_netlink_socket == -1) {
|
||||
@ -111,7 +115,7 @@ int linux_netlink_start_event_monitor(void)
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = set_fd_cloexec_nb(linux_netlink_socket);
|
||||
ret = set_fd_cloexec_nb(linux_netlink_socket, socktype);
|
||||
if (ret == -1)
|
||||
goto err_close_socket;
|
||||
|
||||
@ -162,7 +166,7 @@ int linux_netlink_stop_event_monitor(void)
|
||||
|
||||
/* Write some dummy data to the control pipe and
|
||||
* wait for the thread to exit */
|
||||
r = usbi_write(netlink_control_pipe[1], &dummy, sizeof(dummy));
|
||||
r = write(netlink_control_pipe[1], &dummy, sizeof(dummy));
|
||||
if (r <= 0)
|
||||
usbi_warn(NULL, "netlink control pipe signal failed");
|
||||
|
||||
@ -356,7 +360,8 @@ static int linux_netlink_read_message(void)
|
||||
static void *linux_netlink_event_thread_main(void *arg)
|
||||
{
|
||||
char dummy;
|
||||
ssize_t r;
|
||||
int r;
|
||||
ssize_t nb;
|
||||
struct pollfd fds[] = {
|
||||
{ .fd = netlink_control_pipe[0],
|
||||
.events = POLLIN },
|
||||
@ -368,11 +373,15 @@ static void *linux_netlink_event_thread_main(void *arg)
|
||||
|
||||
usbi_dbg("netlink event thread entering");
|
||||
|
||||
while (poll(fds, 2, -1) >= 0) {
|
||||
while ((r = poll(fds, 2, -1)) >= 0 || errno == EINTR) {
|
||||
if (r < 0) {
|
||||
/* temporary failure */
|
||||
continue;
|
||||
}
|
||||
if (fds[0].revents & POLLIN) {
|
||||
/* activity on control pipe, read the byte and exit */
|
||||
r = usbi_read(netlink_control_pipe[0], &dummy, sizeof(dummy));
|
||||
if (r <= 0)
|
||||
nb = read(netlink_control_pipe[0], &dummy, sizeof(dummy));
|
||||
if (nb <= 0)
|
||||
usbi_warn(NULL, "netlink control pipe read failed");
|
||||
break;
|
||||
}
|
@ -82,18 +82,34 @@ int linux_udev_start_event_monitor(void)
|
||||
|
||||
udev_monitor_fd = udev_monitor_get_fd(udev_monitor);
|
||||
|
||||
#if defined(FD_CLOEXEC)
|
||||
/* Make sure the udev file descriptor is marked as CLOEXEC */
|
||||
r = fcntl(udev_monitor_fd, F_GETFD);
|
||||
if (r == -1) {
|
||||
usbi_err(NULL, "geting udev monitor fd flags (%d)", errno);
|
||||
goto err_free_monitor;
|
||||
}
|
||||
if (!(r & FD_CLOEXEC)) {
|
||||
if (fcntl(udev_monitor_fd, F_SETFD, r | FD_CLOEXEC) == -1) {
|
||||
usbi_err(NULL, "setting udev monitor fd flags (%d)", errno);
|
||||
goto err_free_monitor;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Some older versions of udev are not non-blocking by default,
|
||||
* so make sure this is set */
|
||||
r = fcntl(udev_monitor_fd, F_GETFL);
|
||||
if (r == -1) {
|
||||
usbi_err(NULL, "getting udev monitor fd flags (%d)", errno);
|
||||
usbi_err(NULL, "getting udev monitor fd status flags (%d)", errno);
|
||||
goto err_free_monitor;
|
||||
}
|
||||
r = fcntl(udev_monitor_fd, F_SETFL, r | O_NONBLOCK);
|
||||
if (r) {
|
||||
usbi_err(NULL, "setting udev monitor fd flags (%d)", errno);
|
||||
if (!(r & O_NONBLOCK)) {
|
||||
if (fcntl(udev_monitor_fd, F_SETFL, r | O_NONBLOCK) == -1) {
|
||||
usbi_err(NULL, "setting udev monitor fd status flags (%d)", errno);
|
||||
goto err_free_monitor;
|
||||
}
|
||||
}
|
||||
|
||||
r = usbi_pipe(udev_control_pipe);
|
||||
if (r) {
|
||||
@ -134,7 +150,7 @@ int linux_udev_stop_event_monitor(void)
|
||||
|
||||
/* Write some dummy data to the control pipe and
|
||||
* wait for the thread to exit */
|
||||
r = usbi_write(udev_control_pipe[1], &dummy, sizeof(dummy));
|
||||
r = write(udev_control_pipe[1], &dummy, sizeof(dummy));
|
||||
if (r <= 0) {
|
||||
usbi_warn(NULL, "udev control pipe signal failed");
|
||||
}
|
||||
@ -162,6 +178,7 @@ static void *linux_udev_event_thread_main(void *arg)
|
||||
{
|
||||
char dummy;
|
||||
int r;
|
||||
ssize_t nb;
|
||||
struct udev_device* udev_dev;
|
||||
struct pollfd fds[] = {
|
||||
{.fd = udev_control_pipe[0],
|
||||
@ -179,8 +196,8 @@ static void *linux_udev_event_thread_main(void *arg)
|
||||
}
|
||||
if (fds[0].revents & POLLIN) {
|
||||
/* activity on control pipe, read the byte and exit */
|
||||
r = usbi_read(udev_control_pipe[0], &dummy, sizeof(dummy));
|
||||
if (r <= 0) {
|
||||
nb = read(udev_control_pipe[0], &dummy, sizeof(dummy));
|
||||
if (nb <= 0) {
|
||||
usbi_warn(NULL, "udev control pipe read failed");
|
||||
}
|
||||
break;
|
||||
@ -274,6 +291,7 @@ int linux_udev_scan_devices(struct libusb_context *ctx)
|
||||
udev_enumerate_scan_devices(enumerator);
|
||||
devices = udev_enumerate_get_list_entry(enumerator);
|
||||
|
||||
entry = NULL;
|
||||
udev_list_entry_foreach(entry, devices) {
|
||||
const char *path = udev_list_entry_get_name(entry);
|
||||
uint8_t busnum = 0, devaddr = 0;
|
@ -81,6 +81,19 @@ static const char *usbfs_path = NULL;
|
||||
/* use usbdev*.* device names in /dev instead of the usbfs bus directories */
|
||||
static int usbdev_names = 0;
|
||||
|
||||
/* Linux has changed the maximum length of an individual isochronous packet
|
||||
* over time. Initially this limit was 1,023 bytes, but Linux 2.6.18
|
||||
* (commit 3612242e527eb47ee4756b5350f8bdf791aa5ede) increased this value to
|
||||
* 8,192 bytes to support higher bandwidth devices. Linux 3.10
|
||||
* (commit e2e2f0ea1c935edcf53feb4c4c8fdb4f86d57dd9) further increased this
|
||||
* value to 49,152 bytes to support super speed devices.
|
||||
*/
|
||||
static unsigned int max_iso_packet_len = 0;
|
||||
|
||||
/* Linux 2.6.23 adds support for O_CLOEXEC when opening files, which marks the
|
||||
* close-on-exec flag in the underlying file descriptor. */
|
||||
static int supports_flag_cloexec = -1;
|
||||
|
||||
/* Linux 2.6.32 adds support for a bulk continuation URB flag. this basically
|
||||
* allows us to mark URBs as being part of a specific logical transfer when
|
||||
* we submit them to the kernel. then, on any error except a cancellation, all
|
||||
@ -136,6 +149,12 @@ static int detach_kernel_driver_and_claim(struct libusb_device_handle *, int);
|
||||
static int linux_default_scan_devices (struct libusb_context *ctx);
|
||||
#endif
|
||||
|
||||
struct kernel_version {
|
||||
int major;
|
||||
int minor;
|
||||
int sublevel;
|
||||
};
|
||||
|
||||
struct linux_device_priv {
|
||||
char *sysfs_dir;
|
||||
unsigned char *descriptors;
|
||||
@ -180,6 +199,16 @@ struct linux_transfer_priv {
|
||||
int iso_packet_offset;
|
||||
};
|
||||
|
||||
static int _open(const char *path, int flags)
|
||||
{
|
||||
#if defined(O_CLOEXEC)
|
||||
if (supports_flag_cloexec)
|
||||
return open(path, flags | O_CLOEXEC);
|
||||
else
|
||||
#endif
|
||||
return open(path, flags);
|
||||
}
|
||||
|
||||
static int _get_usbfs_fd(struct libusb_device *dev, mode_t mode, int silent)
|
||||
{
|
||||
struct libusb_context *ctx = DEVICE_CTX(dev);
|
||||
@ -194,7 +223,7 @@ static int _get_usbfs_fd(struct libusb_device *dev, mode_t mode, int silent)
|
||||
snprintf(path, PATH_MAX, "%s/%03d/%03d",
|
||||
usbfs_path, dev->bus_number, dev->device_address);
|
||||
|
||||
fd = open(path, mode);
|
||||
fd = _open(path, mode);
|
||||
if (fd != -1)
|
||||
return fd; /* Success */
|
||||
|
||||
@ -205,7 +234,7 @@ static int _get_usbfs_fd(struct libusb_device *dev, mode_t mode, int silent)
|
||||
/* Wait 10ms for USB device path creation.*/
|
||||
nanosleep(&(struct timespec){delay / 1000000, (delay * 1000) % 1000000000UL}, NULL);
|
||||
|
||||
fd = open(path, mode);
|
||||
fd = _open(path, mode);
|
||||
if (fd != -1)
|
||||
return fd; /* Success */
|
||||
}
|
||||
@ -342,39 +371,59 @@ static clockid_t find_monotonic_clock(void)
|
||||
return CLOCK_REALTIME;
|
||||
}
|
||||
|
||||
static int kernel_version_ge(int major, int minor, int sublevel)
|
||||
static int get_kernel_version(struct libusb_context *ctx,
|
||||
struct kernel_version *ver)
|
||||
{
|
||||
struct utsname uts;
|
||||
int atoms, kmajor, kminor, ksublevel;
|
||||
int atoms;
|
||||
|
||||
if (uname(&uts) < 0)
|
||||
return -1;
|
||||
atoms = sscanf(uts.release, "%d.%d.%d", &kmajor, &kminor, &ksublevel);
|
||||
if (atoms < 1)
|
||||
if (uname(&uts) < 0) {
|
||||
usbi_err(ctx, "uname failed, errno %d", errno);
|
||||
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;
|
||||
if (kmajor < major)
|
||||
else if (ver->major < major)
|
||||
return 0;
|
||||
|
||||
/* kmajor == major */
|
||||
if (atoms < 2)
|
||||
if (ver->minor == -1 && ver->sublevel == -1)
|
||||
return 0 == minor && 0 == sublevel;
|
||||
if (kminor > minor)
|
||||
else if (ver->minor > minor)
|
||||
return 1;
|
||||
if (kminor < minor)
|
||||
else if (ver->minor < minor)
|
||||
return 0;
|
||||
|
||||
/* kminor == minor */
|
||||
if (atoms < 3)
|
||||
if (ver->sublevel == -1)
|
||||
return 0 == sublevel;
|
||||
|
||||
return ksublevel >= sublevel;
|
||||
return ver->sublevel >= sublevel;
|
||||
}
|
||||
|
||||
static int op_init(struct libusb_context *ctx)
|
||||
{
|
||||
struct kernel_version kversion;
|
||||
struct stat statbuf;
|
||||
int r;
|
||||
|
||||
@ -387,13 +436,17 @@ static int op_init(struct libusb_context *ctx)
|
||||
if (monotonic_clkid == -1)
|
||||
monotonic_clkid = find_monotonic_clock();
|
||||
|
||||
if (get_kernel_version(ctx, &kversion) < 0)
|
||||
return LIBUSB_ERROR_OTHER;
|
||||
|
||||
if (supports_flag_cloexec == -1) {
|
||||
/* O_CLOEXEC flag available from Linux 2.6.23 */
|
||||
supports_flag_cloexec = kernel_version_ge(&kversion,2,6,23);
|
||||
}
|
||||
|
||||
if (supports_flag_bulk_continuation == -1) {
|
||||
/* bulk continuation URB flag available from Linux 2.6.32 */
|
||||
supports_flag_bulk_continuation = kernel_version_ge(2,6,32);
|
||||
if (supports_flag_bulk_continuation == -1) {
|
||||
usbi_err(ctx, "error checking for bulk continuation support");
|
||||
return LIBUSB_ERROR_OTHER;
|
||||
}
|
||||
supports_flag_bulk_continuation = kernel_version_ge(&kversion,2,6,32);
|
||||
}
|
||||
|
||||
if (supports_flag_bulk_continuation)
|
||||
@ -401,32 +454,31 @@ static int op_init(struct libusb_context *ctx)
|
||||
|
||||
if (-1 == supports_flag_zero_packet) {
|
||||
/* zero length packet URB flag fixed since Linux 2.6.31 */
|
||||
supports_flag_zero_packet = kernel_version_ge(2,6,31);
|
||||
if (-1 == supports_flag_zero_packet) {
|
||||
usbi_err(ctx, "error checking for zero length packet support");
|
||||
return LIBUSB_ERROR_OTHER;
|
||||
}
|
||||
supports_flag_zero_packet = kernel_version_ge(&kversion,2,6,31);
|
||||
}
|
||||
|
||||
if (supports_flag_zero_packet)
|
||||
usbi_dbg("zero length packet flag supported");
|
||||
|
||||
if (!max_iso_packet_len) {
|
||||
if (kernel_version_ge(&kversion,3,10,0))
|
||||
max_iso_packet_len = 49152;
|
||||
else if (kernel_version_ge(&kversion,2,6,18))
|
||||
max_iso_packet_len = 8192;
|
||||
else
|
||||
max_iso_packet_len = 1023;
|
||||
}
|
||||
|
||||
usbi_dbg("max iso packet length is (likely) %u bytes", max_iso_packet_len);
|
||||
|
||||
if (-1 == sysfs_has_descriptors) {
|
||||
/* sysfs descriptors has all descriptors since Linux 2.6.26 */
|
||||
sysfs_has_descriptors = kernel_version_ge(2,6,26);
|
||||
if (-1 == sysfs_has_descriptors) {
|
||||
usbi_err(ctx, "error checking for sysfs descriptors");
|
||||
return LIBUSB_ERROR_OTHER;
|
||||
}
|
||||
sysfs_has_descriptors = kernel_version_ge(&kversion,2,6,26);
|
||||
}
|
||||
|
||||
if (-1 == sysfs_can_relate_devices) {
|
||||
/* sysfs has busnum since Linux 2.6.22 */
|
||||
sysfs_can_relate_devices = kernel_version_ge(2,6,22);
|
||||
if (-1 == sysfs_can_relate_devices) {
|
||||
usbi_err(ctx, "error checking for sysfs busnum");
|
||||
return LIBUSB_ERROR_OTHER;
|
||||
}
|
||||
sysfs_can_relate_devices = kernel_version_ge(&kversion,2,6,22);
|
||||
}
|
||||
|
||||
if (sysfs_can_relate_devices || sysfs_has_descriptors) {
|
||||
@ -463,8 +515,9 @@ static int op_init(struct libusb_context *ctx)
|
||||
return r;
|
||||
}
|
||||
|
||||
static void op_exit(void)
|
||||
static void op_exit(struct libusb_context *ctx)
|
||||
{
|
||||
UNUSED(ctx);
|
||||
usbi_mutex_static_lock(&linux_hotplug_startstop_lock);
|
||||
assert(init_count != 0);
|
||||
if (!--init_count) {
|
||||
@ -526,7 +579,7 @@ static int _open_sysfs_attr(struct libusb_device *dev, const char *attr)
|
||||
|
||||
snprintf(filename, PATH_MAX, "%s/%s/%s",
|
||||
SYSFS_DEVICE_PATH, priv->sysfs_dir, attr);
|
||||
fd = open(filename, O_RDONLY);
|
||||
fd = _open(filename, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
usbi_err(DEVICE_CTX(dev),
|
||||
"open %s failed ret=%d errno=%d", filename, fd, errno);
|
||||
@ -542,12 +595,12 @@ static int __read_sysfs_attr(struct libusb_context *ctx,
|
||||
{
|
||||
char filename[PATH_MAX];
|
||||
FILE *f;
|
||||
int r, value;
|
||||
int fd, r, value;
|
||||
|
||||
snprintf(filename, PATH_MAX, "%s/%s/%s", SYSFS_DEVICE_PATH,
|
||||
devname, attr);
|
||||
f = fopen(filename, "r");
|
||||
if (f == NULL) {
|
||||
fd = _open(filename, O_RDONLY);
|
||||
if (fd == -1) {
|
||||
if (errno == ENOENT) {
|
||||
/* File doesn't exist. Assume the device has been
|
||||
disconnected (see trac ticket #70). */
|
||||
@ -557,6 +610,13 @@ static int __read_sysfs_attr(struct libusb_context *ctx,
|
||||
return LIBUSB_ERROR_IO;
|
||||
}
|
||||
|
||||
f = fdopen(fd, "r");
|
||||
if (f == NULL) {
|
||||
usbi_err(ctx, "fdopen %s failed errno=%d", filename, errno);
|
||||
close(fd);
|
||||
return LIBUSB_ERROR_OTHER;
|
||||
}
|
||||
|
||||
r = fscanf(f, "%d", &value);
|
||||
fclose(f);
|
||||
if (r != 1) {
|
||||
@ -806,7 +866,7 @@ static int op_get_active_config_descriptor(struct libusb_device *dev,
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
len = MIN(len, r);
|
||||
len = MIN(len, (size_t)r);
|
||||
memcpy(buffer, config_desc, len);
|
||||
return len;
|
||||
}
|
||||
@ -836,7 +896,7 @@ static int op_get_config_descriptor(struct libusb_device *dev,
|
||||
descriptors += r;
|
||||
}
|
||||
|
||||
len = MIN(len, r);
|
||||
len = MIN(len, (size_t)r);
|
||||
memcpy(buffer, descriptors, len);
|
||||
return len;
|
||||
}
|
||||
@ -911,6 +971,7 @@ static int initialize_device(struct libusb_device *dev, uint8_t busnum,
|
||||
case 12: dev->speed = LIBUSB_SPEED_FULL; break;
|
||||
case 480: dev->speed = LIBUSB_SPEED_HIGH; break;
|
||||
case 5000: dev->speed = LIBUSB_SPEED_SUPER; break;
|
||||
case 10000: dev->speed = LIBUSB_SPEED_SUPER_PLUS; break;
|
||||
default:
|
||||
usbi_warn(DEVICE_CTX(dev), "Unknown device speed: %d Mbps", speed);
|
||||
}
|
||||
@ -1239,11 +1300,12 @@ static int sysfs_get_device_list(struct libusb_context *ctx)
|
||||
{
|
||||
DIR *devices = opendir(SYSFS_DEVICE_PATH);
|
||||
struct dirent *entry;
|
||||
int r = LIBUSB_ERROR_IO;
|
||||
int num_devices = 0;
|
||||
int num_enumerated = 0;
|
||||
|
||||
if (!devices) {
|
||||
usbi_err(ctx, "opendir devices failed errno=%d", errno);
|
||||
return r;
|
||||
return LIBUSB_ERROR_IO;
|
||||
}
|
||||
|
||||
while ((entry = readdir(devices))) {
|
||||
@ -1251,16 +1313,23 @@ static int sysfs_get_device_list(struct libusb_context *ctx)
|
||||
|| strchr(entry->d_name, ':'))
|
||||
continue;
|
||||
|
||||
num_devices++;
|
||||
|
||||
if (sysfs_scan_device(ctx, entry->d_name)) {
|
||||
usbi_dbg("failed to enumerate dir entry %s", entry->d_name);
|
||||
continue;
|
||||
}
|
||||
|
||||
r = 0;
|
||||
num_enumerated++;
|
||||
}
|
||||
|
||||
closedir(devices);
|
||||
return r;
|
||||
|
||||
/* successful if at least one device was enumerated or no devices were found */
|
||||
if (num_enumerated || !num_devices)
|
||||
return LIBUSB_SUCCESS;
|
||||
else
|
||||
return LIBUSB_ERROR_IO;
|
||||
}
|
||||
|
||||
static int linux_default_scan_devices (struct libusb_context *ctx)
|
||||
@ -1685,10 +1754,7 @@ static int detach_kernel_driver_and_claim(struct libusb_device_handle *handle,
|
||||
strcpy(dc.driver, "usbfs");
|
||||
dc.flags = USBFS_DISCONNECT_CLAIM_EXCEPT_DRIVER;
|
||||
r = ioctl(fd, IOCTL_USBFS_DISCONNECT_CLAIM, &dc);
|
||||
if (r == 0 || (r != 0 && errno != ENOTTY)) {
|
||||
if (r == 0)
|
||||
return 0;
|
||||
|
||||
if (r != 0 && errno != ENOTTY) {
|
||||
switch (errno) {
|
||||
case EBUSY:
|
||||
return LIBUSB_ERROR_BUSY;
|
||||
@ -1700,7 +1766,8 @@ static int detach_kernel_driver_and_claim(struct libusb_device_handle *handle,
|
||||
usbi_err(HANDLE_CTX(handle),
|
||||
"disconnect-and-claim failed errno %d", errno);
|
||||
return LIBUSB_ERROR_OTHER;
|
||||
}
|
||||
} else if (r == 0)
|
||||
return 0;
|
||||
|
||||
/* Fallback code for kernels which don't support the
|
||||
disconnect-and-claim ioctl */
|
||||
@ -1973,38 +2040,43 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer)
|
||||
struct linux_device_handle_priv *dpriv =
|
||||
_device_handle_priv(transfer->dev_handle);
|
||||
struct usbfs_urb **urbs;
|
||||
size_t alloc_size;
|
||||
int num_packets = transfer->num_iso_packets;
|
||||
int i;
|
||||
int this_urb_len = 0;
|
||||
int num_urbs = 1;
|
||||
int packet_offset = 0;
|
||||
int num_packets_remaining;
|
||||
int i, j;
|
||||
int num_urbs;
|
||||
unsigned int packet_len;
|
||||
unsigned int total_len = 0;
|
||||
unsigned char *urb_buffer = transfer->buffer;
|
||||
|
||||
/* usbfs places arbitrary limits on iso URBs. this limit has changed
|
||||
* at least three times, and it's difficult to accurately detect which
|
||||
* limit this running kernel might impose. so we attempt to submit
|
||||
* whatever the user has provided. if the kernel rejects the request
|
||||
* due to its size, we return an error indicating such to the user.
|
||||
*/
|
||||
if (num_packets < 1)
|
||||
return LIBUSB_ERROR_INVALID_PARAM;
|
||||
|
||||
/* 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++) {
|
||||
unsigned int space_remaining = MAX_ISO_BUFFER_LENGTH - this_urb_len;
|
||||
packet_len = transfer->iso_packet_desc[i].length;
|
||||
|
||||
if (packet_len > space_remaining) {
|
||||
num_urbs++;
|
||||
this_urb_len = packet_len;
|
||||
/* check that we can actually support this packet length */
|
||||
if (this_urb_len > MAX_ISO_BUFFER_LENGTH)
|
||||
if (packet_len > max_iso_packet_len) {
|
||||
usbi_warn(TRANSFER_CTX(transfer),
|
||||
"iso packet length of %u bytes exceeds maximum of %u bytes",
|
||||
packet_len, max_iso_packet_len);
|
||||
return LIBUSB_ERROR_INVALID_PARAM;
|
||||
} 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));
|
||||
if (!urbs)
|
||||
@ -2017,31 +2089,15 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer)
|
||||
tpriv->iso_packet_offset = 0;
|
||||
|
||||
/* allocate + initialize each URB with the correct number of packets */
|
||||
for (i = 0; i < num_urbs; i++) {
|
||||
num_packets_remaining = num_packets;
|
||||
for (i = 0, j = 0; i < num_urbs; i++) {
|
||||
int num_packets_in_urb = MIN(num_packets_remaining, MAX_ISO_PACKETS_PER_URB);
|
||||
struct usbfs_urb *urb;
|
||||
unsigned int space_remaining_in_urb = MAX_ISO_BUFFER_LENGTH;
|
||||
int urb_packet_offset = 0;
|
||||
unsigned char *urb_buffer_orig = urb_buffer;
|
||||
int j;
|
||||
size_t alloc_size;
|
||||
int k;
|
||||
|
||||
/* swallow up all the packets we can fit into this URB */
|
||||
while (packet_offset < transfer->num_iso_packets) {
|
||||
packet_len = transfer->iso_packet_desc[packet_offset].length;
|
||||
if (packet_len <= space_remaining_in_urb) {
|
||||
/* throw it in */
|
||||
urb_packet_offset++;
|
||||
packet_offset++;
|
||||
space_remaining_in_urb -= packet_len;
|
||||
urb_buffer += packet_len;
|
||||
} else {
|
||||
/* it can't fit, save it for the next URB */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
alloc_size = sizeof(*urb)
|
||||
+ (urb_packet_offset * sizeof(struct usbfs_iso_packet_desc));
|
||||
+ (num_packets_in_urb * sizeof(struct usbfs_iso_packet_desc));
|
||||
urb = calloc(1, alloc_size);
|
||||
if (!urb) {
|
||||
free_iso_urbs(tpriv);
|
||||
@ -2050,10 +2106,10 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer)
|
||||
urbs[i] = urb;
|
||||
|
||||
/* populate packet lengths */
|
||||
for (j = 0, k = packet_offset - urb_packet_offset;
|
||||
k < packet_offset; k++, j++) {
|
||||
packet_len = transfer->iso_packet_desc[k].length;
|
||||
urb->iso_frame_desc[j].length = packet_len;
|
||||
for (k = 0; k < num_packets_in_urb; j++, k++) {
|
||||
packet_len = transfer->iso_packet_desc[j].length;
|
||||
urb->buffer_length += packet_len;
|
||||
urb->iso_frame_desc[k].length = packet_len;
|
||||
}
|
||||
|
||||
urb->usercontext = itransfer;
|
||||
@ -2061,8 +2117,11 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer)
|
||||
/* FIXME: interface for non-ASAP data? */
|
||||
urb->flags = USBFS_URB_ISO_ASAP;
|
||||
urb->endpoint = transfer->endpoint;
|
||||
urb->number_of_packets = urb_packet_offset;
|
||||
urb->buffer = urb_buffer_orig;
|
||||
urb->number_of_packets = num_packets_in_urb;
|
||||
urb->buffer = urb_buffer;
|
||||
|
||||
urb_buffer += urb->buffer_length;
|
||||
num_packets_remaining -= num_packets_in_urb;
|
||||
}
|
||||
|
||||
/* submit URBs */
|
||||
@ -2075,6 +2134,10 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer)
|
||||
usbi_warn(TRANSFER_CTX(transfer),
|
||||
"submiturb failed, transfer too large");
|
||||
r = LIBUSB_ERROR_INVALID_PARAM;
|
||||
} else if (errno == EMSGSIZE) {
|
||||
usbi_warn(TRANSFER_CTX(transfer),
|
||||
"submiturb failed, iso packet length too large");
|
||||
r = LIBUSB_ERROR_INVALID_PARAM;
|
||||
} else {
|
||||
usbi_err(TRANSFER_CTX(transfer),
|
||||
"submiturb failed error %d errno=%d", r, errno);
|
||||
@ -2213,7 +2276,6 @@ static void op_clear_transfer_priv(struct usbi_transfer *itransfer)
|
||||
USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
|
||||
struct linux_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer);
|
||||
|
||||
/* urbs can be freed also in submit_transfer so lock mutex first */
|
||||
switch (transfer->type) {
|
||||
case LIBUSB_TRANSFER_TYPE_CONTROL:
|
||||
case LIBUSB_TRANSFER_TYPE_BULK:
|
||||
@ -2685,7 +2747,7 @@ static clockid_t op_get_timerfd_clockid(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
const struct usbi_os_backend linux_usbfs_backend = {
|
||||
const struct usbi_os_backend usbi_backend = {
|
||||
.name = "Linux usbfs",
|
||||
.caps = USBI_CAP_HAS_HID_ACCESS|USBI_CAP_SUPPORTS_DETACH_KERNEL_DRIVER,
|
||||
.init = op_init,
|
@ -81,10 +81,11 @@ struct usbfs_iso_packet_desc {
|
||||
unsigned int status;
|
||||
};
|
||||
|
||||
#define MAX_ISO_BUFFER_LENGTH 49152 * 128
|
||||
#define MAX_BULK_BUFFER_LENGTH 16384
|
||||
#define MAX_CTRL_BUFFER_LENGTH 4096
|
||||
|
||||
#define MAX_ISO_PACKETS_PER_URB 128
|
||||
|
||||
struct usbfs_urb {
|
||||
unsigned char type;
|
||||
unsigned char endpoint;
|
@ -86,11 +86,12 @@ static int _sync_control_transfer(struct usbi_transfer *);
|
||||
static int _sync_gen_transfer(struct usbi_transfer *);
|
||||
static int _access_endpoint(struct libusb_transfer *);
|
||||
|
||||
const struct usbi_os_backend netbsd_backend = {
|
||||
const struct usbi_os_backend usbi_backend = {
|
||||
"Synchronous NetBSD backend",
|
||||
0,
|
||||
NULL, /* init() */
|
||||
NULL, /* exit() */
|
||||
NULL, /* set_option() */
|
||||
netbsd_get_device_list,
|
||||
NULL, /* hotplug_poll */
|
||||
netbsd_open,
|
||||
@ -131,6 +132,7 @@ const struct usbi_os_backend netbsd_backend = {
|
||||
netbsd_handle_transfer_completion,
|
||||
|
||||
netbsd_clock_gettime,
|
||||
0, /* context_priv_size */
|
||||
sizeof(struct device_priv),
|
||||
sizeof(struct handle_priv),
|
||||
0, /* transfer_priv_size */
|
||||
@ -212,7 +214,6 @@ error:
|
||||
int
|
||||
netbsd_open(struct libusb_device_handle *handle)
|
||||
{
|
||||
struct handle_priv *hpriv = (struct handle_priv *)handle->os_priv;
|
||||
struct device_priv *dpriv = (struct device_priv *)handle->dev->os_priv;
|
||||
|
||||
dpriv->fd = open(dpriv->devnode, O_RDWR);
|
||||
@ -230,7 +231,6 @@ netbsd_open(struct libusb_device_handle *handle)
|
||||
void
|
||||
netbsd_close(struct libusb_device_handle *handle)
|
||||
{
|
||||
struct handle_priv *hpriv = (struct handle_priv *)handle->os_priv;
|
||||
struct device_priv *dpriv = (struct device_priv *)handle->dev->os_priv;
|
||||
|
||||
usbi_dbg("close: fd %d", dpriv->fd);
|
@ -89,11 +89,12 @@ static int _access_endpoint(struct libusb_transfer *);
|
||||
static int _bus_open(int);
|
||||
|
||||
|
||||
const struct usbi_os_backend openbsd_backend = {
|
||||
const struct usbi_os_backend usbi_backend = {
|
||||
"Synchronous OpenBSD backend",
|
||||
0,
|
||||
NULL, /* init() */
|
||||
NULL, /* exit() */
|
||||
NULL, /* set_option() */
|
||||
obsd_get_device_list,
|
||||
NULL, /* hotplug_poll */
|
||||
obsd_open,
|
||||
@ -134,6 +135,7 @@ const struct usbi_os_backend openbsd_backend = {
|
||||
obsd_handle_transfer_completion,
|
||||
|
||||
obsd_clock_gettime,
|
||||
0, /* context_priv_size */
|
||||
sizeof(struct device_priv),
|
||||
sizeof(struct handle_priv),
|
||||
0, /* transfer_priv_size */
|
||||
@ -246,7 +248,6 @@ obsd_get_device_list(struct libusb_context * ctx,
|
||||
int
|
||||
obsd_open(struct libusb_device_handle *handle)
|
||||
{
|
||||
struct handle_priv *hpriv = (struct handle_priv *)handle->os_priv;
|
||||
struct device_priv *dpriv = (struct device_priv *)handle->dev->os_priv;
|
||||
char devnode[16];
|
||||
|
||||
@ -270,7 +271,6 @@ obsd_open(struct libusb_device_handle *handle)
|
||||
void
|
||||
obsd_close(struct libusb_device_handle *handle)
|
||||
{
|
||||
struct handle_priv *hpriv = (struct handle_priv *)handle->os_priv;
|
||||
struct device_priv *dpriv = (struct device_priv *)handle->dev->os_priv;
|
||||
|
||||
if (dpriv->devname) {
|
@ -29,25 +29,56 @@
|
||||
|
||||
int usbi_pipe(int pipefd[2])
|
||||
{
|
||||
#if defined(HAVE_PIPE2)
|
||||
int ret = pipe2(pipefd, O_CLOEXEC);
|
||||
#else
|
||||
int ret = pipe(pipefd);
|
||||
#endif
|
||||
|
||||
if (ret != 0) {
|
||||
usbi_err(NULL, "failed to create pipe (%d)", errno);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if !defined(HAVE_PIPE2) && defined(FD_CLOEXEC)
|
||||
ret = fcntl(pipefd[0], F_GETFD);
|
||||
if (ret == -1) {
|
||||
usbi_err(NULL, "failed to get pipe fd flags (%d)", errno);
|
||||
goto err_close_pipe;
|
||||
}
|
||||
ret = fcntl(pipefd[0], F_SETFD, ret | FD_CLOEXEC);
|
||||
if (ret == -1) {
|
||||
usbi_err(NULL, "failed to set pipe fd flags (%d)", errno);
|
||||
goto err_close_pipe;
|
||||
}
|
||||
|
||||
ret = fcntl(pipefd[1], F_GETFD);
|
||||
if (ret == -1) {
|
||||
usbi_err(NULL, "failed to get pipe fd flags (%d)", errno);
|
||||
goto err_close_pipe;
|
||||
}
|
||||
ret = fcntl(pipefd[1], F_SETFD, ret | FD_CLOEXEC);
|
||||
if (ret == -1) {
|
||||
usbi_err(NULL, "failed to set pipe fd flags (%d)", errno);
|
||||
goto err_close_pipe;
|
||||
}
|
||||
#endif
|
||||
|
||||
ret = fcntl(pipefd[1], F_GETFL);
|
||||
if (ret == -1) {
|
||||
usbi_dbg("Failed to get pipe fd flags: %d", errno);
|
||||
usbi_err(NULL, "failed to get pipe fd status flags (%d)", errno);
|
||||
goto err_close_pipe;
|
||||
}
|
||||
ret = fcntl(pipefd[1], F_SETFL, ret | O_NONBLOCK);
|
||||
if (ret != 0) {
|
||||
usbi_dbg("Failed to set non-blocking on new pipe: %d", errno);
|
||||
if (ret == -1) {
|
||||
usbi_err(NULL, "failed to set pipe fd status flags (%d)", errno);
|
||||
goto err_close_pipe;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_close_pipe:
|
||||
usbi_close(pipefd[0]);
|
||||
usbi_close(pipefd[1]);
|
||||
close(pipefd[0]);
|
||||
close(pipefd[1]);
|
||||
return ret;
|
||||
}
|
364
vendor/github.com/karalabe/usb/libusb/libusb/os/poll_windows.c
generated
vendored
Normal file
364
vendor/github.com/karalabe/usb/libusb/libusb/os/poll_windows.c
generated
vendored
Normal 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;
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
* Windows compat: POSIX compatibility wrapper
|
||||
* Copyright © 2012-2013 RealVNC Ltd.
|
||||
* Copyright © 2009-2010 Pete Batard <pete@akeo.ie>
|
||||
* Copyright © 2016-2018 Chris Dickens <christopher.a.dickens@gmail.com>
|
||||
* With contributions from Michael Plante, Orin Eman et al.
|
||||
* Parts of poll implementation from libusb-win32, by Stephan Meyer et al.
|
||||
*
|
||||
@ -40,21 +41,6 @@
|
||||
|
||||
#define DUMMY_HANDLE ((HANDLE)(LONG_PTR)-2)
|
||||
|
||||
/* Windows versions */
|
||||
enum windows_version {
|
||||
WINDOWS_CE = -2,
|
||||
WINDOWS_UNDEFINED = -1,
|
||||
WINDOWS_UNSUPPORTED = 0,
|
||||
WINDOWS_XP = 0x51,
|
||||
WINDOWS_2003 = 0x52, // Also XP x64
|
||||
WINDOWS_VISTA = 0x60,
|
||||
WINDOWS_7 = 0x61,
|
||||
WINDOWS_8 = 0x62,
|
||||
WINDOWS_8_1_OR_LATER = 0x63,
|
||||
WINDOWS_MAX
|
||||
};
|
||||
extern int windows_version;
|
||||
|
||||
#define MAX_FDS 256
|
||||
|
||||
#define POLLIN 0x0001 /* There is data to read */
|
||||
@ -70,41 +56,21 @@ struct pollfd {
|
||||
short revents; /* returned events */
|
||||
};
|
||||
|
||||
// access modes
|
||||
enum rw_type {
|
||||
RW_NONE,
|
||||
RW_READ,
|
||||
RW_WRITE,
|
||||
};
|
||||
|
||||
// fd struct that can be used for polling on Windows
|
||||
typedef int cancel_transfer(struct usbi_transfer *itransfer);
|
||||
|
||||
struct winfd {
|
||||
int fd; // what's exposed to libusb core
|
||||
HANDLE handle; // what we need to attach overlapped to the I/O op, so we can poll it
|
||||
OVERLAPPED *overlapped; // what will report our I/O status
|
||||
struct usbi_transfer *itransfer; // Associated transfer, or NULL if completed
|
||||
cancel_transfer *cancel_fn; // Function pointer to cancel transfer API
|
||||
enum rw_type rw; // I/O transfer direction: read *XOR* write (NOT BOTH)
|
||||
};
|
||||
|
||||
extern const struct winfd INVALID_WINFD;
|
||||
|
||||
struct winfd usbi_create_fd(void);
|
||||
|
||||
int usbi_pipe(int pipefd[2]);
|
||||
int usbi_poll(struct pollfd *fds, unsigned int nfds, int timeout);
|
||||
ssize_t usbi_write(int fd, const void *buf, size_t count);
|
||||
ssize_t usbi_read(int fd, void *buf, size_t count);
|
||||
int usbi_close(int fd);
|
||||
|
||||
void init_polling(void);
|
||||
void exit_polling(void);
|
||||
struct winfd usbi_create_fd(HANDLE handle, int access_mode,
|
||||
struct usbi_transfer *transfer, cancel_transfer *cancel_fn);
|
||||
void usbi_free_fd(struct winfd* winfd);
|
||||
struct winfd fd_to_winfd(int fd);
|
||||
struct winfd handle_to_winfd(HANDLE handle);
|
||||
struct winfd overlapped_to_winfd(OVERLAPPED* overlapped);
|
||||
|
||||
/*
|
||||
* Timeval operations
|
||||
*/
|
@ -21,6 +21,7 @@
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/list.h>
|
||||
#include <sys/stat.h>
|
||||
#include <strings.h>
|
||||
#include <errno.h>
|
||||
@ -28,21 +29,34 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <wait.h>
|
||||
#include <unistd.h>
|
||||
#include <aio.h>
|
||||
#include <libdevinfo.h>
|
||||
#include <sys/nvpair.h>
|
||||
#include <sys/devctl.h>
|
||||
#include <sys/usb/clients/ugen/usb_ugen.h>
|
||||
#include <errno.h>
|
||||
#include <sys/usb/usba.h>
|
||||
#include <sys/pci.h>
|
||||
|
||||
#include "libusbi.h"
|
||||
#include "sunos_usb.h"
|
||||
|
||||
#define UPDATEDRV_PATH "/usr/sbin/update_drv"
|
||||
#define UPDATEDRV "update_drv"
|
||||
|
||||
typedef list_t string_list_t;
|
||||
typedef struct string_node {
|
||||
char *string;
|
||||
list_node_t link;
|
||||
} string_node_t;
|
||||
|
||||
/*
|
||||
* Backend functions
|
||||
*/
|
||||
static int sunos_init(struct libusb_context *);
|
||||
static void sunos_exit(void);
|
||||
static void sunos_exit(struct libusb_context *);
|
||||
static int sunos_get_device_list(struct libusb_context *,
|
||||
struct discovered_devs **);
|
||||
static int sunos_open(struct libusb_device_handle *);
|
||||
@ -67,6 +81,162 @@ static int sunos_cancel_transfer(struct usbi_transfer *);
|
||||
static void sunos_clear_transfer_priv(struct usbi_transfer *);
|
||||
static int sunos_handle_transfer_completion(struct usbi_transfer *);
|
||||
static int sunos_clock_gettime(int, struct timespec *);
|
||||
static int sunos_kernel_driver_active(struct libusb_device_handle *, int interface);
|
||||
static int sunos_detach_kernel_driver (struct libusb_device_handle *dev, int interface_number);
|
||||
static int sunos_attach_kernel_driver (struct libusb_device_handle *dev, int interface_number);
|
||||
static int sunos_usb_open_ep0(sunos_dev_handle_priv_t *hpriv, sunos_dev_priv_t *dpriv);
|
||||
static int sunos_usb_ioctl(struct libusb_device *dev, int cmd);
|
||||
|
||||
static struct devctl_iocdata iocdata;
|
||||
static int sunos_get_link(di_devlink_t devlink, void *arg)
|
||||
{
|
||||
walk_link_t *larg = (walk_link_t *)arg;
|
||||
const char *p;
|
||||
const char *q;
|
||||
|
||||
if (larg->path) {
|
||||
char *content = (char *)di_devlink_content(devlink);
|
||||
char *start = strstr(content, "/devices/");
|
||||
start += strlen("/devices");
|
||||
usbi_dbg("%s", start);
|
||||
|
||||
/* line content must have minor node */
|
||||
if (start == NULL ||
|
||||
strncmp(start, larg->path, larg->len) != 0 ||
|
||||
start[larg->len] != ':')
|
||||
return (DI_WALK_CONTINUE);
|
||||
}
|
||||
|
||||
p = di_devlink_path(devlink);
|
||||
q = strrchr(p, '/');
|
||||
usbi_dbg("%s", q);
|
||||
|
||||
*(larg->linkpp) = strndup(p, strlen(p) - strlen(q));
|
||||
|
||||
return (DI_WALK_TERMINATE);
|
||||
}
|
||||
|
||||
|
||||
static int sunos_physpath_to_devlink(
|
||||
const char *node_path, const char *match, char **link_path)
|
||||
{
|
||||
walk_link_t larg;
|
||||
di_devlink_handle_t hdl;
|
||||
|
||||
*link_path = NULL;
|
||||
larg.linkpp = link_path;
|
||||
if ((hdl = di_devlink_init(NULL, 0)) == NULL) {
|
||||
usbi_dbg("di_devlink_init failure");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
larg.len = strlen(node_path);
|
||||
larg.path = (char *)node_path;
|
||||
|
||||
(void) di_devlink_walk(hdl, match, NULL, DI_PRIMARY_LINK,
|
||||
(void *)&larg, sunos_get_link);
|
||||
|
||||
(void) di_devlink_fini(&hdl);
|
||||
|
||||
if (*link_path == NULL) {
|
||||
usbi_dbg("there is no devlink for this path");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
sunos_usb_ioctl(struct libusb_device *dev, int cmd)
|
||||
{
|
||||
int fd;
|
||||
nvlist_t *nvlist;
|
||||
char *end;
|
||||
char *phypath;
|
||||
char *hubpath;
|
||||
char path_arg[PATH_MAX];
|
||||
sunos_dev_priv_t *dpriv;
|
||||
devctl_ap_state_t devctl_ap_state;
|
||||
|
||||
dpriv = (sunos_dev_priv_t *)dev->os_priv;
|
||||
phypath = dpriv->phypath;
|
||||
|
||||
end = strrchr(phypath, '/');
|
||||
if (end == NULL)
|
||||
return (-1);
|
||||
hubpath = strndup(phypath, end - phypath);
|
||||
if (hubpath == NULL)
|
||||
return (-1);
|
||||
|
||||
end = strrchr(hubpath, '@');
|
||||
if (end == NULL) {
|
||||
free(hubpath);
|
||||
return (-1);
|
||||
}
|
||||
end++;
|
||||
usbi_dbg("unitaddr: %s", end);
|
||||
|
||||
nvlist_alloc(&nvlist, NV_UNIQUE_NAME_TYPE, KM_NOSLEEP);
|
||||
nvlist_add_int32(nvlist, "port", dev->port_number);
|
||||
//find the hub path
|
||||
snprintf(path_arg, sizeof(path_arg), "/devices%s:hubd", hubpath);
|
||||
usbi_dbg("ioctl hub path: %s", path_arg);
|
||||
|
||||
fd = open(path_arg, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
usbi_err(DEVICE_CTX(dev), "open failed: %d (%s)", errno, strerror(errno));
|
||||
nvlist_free(nvlist);
|
||||
free(hubpath);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
memset(&iocdata, 0, sizeof(iocdata));
|
||||
memset(&devctl_ap_state, 0, sizeof(devctl_ap_state));
|
||||
|
||||
nvlist_pack(nvlist, (char **)&iocdata.nvl_user, &iocdata.nvl_usersz, NV_ENCODE_NATIVE, 0);
|
||||
|
||||
iocdata.cmd = DEVCTL_AP_GETSTATE;
|
||||
iocdata.flags = 0;
|
||||
iocdata.c_nodename = "hub";
|
||||
iocdata.c_unitaddr = end;
|
||||
iocdata.cpyout_buf = &devctl_ap_state;
|
||||
usbi_dbg("%p, %d", iocdata.nvl_user, iocdata.nvl_usersz);
|
||||
|
||||
errno = 0;
|
||||
if (ioctl(fd, DEVCTL_AP_GETSTATE, &iocdata) == -1) {
|
||||
usbi_err(DEVICE_CTX(dev), "ioctl failed: fd %d, cmd %x, errno %d (%s)",
|
||||
fd, DEVCTL_AP_GETSTATE, errno, strerror(errno));
|
||||
} else {
|
||||
usbi_dbg("dev rstate: %d", devctl_ap_state.ap_rstate);
|
||||
usbi_dbg("dev ostate: %d", devctl_ap_state.ap_ostate);
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
iocdata.cmd = cmd;
|
||||
if (ioctl(fd, (int)cmd, &iocdata) != 0) {
|
||||
usbi_err(DEVICE_CTX(dev), "ioctl failed: fd %d, cmd %x, errno %d (%s)",
|
||||
fd, cmd, errno, strerror(errno));
|
||||
sleep(2);
|
||||
}
|
||||
|
||||
close(fd);
|
||||
free(iocdata.nvl_user);
|
||||
nvlist_free(nvlist);
|
||||
free(hubpath);
|
||||
|
||||
return (-errno);
|
||||
}
|
||||
|
||||
static int
|
||||
sunos_kernel_driver_active(struct libusb_device_handle *dev, int interface)
|
||||
{
|
||||
sunos_dev_priv_t *dpriv;
|
||||
dpriv = (sunos_dev_priv_t *)dev->dev->os_priv;
|
||||
|
||||
usbi_dbg("%s", dpriv->ugenpath);
|
||||
|
||||
return (dpriv->ugenpath == NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Private functions
|
||||
@ -79,11 +249,229 @@ static int sunos_init(struct libusb_context *ctx)
|
||||
return (LIBUSB_SUCCESS);
|
||||
}
|
||||
|
||||
static void sunos_exit(void)
|
||||
static void sunos_exit(struct libusb_context *ctx)
|
||||
{
|
||||
usbi_dbg("");
|
||||
}
|
||||
|
||||
static string_list_t *
|
||||
sunos_new_string_list(void)
|
||||
{
|
||||
string_list_t *list;
|
||||
|
||||
list = calloc(1, sizeof(*list));
|
||||
if (list != NULL)
|
||||
list_create(list, sizeof(string_node_t),
|
||||
offsetof(string_node_t, link));
|
||||
|
||||
return (list);
|
||||
}
|
||||
|
||||
static int
|
||||
sunos_append_to_string_list(string_list_t *list, const char *arg)
|
||||
{
|
||||
string_node_t *np;
|
||||
|
||||
np = calloc(1, sizeof(*np));
|
||||
if (!np)
|
||||
return (-1);
|
||||
|
||||
np->string = strdup(arg);
|
||||
if (!np->string) {
|
||||
free(np);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
list_insert_tail(list, np);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
sunos_free_string_list(string_list_t *list)
|
||||
{
|
||||
string_node_t *np;
|
||||
|
||||
while ((np = list_remove_head(list)) != NULL) {
|
||||
free(np->string);
|
||||
free(np);
|
||||
}
|
||||
|
||||
free(list);
|
||||
}
|
||||
|
||||
static char **
|
||||
sunos_build_argv_list(string_list_t *list)
|
||||
{
|
||||
char **argv_list;
|
||||
string_node_t *np;
|
||||
int n;
|
||||
|
||||
n = 1; /* Start at 1 for NULL terminator */
|
||||
for (np = list_head(list); np != NULL; np = list_next(list, np))
|
||||
n++;
|
||||
|
||||
argv_list = calloc(n, sizeof(char *));
|
||||
if (argv_list == NULL)
|
||||
return NULL;
|
||||
|
||||
n = 0;
|
||||
for (np = list_head(list); np != NULL; np = list_next(list, np))
|
||||
argv_list[n++] = np->string;
|
||||
|
||||
return (argv_list);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
sunos_exec_command(struct libusb_context *ctx, const char *path,
|
||||
string_list_t *list)
|
||||
{
|
||||
pid_t pid;
|
||||
int status;
|
||||
int waitstat;
|
||||
int exit_status;
|
||||
char **argv_list;
|
||||
|
||||
argv_list = sunos_build_argv_list(list);
|
||||
if (argv_list == NULL)
|
||||
return (-1);
|
||||
|
||||
pid = fork();
|
||||
if (pid == 0) {
|
||||
/* child */
|
||||
execv(path, argv_list);
|
||||
_exit(127);
|
||||
} else if (pid > 0) {
|
||||
/* parent */
|
||||
do {
|
||||
waitstat = waitpid(pid, &status, 0);
|
||||
} while ((waitstat == -1 && errno == EINTR) ||
|
||||
(waitstat == 0 && !WIFEXITED(status) && !WIFSIGNALED(status)));
|
||||
|
||||
if (waitstat == 0) {
|
||||
if (WIFEXITED(status))
|
||||
exit_status = WEXITSTATUS(status);
|
||||
else
|
||||
exit_status = WTERMSIG(status);
|
||||
} else {
|
||||
usbi_err(ctx, "waitpid failed: errno %d (%s)", errno, strerror(errno));
|
||||
exit_status = -1;
|
||||
}
|
||||
} else {
|
||||
/* fork failed */
|
||||
usbi_err(ctx, "fork failed: errno %d (%s)", errno, strerror(errno));
|
||||
exit_status = -1;
|
||||
}
|
||||
|
||||
free(argv_list);
|
||||
|
||||
return (exit_status);
|
||||
}
|
||||
|
||||
static int
|
||||
sunos_detach_kernel_driver(struct libusb_device_handle *dev_handle,
|
||||
int interface_number)
|
||||
{
|
||||
struct libusb_context *ctx = HANDLE_CTX(dev_handle);
|
||||
string_list_t *list;
|
||||
char path_arg[PATH_MAX];
|
||||
sunos_dev_priv_t *dpriv;
|
||||
int r;
|
||||
|
||||
dpriv = (sunos_dev_priv_t *)dev_handle->dev->os_priv;
|
||||
snprintf(path_arg, sizeof(path_arg), "\'\"%s\"\'", dpriv->phypath);
|
||||
usbi_dbg("%s", path_arg);
|
||||
|
||||
list = sunos_new_string_list();
|
||||
if (list == NULL)
|
||||
return (LIBUSB_ERROR_NO_MEM);
|
||||
|
||||
/* attach ugen driver */
|
||||
r = 0;
|
||||
r |= sunos_append_to_string_list(list, UPDATEDRV);
|
||||
r |= sunos_append_to_string_list(list, "-a"); /* add rule */
|
||||
r |= sunos_append_to_string_list(list, "-i"); /* specific device */
|
||||
r |= sunos_append_to_string_list(list, path_arg); /* physical path */
|
||||
r |= sunos_append_to_string_list(list, "ugen");
|
||||
if (r) {
|
||||
sunos_free_string_list(list);
|
||||
return (LIBUSB_ERROR_NO_MEM);
|
||||
}
|
||||
|
||||
r = sunos_exec_command(ctx, UPDATEDRV_PATH, list);
|
||||
sunos_free_string_list(list);
|
||||
if (r < 0)
|
||||
return (LIBUSB_ERROR_OTHER);
|
||||
|
||||
/* reconfigure the driver node */
|
||||
r = 0;
|
||||
r |= sunos_usb_ioctl(dev_handle->dev, DEVCTL_AP_DISCONNECT);
|
||||
r |= sunos_usb_ioctl(dev_handle->dev, DEVCTL_AP_CONFIGURE);
|
||||
if (r)
|
||||
usbi_warn(HANDLE_CTX(dev_handle), "one or more ioctls failed");
|
||||
|
||||
snprintf(path_arg, sizeof(path_arg), "^usb/%x.%x", dpriv->dev_descr.idVendor,
|
||||
dpriv->dev_descr.idProduct);
|
||||
sunos_physpath_to_devlink(dpriv->phypath, path_arg, &dpriv->ugenpath);
|
||||
|
||||
if (access(dpriv->ugenpath, F_OK) == -1) {
|
||||
usbi_err(HANDLE_CTX(dev_handle), "fail to detach kernel driver");
|
||||
return (LIBUSB_ERROR_IO);
|
||||
}
|
||||
|
||||
return sunos_usb_open_ep0((sunos_dev_handle_priv_t *)dev_handle->os_priv, dpriv);
|
||||
}
|
||||
|
||||
static int
|
||||
sunos_attach_kernel_driver(struct libusb_device_handle *dev_handle,
|
||||
int interface_number)
|
||||
{
|
||||
struct libusb_context *ctx = HANDLE_CTX(dev_handle);
|
||||
string_list_t *list;
|
||||
char path_arg[PATH_MAX];
|
||||
sunos_dev_priv_t *dpriv;
|
||||
int r;
|
||||
|
||||
/* we open the dev in detach driver, so we need close it first. */
|
||||
sunos_close(dev_handle);
|
||||
|
||||
dpriv = (sunos_dev_priv_t *)dev_handle->dev->os_priv;
|
||||
snprintf(path_arg, sizeof(path_arg), "\'\"%s\"\'", dpriv->phypath);
|
||||
usbi_dbg("%s", path_arg);
|
||||
|
||||
list = sunos_new_string_list();
|
||||
if (list == NULL)
|
||||
return (LIBUSB_ERROR_NO_MEM);
|
||||
|
||||
/* detach ugen driver */
|
||||
r = 0;
|
||||
r |= sunos_append_to_string_list(list, UPDATEDRV);
|
||||
r |= sunos_append_to_string_list(list, "-d"); /* add rule */
|
||||
r |= sunos_append_to_string_list(list, "-i"); /* specific device */
|
||||
r |= sunos_append_to_string_list(list, path_arg); /* physical path */
|
||||
r |= sunos_append_to_string_list(list, "ugen");
|
||||
if (r) {
|
||||
sunos_free_string_list(list);
|
||||
return (LIBUSB_ERROR_NO_MEM);
|
||||
}
|
||||
|
||||
r = sunos_exec_command(ctx, UPDATEDRV_PATH, list);
|
||||
sunos_free_string_list(list);
|
||||
if (r < 0)
|
||||
return (LIBUSB_ERROR_OTHER);
|
||||
|
||||
/* reconfigure the driver node */
|
||||
r = 0;
|
||||
r |= sunos_usb_ioctl(dev_handle->dev, DEVCTL_AP_CONFIGURE);
|
||||
r |= sunos_usb_ioctl(dev_handle->dev, DEVCTL_AP_DISCONNECT);
|
||||
r |= sunos_usb_ioctl(dev_handle->dev, DEVCTL_AP_CONFIGURE);
|
||||
if (r)
|
||||
usbi_warn(HANDLE_CTX(dev_handle), "one or more ioctls failed");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
sunos_fill_in_dev_info(di_node_t node, struct libusb_device *dev)
|
||||
{
|
||||
@ -93,6 +481,7 @@ sunos_fill_in_dev_info(di_node_t node, struct libusb_device *dev)
|
||||
uint8_t *rdata;
|
||||
struct libusb_device_descriptor *descr;
|
||||
sunos_dev_priv_t *dpriv = (sunos_dev_priv_t *)dev->os_priv;
|
||||
char match_str[PATH_MAX];
|
||||
|
||||
/* Device descriptors */
|
||||
proplen = di_prop_lookup_bytes(DDI_DEV_T_ANY, node,
|
||||
@ -137,7 +526,11 @@ sunos_fill_in_dev_info(di_node_t node, struct libusb_device *dev)
|
||||
phypath = di_devfs_path(node);
|
||||
if (phypath) {
|
||||
dpriv->phypath = strdup(phypath);
|
||||
snprintf(match_str, sizeof(match_str), "^usb/%x.%x", dpriv->dev_descr.idVendor, dpriv->dev_descr.idProduct);
|
||||
usbi_dbg("match is %s", match_str);
|
||||
sunos_physpath_to_devlink(dpriv->phypath, match_str, &dpriv->ugenpath);
|
||||
di_devfs_path_free(phypath);
|
||||
|
||||
} else {
|
||||
free(dpriv->raw_cfgdescr);
|
||||
|
||||
@ -170,97 +563,88 @@ sunos_fill_in_dev_info(di_node_t node, struct libusb_device *dev)
|
||||
return (LIBUSB_SUCCESS);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
sunos_add_devices(di_devlink_t link, void *arg)
|
||||
{
|
||||
struct devlink_cbarg *largs = (struct devlink_cbarg *)arg;
|
||||
struct node_args *nargs;
|
||||
di_node_t myself, pnode;
|
||||
di_node_t myself, dn;
|
||||
uint64_t session_id = 0;
|
||||
uint16_t bdf = 0;
|
||||
uint64_t sid = 0;
|
||||
uint64_t bdf = 0;
|
||||
struct libusb_device *dev;
|
||||
sunos_dev_priv_t *devpriv;
|
||||
const char *path, *newpath;
|
||||
int n, i;
|
||||
int n;
|
||||
int i = 0;
|
||||
int *addr_prop;
|
||||
uint8_t bus_number = 0;
|
||||
|
||||
nargs = (struct node_args *)largs->nargs;
|
||||
myself = largs->myself;
|
||||
if (nargs->last_ugenpath) {
|
||||
/* the same node's links */
|
||||
return (DI_WALK_CONTINUE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Construct session ID.
|
||||
* session ID = ...parent hub addr|hub addr|dev addr.
|
||||
*/
|
||||
pnode = myself;
|
||||
i = 0;
|
||||
while (pnode != DI_NODE_NIL) {
|
||||
if (di_prop_exists(DDI_DEV_T_ANY, pnode, "root-hub") == 1) {
|
||||
/* walk to root */
|
||||
uint32_t * regbuf = NULL;
|
||||
uint32_t reg;
|
||||
|
||||
n = di_prop_lookup_ints(DDI_DEV_T_ANY, pnode, "reg",
|
||||
(int **)®buf);
|
||||
reg = regbuf[0];
|
||||
bdf = (PCI_REG_BUS_G(reg) << 8) |
|
||||
(PCI_REG_DEV_G(reg) << 3) | PCI_REG_FUNC_G(reg);
|
||||
session_id |= (bdf << i * 8);
|
||||
nargs = (struct node_args *)largs->nargs;
|
||||
myself = largs->myself;
|
||||
|
||||
/* same as 'unit-address' property */
|
||||
bus_number =
|
||||
(PCI_REG_DEV_G(reg) << 3) | PCI_REG_FUNC_G(reg);
|
||||
/*
|
||||
* Construct session ID.
|
||||
* session ID = dev_addr | hub addr |parent hub addr|...|root hub bdf
|
||||
* 8 bits 8bits 8 bits 16bits
|
||||
*/
|
||||
if (myself == DI_NODE_NIL)
|
||||
return (DI_WALK_CONTINUE);
|
||||
|
||||
usbi_dbg("device bus address=%s:%x",
|
||||
di_bus_addr(pnode), bus_number);
|
||||
|
||||
break;
|
||||
dn = myself;
|
||||
/* find the root hub */
|
||||
while (di_prop_exists(DDI_DEV_T_ANY, dn, "root-hub") != 1) {
|
||||
usbi_dbg("find_root_hub:%s", di_devfs_path(dn));
|
||||
n = di_prop_lookup_ints(DDI_DEV_T_ANY, dn,
|
||||
"assigned-address", &addr_prop);
|
||||
session_id |= ((addr_prop[0] & 0xff) << i++ * 8);
|
||||
dn = di_parent_node(dn);
|
||||
}
|
||||
|
||||
/* dn is the root hub node */
|
||||
n = di_prop_lookup_ints(DDI_DEV_T_ANY, dn, "reg", (int **)®buf);
|
||||
reg = regbuf[0];
|
||||
bdf = (PCI_REG_BUS_G(reg) << 8) | (PCI_REG_DEV_G(reg) << 3) | PCI_REG_FUNC_G(reg);
|
||||
/* bdf must larger than i*8 bits */
|
||||
session_id |= (bdf << i * 8);
|
||||
bus_number = (PCI_REG_DEV_G(reg) << 3) | PCI_REG_FUNC_G(reg);
|
||||
|
||||
usbi_dbg("device bus address=%s:%x, name:%s",
|
||||
di_bus_addr(myself), bus_number, di_node_name(dn));
|
||||
usbi_dbg("session id org:%lx", session_id);
|
||||
|
||||
/* dn is the usb device */
|
||||
for (dn = di_child_node(myself); dn != DI_NODE_NIL; dn = di_sibling_node(dn)) {
|
||||
usbi_dbg("device path:%s", di_devfs_path(dn));
|
||||
/* skip hub devices, because its driver can not been unload */
|
||||
if (di_prop_lookup_ints(DDI_DEV_T_ANY, dn, "usb-port-count", &addr_prop) != -1)
|
||||
continue;
|
||||
/* usb_addr */
|
||||
n = di_prop_lookup_ints(DDI_DEV_T_ANY, pnode,
|
||||
n = di_prop_lookup_ints(DDI_DEV_T_ANY, dn,
|
||||
"assigned-address", &addr_prop);
|
||||
if ((n != 1) || (addr_prop[0] == 0)) {
|
||||
usbi_dbg("cannot get valid usb_addr");
|
||||
|
||||
return (DI_WALK_CONTINUE);
|
||||
continue;
|
||||
}
|
||||
|
||||
session_id |= ((addr_prop[0] & 0xff) << i * 8);
|
||||
if (++i > 7)
|
||||
break;
|
||||
sid = (session_id << 8) | (addr_prop[0] & 0xff) ;
|
||||
usbi_dbg("session id %lx", sid);
|
||||
|
||||
pnode = di_parent_node(pnode);
|
||||
}
|
||||
|
||||
path = di_devlink_path(link);
|
||||
dev = usbi_get_device_by_session_id(nargs->ctx, session_id);
|
||||
dev = usbi_get_device_by_session_id(nargs->ctx, sid);
|
||||
if (dev == NULL) {
|
||||
dev = usbi_alloc_device(nargs->ctx, session_id);
|
||||
dev = usbi_alloc_device(nargs->ctx, sid);
|
||||
if (dev == NULL) {
|
||||
usbi_dbg("can't alloc device");
|
||||
|
||||
return (DI_WALK_TERMINATE);
|
||||
continue;
|
||||
}
|
||||
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) {
|
||||
if (sunos_fill_in_dev_info(dn, dev) != LIBUSB_SUCCESS) {
|
||||
libusb_unref_device(dev);
|
||||
|
||||
return (DI_WALK_TERMINATE);
|
||||
usbi_dbg("get infomation fail");
|
||||
continue;
|
||||
}
|
||||
if (usbi_sanitize_device(dev) < 0) {
|
||||
libusb_unref_device(dev);
|
||||
@ -268,13 +652,9 @@ sunos_add_devices(di_devlink_t link, void *arg)
|
||||
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;
|
||||
usbi_dbg("Dev %s exists", devpriv->ugenpath);
|
||||
}
|
||||
|
||||
if (discovered_devs_append(*(nargs->discdevs), dev) == NULL) {
|
||||
usbi_dbg("cannot append device");
|
||||
@ -285,11 +665,11 @@ sunos_add_devices(di_devlink_t link, void *arg)
|
||||
* hereafter. Front end or app should take care of their ref.
|
||||
*/
|
||||
libusb_unref_device(dev);
|
||||
}
|
||||
|
||||
usbi_dbg("Device %s %s id=0x%llx, devcount:%d, bdf=%x",
|
||||
devpriv->ugenpath, path, (uint64_t)session_id,
|
||||
devpriv->ugenpath, di_devfs_path(dn), (uint64_t)sid,
|
||||
(*nargs->discdevs)->len, bdf);
|
||||
}
|
||||
|
||||
return (DI_WALK_CONTINUE);
|
||||
}
|
||||
@ -303,14 +683,14 @@ sunos_walk_minor_node_link(di_node_t node, void *args)
|
||||
struct node_args *nargs = (struct node_args *)args;
|
||||
di_devlink_handle_t devlink_hdl = nargs->dlink_hdl;
|
||||
|
||||
/* walk each minor to find ugen devices */
|
||||
/* walk each minor to find usb devices */
|
||||
while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
|
||||
minor_path = di_devfs_minor_path(minor);
|
||||
arg.nargs = args;
|
||||
arg.myself = node;
|
||||
arg.minor = minor;
|
||||
(void) di_devlink_walk(devlink_hdl,
|
||||
"^usb/[0-9a-f]+[.][0-9a-f]+", minor_path,
|
||||
"^usb/hub[0-9]+", minor_path,
|
||||
DI_PRIMARY_LINK, (void *)&arg, sunos_add_devices);
|
||||
di_devfs_path_free(minor_path);
|
||||
}
|
||||
@ -496,7 +876,7 @@ sunos_check_device_and_status_open(struct libusb_device_handle *hdl,
|
||||
usbi_dbg("can't find interface for endpoint 0x%02x",
|
||||
ep_addr);
|
||||
|
||||
return (LIBUSB_ERROR_ACCESS);
|
||||
return (EACCES);
|
||||
}
|
||||
|
||||
/* create filename */
|
||||
@ -603,6 +983,11 @@ sunos_open(struct libusb_device_handle *handle)
|
||||
hpriv->eps[i].statfd = -1;
|
||||
}
|
||||
|
||||
if (sunos_kernel_driver_active(handle, 0)) {
|
||||
/* pretend we can open the device */
|
||||
return (LIBUSB_SUCCESS);
|
||||
}
|
||||
|
||||
if ((ret = sunos_usb_open_ep0(hpriv, dpriv)) != LIBUSB_SUCCESS) {
|
||||
usbi_dbg("fail: %d", ret);
|
||||
return (ret);
|
||||
@ -1010,9 +1395,7 @@ void
|
||||
sunos_destroy_device(struct libusb_device *dev)
|
||||
{
|
||||
sunos_dev_priv_t *dpriv = (sunos_dev_priv_t *)dev->os_priv;
|
||||
|
||||
usbi_dbg("");
|
||||
|
||||
usbi_dbg("destroy everyting");
|
||||
free(dpriv->raw_cfgdescr);
|
||||
free(dpriv->ugenpath);
|
||||
free(dpriv->phypath);
|
||||
@ -1254,7 +1637,7 @@ sunos_usb_get_status(int fd)
|
||||
return (status);
|
||||
}
|
||||
|
||||
const struct usbi_os_backend sunos_backend = {
|
||||
const struct usbi_os_backend usbi_backend = {
|
||||
.name = "Solaris",
|
||||
.caps = 0,
|
||||
.init = sunos_init,
|
||||
@ -1276,9 +1659,9 @@ const struct usbi_os_backend sunos_backend = {
|
||||
.reset_device = sunos_reset_device, /* TODO */
|
||||
.alloc_streams = NULL,
|
||||
.free_streams = NULL,
|
||||
.kernel_driver_active = NULL,
|
||||
.detach_kernel_driver = NULL,
|
||||
.attach_kernel_driver = NULL,
|
||||
.kernel_driver_active = sunos_kernel_driver_active,
|
||||
.detach_kernel_driver = sunos_detach_kernel_driver,
|
||||
.attach_kernel_driver = sunos_attach_kernel_driver,
|
||||
.destroy_device = sunos_destroy_device,
|
||||
.submit_transfer = sunos_submit_transfer,
|
||||
.cancel_transfer = sunos_cancel_transfer,
|
@ -65,6 +65,12 @@ struct devlink_cbarg {
|
||||
di_minor_t minor;
|
||||
};
|
||||
|
||||
typedef struct walk_link {
|
||||
char *path;
|
||||
int len;
|
||||
char **linkpp;
|
||||
} walk_link_t;
|
||||
|
||||
/* AIO callback args */
|
||||
struct aio_callback_args{
|
||||
struct libusb_transfer *transfer;
|
@ -29,7 +29,7 @@
|
||||
# include <unistd.h>
|
||||
# include <sys/syscall.h>
|
||||
#elif defined(__APPLE__)
|
||||
# include <mach/mach.h>
|
||||
# include <pthread.h>
|
||||
#elif defined(__CYGWIN__)
|
||||
# include <windows.h>
|
||||
#endif
|
||||
@ -43,7 +43,7 @@ int usbi_cond_timedwait(pthread_cond_t *cond,
|
||||
struct timespec timeout;
|
||||
int r;
|
||||
|
||||
r = usbi_backend->clock_gettime(USBI_CLOCK_REALTIME, &timeout);
|
||||
r = usbi_backend.clock_gettime(USBI_CLOCK_REALTIME, &timeout);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -59,7 +59,7 @@ int usbi_cond_timedwait(pthread_cond_t *cond,
|
||||
|
||||
int usbi_get_tid(void)
|
||||
{
|
||||
int ret = -1;
|
||||
int ret;
|
||||
#if defined(__ANDROID__)
|
||||
ret = gettid();
|
||||
#elif defined(__linux__)
|
||||
@ -69,10 +69,11 @@ int usbi_get_tid(void)
|
||||
real thread support. For 5.1 and earlier, -1 is returned. */
|
||||
ret = syscall(SYS_getthrid);
|
||||
#elif defined(__APPLE__)
|
||||
ret = mach_thread_self();
|
||||
mach_port_deallocate(mach_task_self(), ret);
|
||||
ret = (int)pthread_mach_thread_np(pthread_self());
|
||||
#elif defined(__CYGWIN__)
|
||||
ret = GetCurrentThreadId();
|
||||
#else
|
||||
ret = -1;
|
||||
#endif
|
||||
/* TODO: NetBSD thread ID support */
|
||||
return ret;
|
102
vendor/github.com/karalabe/usb/libusb/libusb/os/threads_posix.h
generated
vendored
Normal file
102
vendor/github.com/karalabe/usb/libusb/libusb/os/threads_posix.h
generated
vendored
Normal 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 */
|
126
vendor/github.com/karalabe/usb/libusb/libusb/os/threads_windows.c
generated
vendored
Normal file
126
vendor/github.com/karalabe/usb/libusb/libusb/os/threads_windows.c
generated
vendored
Normal 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);
|
||||
}
|
||||
}
|
@ -21,17 +21,40 @@
|
||||
#ifndef LIBUSB_THREADS_WINDOWS_H
|
||||
#define LIBUSB_THREADS_WINDOWS_H
|
||||
|
||||
#define usbi_mutex_static_t volatile LONG
|
||||
#define USBI_MUTEX_INITIALIZER 0
|
||||
#define USBI_MUTEX_INITIALIZER 0L
|
||||
#ifdef _WIN32_WCE
|
||||
typedef LONG usbi_mutex_static_t;
|
||||
#else
|
||||
typedef volatile LONG usbi_mutex_static_t;
|
||||
#endif
|
||||
void usbi_mutex_static_lock(usbi_mutex_static_t *mutex);
|
||||
static inline void usbi_mutex_static_unlock(usbi_mutex_static_t *mutex)
|
||||
{
|
||||
InterlockedExchange(mutex, 0L);
|
||||
}
|
||||
|
||||
#define usbi_mutex_t HANDLE
|
||||
|
||||
typedef struct usbi_cond {
|
||||
// Every time a thread touches the CV, it winds up in one of these lists.
|
||||
// It stays there until the CV is destroyed, even if the thread terminates.
|
||||
struct list_head waiters;
|
||||
struct list_head not_waiting;
|
||||
} usbi_cond_t;
|
||||
typedef CRITICAL_SECTION usbi_mutex_t;
|
||||
static inline int usbi_mutex_init(usbi_mutex_t *mutex)
|
||||
{
|
||||
InitializeCriticalSection(mutex);
|
||||
return 0;
|
||||
}
|
||||
static inline void usbi_mutex_lock(usbi_mutex_t *mutex)
|
||||
{
|
||||
EnterCriticalSection(mutex);
|
||||
}
|
||||
static inline void usbi_mutex_unlock(usbi_mutex_t *mutex)
|
||||
{
|
||||
LeaveCriticalSection(mutex);
|
||||
}
|
||||
static inline int usbi_mutex_trylock(usbi_mutex_t *mutex)
|
||||
{
|
||||
return !TryEnterCriticalSection(mutex);
|
||||
}
|
||||
static inline void usbi_mutex_destroy(usbi_mutex_t *mutex)
|
||||
{
|
||||
DeleteCriticalSection(mutex);
|
||||
}
|
||||
|
||||
// We *were* getting timespec from pthread.h:
|
||||
#if (!defined(HAVE_STRUCT_TIMESPEC) && !defined(_TIMESPEC_DEFINED))
|
||||
@ -48,29 +71,41 @@ struct timespec {
|
||||
#define ETIMEDOUT 10060 /* This is the value in winsock.h. */
|
||||
#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);
|
||||
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);
|
||||
void usbi_cond_init(usbi_cond_t *cond);
|
||||
int usbi_cond_wait(usbi_cond_t *cond, usbi_mutex_t *mutex);
|
||||
int usbi_cond_timedwait(usbi_cond_t *cond,
|
||||
usbi_mutex_t *mutex, const struct timeval *tv);
|
||||
int usbi_cond_broadcast(usbi_cond_t *cond);
|
||||
int usbi_cond_destroy(usbi_cond_t *cond);
|
||||
void usbi_cond_broadcast(usbi_cond_t *cond);
|
||||
void usbi_cond_destroy(usbi_cond_t *cond);
|
||||
|
||||
int usbi_tls_key_create(usbi_tls_key_t *key);
|
||||
void *usbi_tls_key_get(usbi_tls_key_t key);
|
||||
int usbi_tls_key_set(usbi_tls_key_t key, void *value);
|
||||
int usbi_tls_key_delete(usbi_tls_key_t key);
|
||||
typedef DWORD usbi_tls_key_t;
|
||||
static inline void usbi_tls_key_create(usbi_tls_key_t *key)
|
||||
{
|
||||
*key = TlsAlloc();
|
||||
}
|
||||
static inline void *usbi_tls_key_get(usbi_tls_key_t key)
|
||||
{
|
||||
return TlsGetValue(key);
|
||||
}
|
||||
static inline void usbi_tls_key_set(usbi_tls_key_t key, void *ptr)
|
||||
{
|
||||
(void)TlsSetValue(key, ptr);
|
||||
}
|
||||
static inline void usbi_tls_key_delete(usbi_tls_key_t key)
|
||||
{
|
||||
(void)TlsFree(key);
|
||||
}
|
||||
|
||||
int usbi_get_tid(void);
|
||||
static inline int usbi_get_tid(void)
|
||||
{
|
||||
return (int)GetCurrentThreadId();
|
||||
}
|
||||
|
||||
#endif /* LIBUSB_THREADS_WINDOWS_H */
|
@ -31,7 +31,7 @@
|
||||
#include "wince_usb.h"
|
||||
|
||||
// Global variables
|
||||
int windows_version = WINDOWS_CE;
|
||||
int errno = 0;
|
||||
static uint64_t hires_frequency, hires_ticks_to_ps;
|
||||
static HANDLE driver_handle = INVALID_HANDLE_VALUE;
|
||||
static int concurrent_usage = -1;
|
||||
@ -109,7 +109,7 @@ static int translate_driver_error(DWORD error)
|
||||
}
|
||||
}
|
||||
|
||||
static int init_dllimports(void)
|
||||
static BOOL init_dllimports(void)
|
||||
{
|
||||
DLL_GET_HANDLE(ceusbkwrapper);
|
||||
DLL_LOAD_FUNC(ceusbkwrapper, UkwOpenDriver, TRUE);
|
||||
@ -135,7 +135,7 @@ static int init_dllimports(void)
|
||||
DLL_LOAD_FUNC(ceusbkwrapper, UkwIssueBulkTransfer, TRUE);
|
||||
DLL_LOAD_FUNC(ceusbkwrapper, UkwIsPipeHalted, TRUE);
|
||||
|
||||
return LIBUSB_SUCCESS;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void exit_dllimports(void)
|
||||
@ -186,11 +186,8 @@ static int wince_init(struct libusb_context *ctx)
|
||||
// NB: concurrent usage supposes that init calls are equally balanced with
|
||||
// exit calls. If init is called more than exit, we will not exit properly
|
||||
if ( ++concurrent_usage == 0 ) { // First init?
|
||||
// Initialize pollable file descriptors
|
||||
init_polling();
|
||||
|
||||
// Load DLL imports
|
||||
if (init_dllimports() != LIBUSB_SUCCESS) {
|
||||
if (!init_dllimports()) {
|
||||
usbi_err(ctx, "could not resolve DLL functions");
|
||||
r = LIBUSB_ERROR_NOT_SUPPORTED;
|
||||
goto init_exit;
|
||||
@ -223,7 +220,6 @@ static int wince_init(struct libusb_context *ctx)
|
||||
init_exit: // Holds semaphore here.
|
||||
if (!concurrent_usage && r != LIBUSB_SUCCESS) { // First init failed?
|
||||
exit_dllimports();
|
||||
exit_polling();
|
||||
|
||||
if (driver_handle != INVALID_HANDLE_VALUE) {
|
||||
UkwCloseDriver(driver_handle);
|
||||
@ -239,10 +235,11 @@ init_exit: // Holds semaphore here.
|
||||
return r;
|
||||
}
|
||||
|
||||
static void wince_exit(void)
|
||||
static void wince_exit(struct libusb_context *ctx)
|
||||
{
|
||||
HANDLE semaphore;
|
||||
TCHAR sem_name[11 + 8 + 1]; // strlen("libusb_init") + (32-bit hex PID) + '\0'
|
||||
UNUSED(ctx);
|
||||
|
||||
_stprintf(sem_name, _T("libusb_init%08X"), (unsigned int)(GetCurrentProcessId() & 0xFFFFFFFF));
|
||||
semaphore = CreateSemaphore(NULL, 1, 1, sem_name);
|
||||
@ -259,7 +256,6 @@ static void wince_exit(void)
|
||||
// Only works if exits and inits are balanced exactly
|
||||
if (--concurrent_usage < 0) { // Last exit
|
||||
exit_dllimports();
|
||||
exit_polling();
|
||||
|
||||
if (driver_handle != INVALID_HANDLE_VALUE) {
|
||||
UkwCloseDriver(driver_handle);
|
||||
@ -328,7 +324,7 @@ static int wince_get_device_list(
|
||||
}
|
||||
|
||||
new_devices = discovered_devs_append(new_devices, dev);
|
||||
if (!discdevs) {
|
||||
if (!new_devices) {
|
||||
r = LIBUSB_ERROR_NO_MEM;
|
||||
goto err_out;
|
||||
}
|
||||
@ -541,12 +537,9 @@ static void wince_destroy_device(struct libusb_device *dev)
|
||||
static void wince_clear_transfer_priv(struct usbi_transfer *itransfer)
|
||||
{
|
||||
struct wince_transfer_priv *transfer_priv = usbi_transfer_get_os_priv(itransfer);
|
||||
struct winfd wfd = fd_to_winfd(transfer_priv->pollable_fd.fd);
|
||||
|
||||
// No need to cancel transfer as it is either complete or abandoned
|
||||
wfd.itransfer = NULL;
|
||||
CloseHandle(wfd.handle);
|
||||
usbi_free_fd(&transfer_priv->pollable_fd);
|
||||
usbi_close(transfer_priv->pollable_fd.fd);
|
||||
transfer_priv->pollable_fd = INVALID_WINFD;
|
||||
}
|
||||
|
||||
static int wince_cancel_transfer(struct usbi_transfer *itransfer)
|
||||
@ -570,11 +563,10 @@ static int wince_submit_control_or_bulk_transfer(struct usbi_transfer *itransfer
|
||||
BOOL direction_in, ret;
|
||||
struct winfd wfd;
|
||||
DWORD flags;
|
||||
HANDLE eventHandle;
|
||||
PUKW_CONTROL_HEADER setup = NULL;
|
||||
const BOOL control_transfer = transfer->type == LIBUSB_TRANSFER_TYPE_CONTROL;
|
||||
int r;
|
||||
|
||||
transfer_priv->pollable_fd = INVALID_WINFD;
|
||||
if (control_transfer) {
|
||||
setup = (PUKW_CONTROL_HEADER) transfer->buffer;
|
||||
direction_in = setup->bmRequestType & LIBUSB_ENDPOINT_IN;
|
||||
@ -584,19 +576,18 @@ static int wince_submit_control_or_bulk_transfer(struct usbi_transfer *itransfer
|
||||
flags = direction_in ? UKW_TF_IN_TRANSFER : UKW_TF_OUT_TRANSFER;
|
||||
flags |= UKW_TF_SHORT_TRANSFER_OK;
|
||||
|
||||
eventHandle = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
if (eventHandle == NULL) {
|
||||
usbi_err(ctx, "Failed to create event for async transfer");
|
||||
wfd = usbi_create_fd();
|
||||
if (wfd.fd < 0)
|
||||
return LIBUSB_ERROR_NO_MEM;
|
||||
}
|
||||
|
||||
wfd = usbi_create_fd(eventHandle, direction_in ? RW_READ : RW_WRITE, itransfer, &wince_cancel_transfer);
|
||||
if (wfd.fd < 0) {
|
||||
CloseHandle(eventHandle);
|
||||
return LIBUSB_ERROR_NO_MEM;
|
||||
r = usbi_add_pollfd(ctx, wfd.fd, direction_in ? POLLIN : POLLOUT);
|
||||
if (r) {
|
||||
usbi_close(wfd.fd);
|
||||
return r;
|
||||
}
|
||||
|
||||
transfer_priv->pollable_fd = wfd;
|
||||
|
||||
if (control_transfer) {
|
||||
// Split out control setup header and data buffer
|
||||
DWORD bufLen = transfer->length - sizeof(UKW_CONTROL_HEADER);
|
||||
@ -612,19 +603,16 @@ static int wince_submit_control_or_bulk_transfer(struct usbi_transfer *itransfer
|
||||
int libusbErr = translate_driver_error(GetLastError());
|
||||
usbi_err(ctx, "UkwIssue%sTransfer failed: error %u",
|
||||
control_transfer ? "Control" : "Bulk", (unsigned int)GetLastError());
|
||||
wince_clear_transfer_priv(itransfer);
|
||||
usbi_remove_pollfd(ctx, wfd.fd);
|
||||
usbi_close(wfd.fd);
|
||||
transfer_priv->pollable_fd = INVALID_WINFD;
|
||||
return libusbErr;
|
||||
}
|
||||
usbi_add_pollfd(ctx, transfer_priv->pollable_fd.fd, direction_in ? POLLIN : POLLOUT);
|
||||
|
||||
|
||||
return LIBUSB_SUCCESS;
|
||||
}
|
||||
|
||||
static int wince_submit_iso_transfer(struct usbi_transfer *itransfer)
|
||||
{
|
||||
return LIBUSB_ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
static int wince_submit_transfer(struct usbi_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:
|
||||
return wince_submit_control_or_bulk_transfer(itransfer);
|
||||
case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
|
||||
return wince_submit_iso_transfer(itransfer);
|
||||
case LIBUSB_TRANSFER_TYPE_BULK_STREAM:
|
||||
return LIBUSB_ERROR_NOT_SUPPORTED;
|
||||
default:
|
||||
@ -763,7 +750,7 @@ static int wince_handle_events(
|
||||
struct wince_transfer_priv* transfer_priv = NULL;
|
||||
POLL_NFDS_TYPE i = 0;
|
||||
BOOL found = FALSE;
|
||||
struct usbi_transfer *transfer;
|
||||
struct usbi_transfer *itransfer;
|
||||
DWORD io_size, io_result;
|
||||
int r = LIBUSB_SUCCESS;
|
||||
|
||||
@ -780,8 +767,8 @@ static int wince_handle_events(
|
||||
// Because a Windows OVERLAPPED is used for poll emulation,
|
||||
// a pollable fd is created and stored with each transfer
|
||||
usbi_mutex_lock(&ctx->flying_transfers_lock);
|
||||
list_for_each_entry(transfer, &ctx->flying_transfers, list, struct usbi_transfer) {
|
||||
transfer_priv = usbi_transfer_get_os_priv(transfer);
|
||||
list_for_each_entry(itransfer, &ctx->flying_transfers, list, struct usbi_transfer) {
|
||||
transfer_priv = usbi_transfer_get_os_priv(itransfer);
|
||||
if (transfer_priv->pollable_fd.fd == fds[i].fd) {
|
||||
found = TRUE;
|
||||
break;
|
||||
@ -796,7 +783,7 @@ static int wince_handle_events(
|
||||
// let handle_callback free the event using the transfer wfd
|
||||
// If you don't use the transfer wfd, you run a risk of trying to free a
|
||||
// newly allocated wfd that took the place of the one from the transfer.
|
||||
wince_handle_callback(transfer, io_result, io_size);
|
||||
wince_handle_callback(itransfer, io_result, io_size);
|
||||
} else if (found) {
|
||||
usbi_err(ctx, "matching transfer for fd %d has not completed", fds[i]);
|
||||
r = LIBUSB_ERROR_OTHER;
|
||||
@ -848,11 +835,12 @@ static int wince_clock_gettime(int clk_id, struct timespec *tp)
|
||||
}
|
||||
}
|
||||
|
||||
const struct usbi_os_backend wince_backend = {
|
||||
const struct usbi_os_backend usbi_backend = {
|
||||
"Windows CE",
|
||||
0,
|
||||
wince_init,
|
||||
wince_exit,
|
||||
NULL, /* set_option() */
|
||||
|
||||
wince_get_device_list,
|
||||
NULL, /* hotplug_poll */
|
||||
@ -893,6 +881,7 @@ const struct usbi_os_backend wince_backend = {
|
||||
NULL, /* handle_transfer_completion() */
|
||||
|
||||
wince_clock_gettime,
|
||||
0,
|
||||
sizeof(struct wince_device_priv),
|
||||
0,
|
||||
sizeof(struct wince_transfer_priv),
|
@ -68,21 +68,23 @@
|
||||
/*
|
||||
* Macros for handling DLL themselves
|
||||
*/
|
||||
#define DLL_HANDLE_NAME(name) __dll_##name##_handle
|
||||
|
||||
#define DLL_DECLARE_HANDLE(name) \
|
||||
static HMODULE __dll_##name##_handle = NULL
|
||||
static HMODULE DLL_HANDLE_NAME(name) = NULL
|
||||
|
||||
#define DLL_GET_HANDLE(name) \
|
||||
do { \
|
||||
__dll_##name##_handle = DLL_LOAD_LIBRARY(name); \
|
||||
if (!__dll_##name##_handle) \
|
||||
return LIBUSB_ERROR_OTHER; \
|
||||
DLL_HANDLE_NAME(name) = DLL_LOAD_LIBRARY(name); \
|
||||
if (!DLL_HANDLE_NAME(name)) \
|
||||
return FALSE; \
|
||||
} while (0)
|
||||
|
||||
#define DLL_FREE_HANDLE(name) \
|
||||
do { \
|
||||
if (__dll_##name##_handle) { \
|
||||
FreeLibrary(__dll_##name##_handle); \
|
||||
__dll_##name##_handle = NULL; \
|
||||
if (DLL_HANDLE_NAME(name)) { \
|
||||
FreeLibrary(DLL_HANDLE_NAME(name)); \
|
||||
DLL_HANDLE_NAME(name) = NULL; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
@ -90,9 +92,11 @@
|
||||
/*
|
||||
* Macros for handling functions within a DLL
|
||||
*/
|
||||
#define DLL_FUNC_NAME(name) __dll_##name##_func_t
|
||||
|
||||
#define DLL_DECLARE_FUNC_PREFIXNAME(api, ret, prefixname, name, args) \
|
||||
typedef ret (api * __dll_##name##_func_t)args; \
|
||||
static __dll_##name##_func_t prefixname = NULL
|
||||
typedef ret (api * DLL_FUNC_NAME(name))args; \
|
||||
static DLL_FUNC_NAME(name) prefixname = NULL
|
||||
|
||||
#define DLL_DECLARE_FUNC(api, ret, name, args) \
|
||||
DLL_DECLARE_FUNC_PREFIXNAME(api, ret, name, name, args)
|
||||
@ -101,21 +105,21 @@
|
||||
|
||||
#define DLL_LOAD_FUNC_PREFIXNAME(dll, prefixname, name, ret_on_failure) \
|
||||
do { \
|
||||
HMODULE h = __dll_##dll##_handle; \
|
||||
prefixname = (__dll_##name##_func_t)GetProcAddress(h, \
|
||||
HMODULE h = DLL_HANDLE_NAME(dll); \
|
||||
prefixname = (DLL_FUNC_NAME(name))GetProcAddress(h, \
|
||||
DLL_STRINGIFY(name)); \
|
||||
if (prefixname) \
|
||||
break; \
|
||||
prefixname = (__dll_##name##_func_t)GetProcAddress(h, \
|
||||
prefixname = (DLL_FUNC_NAME(name))GetProcAddress(h, \
|
||||
DLL_STRINGIFY(name) DLL_STRINGIFY(A)); \
|
||||
if (prefixname) \
|
||||
break; \
|
||||
prefixname = (__dll_##name##_func_t)GetProcAddress(h, \
|
||||
prefixname = (DLL_FUNC_NAME(name))GetProcAddress(h, \
|
||||
DLL_STRINGIFY(name) DLL_STRINGIFY(W)); \
|
||||
if (prefixname) \
|
||||
break; \
|
||||
if (ret_on_failure) \
|
||||
return LIBUSB_ERROR_NOT_FOUND; \
|
||||
return FALSE; \
|
||||
} while (0)
|
||||
|
||||
#define DLL_LOAD_FUNC(dll, name, ret_on_failure) \
|
@ -32,6 +32,14 @@
|
||||
#include "windows_common.h"
|
||||
#include "windows_nt_common.h"
|
||||
|
||||
// Public
|
||||
BOOL (WINAPI *pCancelIoEx)(HANDLE, LPOVERLAPPED);
|
||||
enum windows_version windows_version = WINDOWS_UNDEFINED;
|
||||
|
||||
// Global variables for init/exit
|
||||
static unsigned int init_count = 0;
|
||||
static bool usbdk_available = false;
|
||||
|
||||
// Global variables for clock_gettime mechanism
|
||||
static uint64_t hires_ticks_to_ps;
|
||||
static uint64_t hires_frequency;
|
||||
@ -50,6 +58,11 @@ struct timer_request {
|
||||
static HANDLE timer_thread = NULL;
|
||||
static DWORD timer_thread_id = 0;
|
||||
|
||||
/* Kernel32 dependencies */
|
||||
DLL_DECLARE_HANDLE(Kernel32);
|
||||
/* This call is only available from XP SP2 */
|
||||
DLL_DECLARE_FUNC_PREFIXED(WINAPI, BOOL, p, IsWow64Process, (HANDLE, PBOOL));
|
||||
|
||||
/* User32 dependencies */
|
||||
DLL_DECLARE_HANDLE(User32);
|
||||
DLL_DECLARE_FUNC_PREFIXED(WINAPI, BOOL, p, GetMessageA, (LPMSG, HWND, UINT, UINT));
|
||||
@ -111,6 +124,11 @@ const char *windows_error_str(DWORD error_code)
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline struct windows_context_priv *_context_priv(struct libusb_context *ctx)
|
||||
{
|
||||
return (struct windows_context_priv *)ctx->os_priv;
|
||||
}
|
||||
|
||||
/* Hash table functions - modified From glibc 2.3.2:
|
||||
[Aho,Sethi,Ullman] Compilers: Principles, Techniques and Tools, 1986
|
||||
[Knuth] The Art of Computer Programming, part 3 (6.4) */
|
||||
@ -123,7 +141,7 @@ typedef struct htab_entry {
|
||||
} htab_entry;
|
||||
|
||||
static htab_entry *htab_table = NULL;
|
||||
static usbi_mutex_t htab_mutex = NULL;
|
||||
static usbi_mutex_t htab_mutex;
|
||||
static unsigned long htab_filled;
|
||||
|
||||
/* Before using the hash table we must allocate memory for it.
|
||||
@ -256,35 +274,46 @@ out_unlock:
|
||||
return idx;
|
||||
}
|
||||
|
||||
static int windows_init_dlls(void)
|
||||
/*
|
||||
* Make a transfer complete synchronously
|
||||
*/
|
||||
void windows_force_sync_completion(OVERLAPPED *overlapped, ULONG size)
|
||||
{
|
||||
overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY;
|
||||
overlapped->InternalHigh = size;
|
||||
SetEvent(overlapped->hEvent);
|
||||
}
|
||||
|
||||
static BOOL windows_init_dlls(void)
|
||||
{
|
||||
DLL_GET_HANDLE(Kernel32);
|
||||
DLL_LOAD_FUNC_PREFIXED(Kernel32, p, IsWow64Process, FALSE);
|
||||
pCancelIoEx = (BOOL (WINAPI *)(HANDLE, LPOVERLAPPED))
|
||||
GetProcAddress(DLL_HANDLE_NAME(Kernel32), "CancelIoEx");
|
||||
usbi_dbg("Will use CancelIo%s for I/O cancellation", pCancelIoEx ? "Ex" : "");
|
||||
|
||||
DLL_GET_HANDLE(User32);
|
||||
DLL_LOAD_FUNC_PREFIXED(User32, p, GetMessageA, TRUE);
|
||||
DLL_LOAD_FUNC_PREFIXED(User32, p, PeekMessageA, TRUE);
|
||||
DLL_LOAD_FUNC_PREFIXED(User32, p, PostThreadMessageA, TRUE);
|
||||
|
||||
return LIBUSB_SUCCESS;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void windows_exit_dlls(void)
|
||||
{
|
||||
DLL_FREE_HANDLE(Kernel32);
|
||||
DLL_FREE_HANDLE(User32);
|
||||
}
|
||||
|
||||
static bool windows_init_clock(struct libusb_context *ctx)
|
||||
{
|
||||
DWORD_PTR affinity, dummy;
|
||||
HANDLE event = NULL;
|
||||
HANDLE event;
|
||||
LARGE_INTEGER li_frequency;
|
||||
int i;
|
||||
|
||||
if (QueryPerformanceFrequency(&li_frequency)) {
|
||||
// Load DLL imports
|
||||
if (windows_init_dlls() != LIBUSB_SUCCESS) {
|
||||
usbi_err(ctx, "could not resolve DLL functions");
|
||||
return false;
|
||||
}
|
||||
|
||||
// The hires frequency can go as high as 4 GHz, so we'll use a conversion
|
||||
// to picoseconds to compute the tv_nsecs part in clock_gettime
|
||||
hires_frequency = li_frequency.QuadPart;
|
||||
@ -340,7 +369,7 @@ static bool windows_init_clock(struct libusb_context *ctx)
|
||||
return true;
|
||||
}
|
||||
|
||||
void windows_destroy_clock(void)
|
||||
static void windows_destroy_clock(void)
|
||||
{
|
||||
if (timer_thread) {
|
||||
// actually the signal to quit the thread.
|
||||
@ -357,6 +386,110 @@ void windows_destroy_clock(void)
|
||||
}
|
||||
}
|
||||
|
||||
/* Windows version detection */
|
||||
static BOOL is_x64(void)
|
||||
{
|
||||
BOOL ret = FALSE;
|
||||
|
||||
// Detect if we're running a 32 or 64 bit system
|
||||
if (sizeof(uintptr_t) < 8) {
|
||||
if (pIsWow64Process != NULL)
|
||||
pIsWow64Process(GetCurrentProcess(), &ret);
|
||||
} else {
|
||||
ret = TRUE;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void get_windows_version(void)
|
||||
{
|
||||
OSVERSIONINFOEXA vi, vi2;
|
||||
const char *arch, *w = NULL;
|
||||
unsigned major, minor, version;
|
||||
ULONGLONG major_equal, minor_equal;
|
||||
BOOL ws;
|
||||
|
||||
windows_version = WINDOWS_UNDEFINED;
|
||||
|
||||
memset(&vi, 0, sizeof(vi));
|
||||
vi.dwOSVersionInfoSize = sizeof(vi);
|
||||
if (!GetVersionExA((OSVERSIONINFOA *)&vi)) {
|
||||
memset(&vi, 0, sizeof(vi));
|
||||
vi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
|
||||
if (!GetVersionExA((OSVERSIONINFOA *)&vi))
|
||||
return;
|
||||
}
|
||||
|
||||
if (vi.dwPlatformId != VER_PLATFORM_WIN32_NT)
|
||||
return;
|
||||
|
||||
if ((vi.dwMajorVersion > 6) || ((vi.dwMajorVersion == 6) && (vi.dwMinorVersion >= 2))) {
|
||||
// Starting with Windows 8.1 Preview, GetVersionEx() does no longer report the actual OS version
|
||||
// See: http://msdn.microsoft.com/en-us/library/windows/desktop/dn302074.aspx
|
||||
|
||||
major_equal = VerSetConditionMask(0, VER_MAJORVERSION, VER_EQUAL);
|
||||
for (major = vi.dwMajorVersion; major <= 9; major++) {
|
||||
memset(&vi2, 0, sizeof(vi2));
|
||||
vi2.dwOSVersionInfoSize = sizeof(vi2);
|
||||
vi2.dwMajorVersion = major;
|
||||
if (!VerifyVersionInfoA(&vi2, VER_MAJORVERSION, major_equal))
|
||||
continue;
|
||||
|
||||
if (vi.dwMajorVersion < major) {
|
||||
vi.dwMajorVersion = major;
|
||||
vi.dwMinorVersion = 0;
|
||||
}
|
||||
|
||||
minor_equal = VerSetConditionMask(0, VER_MINORVERSION, VER_EQUAL);
|
||||
for (minor = vi.dwMinorVersion; minor <= 9; minor++) {
|
||||
memset(&vi2, 0, sizeof(vi2));
|
||||
vi2.dwOSVersionInfoSize = sizeof(vi2);
|
||||
vi2.dwMinorVersion = minor;
|
||||
if (!VerifyVersionInfoA(&vi2, VER_MINORVERSION, minor_equal))
|
||||
continue;
|
||||
|
||||
vi.dwMinorVersion = minor;
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((vi.dwMajorVersion > 0xf) || (vi.dwMinorVersion > 0xf))
|
||||
return;
|
||||
|
||||
ws = (vi.wProductType <= VER_NT_WORKSTATION);
|
||||
version = vi.dwMajorVersion << 4 | vi.dwMinorVersion;
|
||||
switch (version) {
|
||||
case 0x50: windows_version = WINDOWS_2000; w = "2000"; break;
|
||||
case 0x51: windows_version = WINDOWS_XP; w = "XP"; break;
|
||||
case 0x52: windows_version = WINDOWS_2003; w = "2003"; break;
|
||||
case 0x60: windows_version = WINDOWS_VISTA; w = (ws ? "Vista" : "2008"); break;
|
||||
case 0x61: windows_version = WINDOWS_7; w = (ws ? "7" : "2008_R2"); break;
|
||||
case 0x62: windows_version = WINDOWS_8; w = (ws ? "8" : "2012"); break;
|
||||
case 0x63: windows_version = WINDOWS_8_1; w = (ws ? "8.1" : "2012_R2"); break;
|
||||
case 0x64: windows_version = WINDOWS_10; w = (ws ? "10" : "2016"); break;
|
||||
default:
|
||||
if (version < 0x50) {
|
||||
return;
|
||||
} else {
|
||||
windows_version = WINDOWS_11_OR_LATER;
|
||||
w = "11 or later";
|
||||
}
|
||||
}
|
||||
|
||||
arch = is_x64() ? "64-bit" : "32-bit";
|
||||
|
||||
if (vi.wServicePackMinor)
|
||||
usbi_dbg("Windows %s SP%u.%u %s", w, vi.wServicePackMajor, vi.wServicePackMinor, arch);
|
||||
else if (vi.wServicePackMajor)
|
||||
usbi_dbg("Windows %s SP%u %s", w, vi.wServicePackMajor, arch);
|
||||
else
|
||||
usbi_dbg("Windows %s %s", w, arch);
|
||||
}
|
||||
|
||||
/*
|
||||
* Monotonic and real time functions
|
||||
*/
|
||||
@ -401,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;
|
||||
#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)
|
||||
{
|
||||
int status, istatus;
|
||||
|
||||
usbi_dbg("handling I/O completion with errcode %u, size %u", io_result, io_size);
|
||||
|
||||
switch (io_result) {
|
||||
case NO_ERROR:
|
||||
status = windows_copy_transfer_data(itransfer, io_size);
|
||||
break;
|
||||
case ERROR_GEN_FAILURE:
|
||||
usbi_dbg("detected endpoint stall");
|
||||
status = LIBUSB_TRANSFER_STALL;
|
||||
break;
|
||||
case ERROR_SEM_TIMEOUT:
|
||||
usbi_dbg("detected semaphore timeout");
|
||||
status = LIBUSB_TRANSFER_TIMED_OUT;
|
||||
break;
|
||||
case ERROR_OPERATION_ABORTED:
|
||||
istatus = windows_copy_transfer_data(itransfer, 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;
|
||||
default:
|
||||
usbi_err(ITRANSFER_CTX(itransfer), "detected I/O error %u: %s", io_result, windows_error_str(io_result));
|
||||
status = LIBUSB_TRANSFER_ERROR;
|
||||
break;
|
||||
}
|
||||
windows_clear_transfer_priv(itransfer); // Cancel polling
|
||||
if (status == LIBUSB_TRANSFER_CANCELLED)
|
||||
usbi_handle_transfer_cancellation(itransfer);
|
||||
else
|
||||
usbi_handle_transfer_completion(itransfer, (enum libusb_transfer_status)status);
|
||||
}
|
||||
|
||||
void windows_handle_callback(struct usbi_transfer *itransfer, uint32_t io_result, uint32_t io_size)
|
||||
{
|
||||
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();
|
||||
}
|
||||
// NB: MSVC6 does not support named initializers.
|
||||
const struct usbi_os_backend usbi_backend = {
|
||||
"Windows",
|
||||
USBI_CAP_HAS_HID_ACCESS,
|
||||
windows_init,
|
||||
windows_exit,
|
||||
windows_set_option,
|
||||
windows_get_device_list,
|
||||
NULL, /* hotplug_poll */
|
||||
windows_open,
|
||||
windows_close,
|
||||
windows_get_device_descriptor,
|
||||
windows_get_active_config_descriptor,
|
||||
windows_get_config_descriptor,
|
||||
windows_get_config_descriptor_by_value,
|
||||
windows_get_configuration,
|
||||
windows_set_configuration,
|
||||
windows_claim_interface,
|
||||
windows_release_interface,
|
||||
windows_set_interface_altsetting,
|
||||
windows_clear_halt,
|
||||
windows_reset_device,
|
||||
NULL, /* alloc_streams */
|
||||
NULL, /* free_streams */
|
||||
NULL, /* dev_mem_alloc */
|
||||
NULL, /* dev_mem_free */
|
||||
NULL, /* kernel_driver_active */
|
||||
NULL, /* detach_kernel_driver */
|
||||
NULL, /* attach_kernel_driver */
|
||||
windows_destroy_device,
|
||||
windows_submit_transfer,
|
||||
windows_cancel_transfer,
|
||||
windows_clear_transfer_priv,
|
||||
windows_handle_events,
|
||||
NULL, /* handle_transfer_completion */
|
||||
windows_clock_gettime,
|
||||
sizeof(struct windows_context_priv),
|
||||
sizeof(union windows_device_priv),
|
||||
sizeof(union windows_device_handle_priv),
|
||||
sizeof(union windows_transfer_priv),
|
||||
};
|
110
vendor/github.com/karalabe/usb/libusb/libusb/os/windows_nt_common.h
generated
vendored
Normal file
110
vendor/github.com/karalabe/usb/libusb/libusb/os/windows_nt_common.h
generated
vendored
Normal 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
|
138
vendor/github.com/karalabe/usb/libusb/libusb/os/windows_nt_shared_types.h
generated
vendored
Normal file
138
vendor/github.com/karalabe/usb/libusb/libusb/os/windows_nt_shared_types.h
generated
vendored
Normal 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;
|
||||
};
|
@ -23,22 +23,12 @@
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#if defined(USE_USBDK)
|
||||
|
||||
#include <windows.h>
|
||||
#include <cfgmgr32.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "libusbi.h"
|
||||
#include "windows_common.h"
|
||||
#include "windows_nt_common.h"
|
||||
|
||||
#define ULONG64 uint64_t
|
||||
#define PVOID64 uint64_t
|
||||
|
||||
typedef CONST WCHAR *PCWCHAR;
|
||||
#define wcsncpy_s wcsncpy
|
||||
|
||||
#include "windows_usbdk.h"
|
||||
|
||||
#if !defined(STATUS_SUCCESS)
|
||||
@ -55,7 +45,7 @@ typedef LONG NTSTATUS;
|
||||
#endif
|
||||
|
||||
#if !defined(USBD_SUCCESS)
|
||||
typedef int32_t USBD_STATUS;
|
||||
typedef LONG USBD_STATUS;
|
||||
#define USBD_SUCCESS(Status) ((USBD_STATUS) (Status) >= 0)
|
||||
#define USBD_PENDING(Status) ((ULONG) (Status) >> 30 == 1)
|
||||
#define USBD_ERROR(Status) ((USBD_STATUS) (Status) < 0)
|
||||
@ -66,22 +56,6 @@ typedef int32_t USBD_STATUS;
|
||||
#define USBD_STATUS_CANCELED ((USBD_STATUS) 0xc0010000)
|
||||
#endif
|
||||
|
||||
static int concurrent_usage = -1;
|
||||
|
||||
struct usbdk_device_priv {
|
||||
USB_DK_DEVICE_INFO info;
|
||||
PUSB_CONFIGURATION_DESCRIPTOR *config_descriptors;
|
||||
HANDLE redirector_handle;
|
||||
uint8_t active_configuration;
|
||||
};
|
||||
|
||||
struct usbdk_transfer_priv {
|
||||
USB_DK_TRANSFER_REQUEST request;
|
||||
struct winfd pollable_fd;
|
||||
PULONG64 IsochronousPacketsArray;
|
||||
PUSB_DK_ISO_TRANSFER_RESULT IsochronousResultsArray;
|
||||
};
|
||||
|
||||
static inline struct usbdk_device_priv *_usbdk_device_priv(struct libusb_device *dev)
|
||||
{
|
||||
return (struct usbdk_device_priv *)dev->os_priv;
|
||||
@ -115,7 +89,7 @@ static FARPROC get_usbdk_proc_addr(struct libusb_context *ctx, LPCSTR api_name)
|
||||
FARPROC api_ptr = GetProcAddress(usbdk_helper.module, api_name);
|
||||
|
||||
if (api_ptr == NULL)
|
||||
usbi_err(ctx, "UsbDkHelper API %s not found, error %d", api_name, GetLastError());
|
||||
usbi_err(ctx, "UsbDkHelper API %s not found: %s", api_name, windows_error_str(0));
|
||||
|
||||
return api_ptr;
|
||||
}
|
||||
@ -132,7 +106,7 @@ static int load_usbdk_helper_dll(struct libusb_context *ctx)
|
||||
{
|
||||
usbdk_helper.module = LoadLibraryA("UsbDkHelper");
|
||||
if (usbdk_helper.module == NULL) {
|
||||
usbi_err(ctx, "Failed to load UsbDkHelper.dll, error %d", GetLastError());
|
||||
usbi_err(ctx, "Failed to load UsbDkHelper.dll: %s", windows_error_str(0));
|
||||
return LIBUSB_ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
@ -198,41 +172,41 @@ error_unload:
|
||||
|
||||
static int usbdk_init(struct libusb_context *ctx)
|
||||
{
|
||||
int r;
|
||||
SC_HANDLE managerHandle;
|
||||
SC_HANDLE serviceHandle;
|
||||
|
||||
if (++concurrent_usage == 0) { // First init?
|
||||
r = load_usbdk_helper_dll(ctx);
|
||||
if (r)
|
||||
goto init_exit;
|
||||
|
||||
init_polling();
|
||||
|
||||
r = windows_common_init(ctx);
|
||||
if (r)
|
||||
goto init_exit;
|
||||
managerHandle = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
|
||||
if (managerHandle == NULL) {
|
||||
usbi_warn(ctx, "failed to open service control manager: %s", windows_error_str(0));
|
||||
return LIBUSB_ERROR_OTHER;
|
||||
}
|
||||
// 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();
|
||||
serviceHandle = OpenServiceA(managerHandle, "UsbDk", GENERIC_READ);
|
||||
CloseServiceHandle(managerHandle);
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
if (r != LIBUSB_SUCCESS)
|
||||
--concurrent_usage; // Not expected to call libusb_exit if we failed.
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int usbdk_get_session_id_for_device(struct libusb_context *ctx,
|
||||
PUSB_DK_DEVICE_ID id, unsigned long *session_id)
|
||||
{
|
||||
char dev_identity[ARRAYSIZE(id->DeviceID) + ARRAYSIZE(id->InstanceID)];
|
||||
char dev_identity[ARRAYSIZE(id->DeviceID) + ARRAYSIZE(id->InstanceID) + 1];
|
||||
|
||||
if (sprintf(dev_identity, "%S%S", id->DeviceID, id->InstanceID) == -1) {
|
||||
if (snprintf(dev_identity, sizeof(dev_identity), "%S%S", id->DeviceID, id->InstanceID) == -1) {
|
||||
usbi_warn(ctx, "cannot form device identity", id->DeviceID);
|
||||
return LIBUSB_ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
@ -300,7 +274,7 @@ static void usbdk_device_init(libusb_device *dev, PUSB_DK_DEVICE_INFO info)
|
||||
dev->device_address = (uint8_t)(info->Port + 1);
|
||||
|
||||
dev->num_configurations = info->DeviceDescriptor.bNumConfigurations;
|
||||
dev->device_descriptor = info->DeviceDescriptor;
|
||||
memcpy(&dev->device_descriptor, &info->DeviceDescriptor, LIBUSB_DT_DEVICE_SIZE);
|
||||
|
||||
switch (info->Speed) {
|
||||
case LowSpeed:
|
||||
@ -371,26 +345,16 @@ func_exit:
|
||||
return r;
|
||||
}
|
||||
|
||||
static void usbdk_exit(void)
|
||||
{
|
||||
if (--concurrent_usage < 0) {
|
||||
windows_common_exit();
|
||||
exit_polling();
|
||||
unload_usbdk_helper_dll();
|
||||
}
|
||||
}
|
||||
|
||||
static int usbdk_get_device_descriptor(struct libusb_device *dev, unsigned char *buffer, int *host_endian)
|
||||
static int usbdk_get_device_descriptor(struct libusb_device *dev, unsigned char *buffer)
|
||||
{
|
||||
struct usbdk_device_priv *priv = _usbdk_device_priv(dev);
|
||||
|
||||
memcpy(buffer, &priv->info.DeviceDescriptor, DEVICE_DESC_LENGTH);
|
||||
*host_endian = 0;
|
||||
|
||||
return LIBUSB_SUCCESS;
|
||||
}
|
||||
|
||||
static int usbdk_get_config_descriptor(struct libusb_device *dev, uint8_t config_index, unsigned char *buffer, size_t len, int *host_endian)
|
||||
static int usbdk_get_config_descriptor(struct libusb_device *dev, uint8_t config_index, unsigned char *buffer, size_t len)
|
||||
{
|
||||
struct usbdk_device_priv *priv = _usbdk_device_priv(dev);
|
||||
PUSB_CONFIGURATION_DESCRIPTOR config_header;
|
||||
@ -403,15 +367,31 @@ static int usbdk_get_config_descriptor(struct libusb_device *dev, uint8_t config
|
||||
|
||||
size = min(config_header->wTotalLength, len);
|
||||
memcpy(buffer, config_header, size);
|
||||
*host_endian = 0;
|
||||
|
||||
return (int)size;
|
||||
}
|
||||
|
||||
static inline int usbdk_get_active_config_descriptor(struct libusb_device *dev, unsigned char *buffer, size_t len, int *host_endian)
|
||||
static int usbdk_get_config_descriptor_by_value(struct libusb_device *dev, uint8_t bConfigurationValue,
|
||||
unsigned char **buffer)
|
||||
{
|
||||
struct usbdk_device_priv *priv = _usbdk_device_priv(dev);
|
||||
PUSB_CONFIGURATION_DESCRIPTOR config_header;
|
||||
uint8_t index;
|
||||
|
||||
for (index = 0; index < dev->num_configurations; index++) {
|
||||
config_header = priv->config_descriptors[index];
|
||||
if (config_header->bConfigurationValue == bConfigurationValue) {
|
||||
*buffer = (unsigned char *)priv->config_descriptors[index];
|
||||
return (int)config_header->wTotalLength;
|
||||
}
|
||||
}
|
||||
|
||||
return LIBUSB_ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
static int usbdk_get_active_config_descriptor(struct libusb_device *dev, unsigned char *buffer, size_t len)
|
||||
{
|
||||
return usbdk_get_config_descriptor(dev, _usbdk_device_priv(dev)->active_configuration,
|
||||
buffer, len, host_endian);
|
||||
buffer, len);
|
||||
}
|
||||
|
||||
static int usbdk_open(struct libusb_device_handle *dev_handle)
|
||||
@ -424,6 +404,8 @@ static int usbdk_open(struct libusb_device_handle *dev_handle)
|
||||
return LIBUSB_ERROR_OTHER;
|
||||
}
|
||||
|
||||
priv->system_handle = usbdk_helper.GetRedirectorSystemHandle(priv->redirector_handle);
|
||||
|
||||
return LIBUSB_SUCCESS;
|
||||
}
|
||||
|
||||
@ -431,10 +413,8 @@ static void usbdk_close(struct libusb_device_handle *dev_handle)
|
||||
{
|
||||
struct usbdk_device_priv *priv = _usbdk_device_priv(dev_handle->dev);
|
||||
|
||||
if (!usbdk_helper.StopRedirect(priv->redirector_handle)) {
|
||||
struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev);
|
||||
usbi_err(ctx, "Redirector shutdown failed");
|
||||
}
|
||||
if (!usbdk_helper.StopRedirect(priv->redirector_handle))
|
||||
usbi_err(HANDLE_CTX(dev_handle), "Redirector shutdown failed");
|
||||
}
|
||||
|
||||
static int usbdk_get_configuration(struct libusb_device_handle *dev_handle, int *config)
|
||||
@ -460,7 +440,7 @@ static int usbdk_claim_interface(struct libusb_device_handle *dev_handle, int if
|
||||
|
||||
static int usbdk_set_interface_altsetting(struct libusb_device_handle *dev_handle, int iface, int altsetting)
|
||||
{
|
||||
struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev);
|
||||
struct libusb_context *ctx = HANDLE_CTX(dev_handle);
|
||||
struct usbdk_device_priv *priv = _usbdk_device_priv(dev_handle->dev);
|
||||
|
||||
if (!usbdk_helper.SetAltsetting(priv->redirector_handle, iface, altsetting)) {
|
||||
@ -480,7 +460,7 @@ static int usbdk_release_interface(struct libusb_device_handle *dev_handle, int
|
||||
|
||||
static int usbdk_clear_halt(struct libusb_device_handle *dev_handle, unsigned char endpoint)
|
||||
{
|
||||
struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev);
|
||||
struct libusb_context *ctx = HANDLE_CTX(dev_handle);
|
||||
struct usbdk_device_priv *priv = _usbdk_device_priv(dev_handle->dev);
|
||||
|
||||
if (!usbdk_helper.ResetPipe(priv->redirector_handle, endpoint)) {
|
||||
@ -493,7 +473,7 @@ static int usbdk_clear_halt(struct libusb_device_handle *dev_handle, unsigned ch
|
||||
|
||||
static int usbdk_reset_device(struct libusb_device_handle *dev_handle)
|
||||
{
|
||||
struct libusb_context *ctx = DEVICE_CTX(dev_handle->dev);
|
||||
struct libusb_context *ctx = HANDLE_CTX(dev_handle);
|
||||
struct usbdk_device_priv *priv = _usbdk_device_priv(dev_handle->dev);
|
||||
|
||||
if (!usbdk_helper.ResetDevice(priv->redirector_handle)) {
|
||||
@ -504,27 +484,6 @@ static int usbdk_reset_device(struct libusb_device_handle *dev_handle)
|
||||
return LIBUSB_SUCCESS;
|
||||
}
|
||||
|
||||
static int usbdk_kernel_driver_active(struct libusb_device_handle *dev_handle, int iface)
|
||||
{
|
||||
UNUSED(dev_handle);
|
||||
UNUSED(iface);
|
||||
return LIBUSB_ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
static int usbdk_attach_kernel_driver(struct libusb_device_handle *dev_handle, int iface)
|
||||
{
|
||||
UNUSED(dev_handle);
|
||||
UNUSED(iface);
|
||||
return LIBUSB_ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
static int usbdk_detach_kernel_driver(struct libusb_device_handle *dev_handle, int iface)
|
||||
{
|
||||
UNUSED(dev_handle);
|
||||
UNUSED(iface);
|
||||
return LIBUSB_ERROR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
static void usbdk_destroy_device(struct libusb_device *dev)
|
||||
{
|
||||
struct usbdk_device_priv* p = _usbdk_device_priv(dev);
|
||||
@ -533,12 +492,14 @@ static void usbdk_destroy_device(struct libusb_device *dev)
|
||||
usbdk_release_config_descriptors(p, p->info.DeviceDescriptor.bNumConfigurations);
|
||||
}
|
||||
|
||||
void windows_clear_transfer_priv(struct usbi_transfer *itransfer)
|
||||
static void usbdk_clear_transfer_priv(struct usbi_transfer *itransfer)
|
||||
{
|
||||
struct usbdk_transfer_priv *transfer_priv = _usbdk_transfer_priv(itransfer);
|
||||
struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
|
||||
|
||||
usbi_free_fd(&transfer_priv->pollable_fd);
|
||||
usbi_close(transfer_priv->pollable_fd.fd);
|
||||
transfer_priv->pollable_fd = INVALID_WINFD;
|
||||
transfer_priv->system_handle = NULL;
|
||||
|
||||
if (transfer->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS) {
|
||||
safe_free(transfer_priv->IsochronousPacketsArray);
|
||||
@ -551,47 +512,30 @@ static int usbdk_do_control_transfer(struct usbi_transfer *itransfer)
|
||||
struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
|
||||
struct usbdk_device_priv *priv = _usbdk_device_priv(transfer->dev_handle->dev);
|
||||
struct usbdk_transfer_priv *transfer_priv = _usbdk_transfer_priv(itransfer);
|
||||
struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev);
|
||||
struct winfd wfd;
|
||||
ULONG Length;
|
||||
struct libusb_context *ctx = TRANSFER_CTX(transfer);
|
||||
OVERLAPPED *overlapped = transfer_priv->pollable_fd.overlapped;
|
||||
TransferResult transResult;
|
||||
HANDLE sysHandle;
|
||||
|
||||
sysHandle = usbdk_helper.GetRedirectorSystemHandle(priv->redirector_handle);
|
||||
|
||||
wfd = usbi_create_fd(sysHandle, RW_READ, NULL, NULL);
|
||||
// Always use the handle returned from usbi_create_fd (wfd.handle)
|
||||
if (wfd.fd < 0)
|
||||
return LIBUSB_ERROR_NO_MEM;
|
||||
|
||||
transfer_priv->request.Buffer = (PVOID64)(uintptr_t)transfer->buffer;
|
||||
transfer_priv->request.Buffer = (PVOID64)transfer->buffer;
|
||||
transfer_priv->request.BufferLength = transfer->length;
|
||||
transfer_priv->request.TransferType = ControlTransferType;
|
||||
transfer_priv->pollable_fd = INVALID_WINFD;
|
||||
Length = (ULONG)transfer->length;
|
||||
|
||||
if (IS_XFERIN(transfer))
|
||||
transResult = usbdk_helper.ReadPipe(priv->redirector_handle, &transfer_priv->request, wfd.overlapped);
|
||||
if (transfer->buffer[0] & LIBUSB_ENDPOINT_IN)
|
||||
transResult = usbdk_helper.ReadPipe(priv->redirector_handle, &transfer_priv->request, overlapped);
|
||||
else
|
||||
transResult = usbdk_helper.WritePipe(priv->redirector_handle, &transfer_priv->request, wfd.overlapped);
|
||||
transResult = usbdk_helper.WritePipe(priv->redirector_handle, &transfer_priv->request, overlapped);
|
||||
|
||||
switch (transResult) {
|
||||
case TransferSuccess:
|
||||
wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY;
|
||||
wfd.overlapped->InternalHigh = (DWORD)Length;
|
||||
windows_force_sync_completion(overlapped, (ULONG)transfer_priv->request.Result.GenResult.BytesTransferred);
|
||||
break;
|
||||
case TransferSuccessAsync:
|
||||
break;
|
||||
case TransferFailure:
|
||||
usbi_err(ctx, "ControlTransfer failed: %s", windows_error_str(0));
|
||||
usbi_free_fd(&wfd);
|
||||
return LIBUSB_ERROR_IO;
|
||||
}
|
||||
|
||||
// Use priv_transfer to store data needed for async polling
|
||||
transfer_priv->pollable_fd = wfd;
|
||||
usbi_add_pollfd(ctx, transfer_priv->pollable_fd.fd, POLLIN);
|
||||
|
||||
return LIBUSB_SUCCESS;
|
||||
}
|
||||
|
||||
@ -600,12 +544,11 @@ static int usbdk_do_bulk_transfer(struct usbi_transfer *itransfer)
|
||||
struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
|
||||
struct usbdk_device_priv *priv = _usbdk_device_priv(transfer->dev_handle->dev);
|
||||
struct usbdk_transfer_priv *transfer_priv = _usbdk_transfer_priv(itransfer);
|
||||
struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev);
|
||||
struct winfd wfd;
|
||||
struct libusb_context *ctx = TRANSFER_CTX(transfer);
|
||||
OVERLAPPED *overlapped = transfer_priv->pollable_fd.overlapped;
|
||||
TransferResult transferRes;
|
||||
HANDLE sysHandle;
|
||||
|
||||
transfer_priv->request.Buffer = (PVOID64)(uintptr_t)transfer->buffer;
|
||||
transfer_priv->request.Buffer = (PVOID64)transfer->buffer;
|
||||
transfer_priv->request.BufferLength = transfer->length;
|
||||
transfer_priv->request.EndpointAddress = transfer->endpoint;
|
||||
|
||||
@ -614,42 +557,29 @@ static int usbdk_do_bulk_transfer(struct usbi_transfer *itransfer)
|
||||
transfer_priv->request.TransferType = BulkTransferType;
|
||||
break;
|
||||
case LIBUSB_TRANSFER_TYPE_INTERRUPT:
|
||||
transfer_priv->request.TransferType = IntertuptTransferType;
|
||||
transfer_priv->request.TransferType = InterruptTransferType;
|
||||
break;
|
||||
default:
|
||||
usbi_err(ctx, "Wrong transfer type (%d) in usbdk_do_bulk_transfer. %s", transfer->type, windows_error_str(0));
|
||||
usbi_err(ctx, "Wrong transfer type (%d) in usbdk_do_bulk_transfer", transfer->type);
|
||||
return LIBUSB_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
transfer_priv->pollable_fd = INVALID_WINFD;
|
||||
|
||||
sysHandle = usbdk_helper.GetRedirectorSystemHandle(priv->redirector_handle);
|
||||
|
||||
wfd = usbi_create_fd(sysHandle, IS_XFERIN(transfer) ? RW_READ : RW_WRITE, NULL, NULL);
|
||||
// Always use the handle returned from usbi_create_fd (wfd.handle)
|
||||
if (wfd.fd < 0)
|
||||
return LIBUSB_ERROR_NO_MEM;
|
||||
|
||||
if (IS_XFERIN(transfer))
|
||||
transferRes = usbdk_helper.ReadPipe(priv->redirector_handle, &transfer_priv->request, wfd.overlapped);
|
||||
transferRes = usbdk_helper.ReadPipe(priv->redirector_handle, &transfer_priv->request, overlapped);
|
||||
else
|
||||
transferRes = usbdk_helper.WritePipe(priv->redirector_handle, &transfer_priv->request, wfd.overlapped);
|
||||
transferRes = usbdk_helper.WritePipe(priv->redirector_handle, &transfer_priv->request, overlapped);
|
||||
|
||||
switch (transferRes) {
|
||||
case TransferSuccess:
|
||||
wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY;
|
||||
windows_force_sync_completion(overlapped, (ULONG)transfer_priv->request.Result.GenResult.BytesTransferred);
|
||||
break;
|
||||
case TransferSuccessAsync:
|
||||
break;
|
||||
case TransferFailure:
|
||||
usbi_err(ctx, "ReadPipe/WritePipe failed: %s", windows_error_str(0));
|
||||
usbi_free_fd(&wfd);
|
||||
return LIBUSB_ERROR_IO;
|
||||
}
|
||||
|
||||
transfer_priv->pollable_fd = wfd;
|
||||
usbi_add_pollfd(ctx, transfer_priv->pollable_fd.fd, IS_XFERIN(transfer) ? POLLIN : POLLOUT);
|
||||
|
||||
return LIBUSB_SUCCESS;
|
||||
}
|
||||
|
||||
@ -658,68 +588,81 @@ static int usbdk_do_iso_transfer(struct usbi_transfer *itransfer)
|
||||
struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
|
||||
struct usbdk_device_priv *priv = _usbdk_device_priv(transfer->dev_handle->dev);
|
||||
struct usbdk_transfer_priv *transfer_priv = _usbdk_transfer_priv(itransfer);
|
||||
struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev);
|
||||
struct winfd wfd;
|
||||
struct libusb_context *ctx = TRANSFER_CTX(transfer);
|
||||
OVERLAPPED *overlapped = transfer_priv->pollable_fd.overlapped;
|
||||
TransferResult transferRes;
|
||||
int i;
|
||||
HANDLE sysHandle;
|
||||
|
||||
transfer_priv->request.Buffer = (PVOID64)(uintptr_t)transfer->buffer;
|
||||
transfer_priv->request.Buffer = (PVOID64)transfer->buffer;
|
||||
transfer_priv->request.BufferLength = transfer->length;
|
||||
transfer_priv->request.EndpointAddress = transfer->endpoint;
|
||||
transfer_priv->request.TransferType = IsochronousTransferType;
|
||||
transfer_priv->request.IsochronousPacketsArraySize = transfer->num_iso_packets;
|
||||
transfer_priv->IsochronousPacketsArray = malloc(transfer->num_iso_packets * sizeof(ULONG64));
|
||||
transfer_priv->request.IsochronousPacketsArray = (PVOID64)(uintptr_t)transfer_priv->IsochronousPacketsArray;
|
||||
transfer_priv->request.IsochronousPacketsArray = (PVOID64)transfer_priv->IsochronousPacketsArray;
|
||||
if (!transfer_priv->IsochronousPacketsArray) {
|
||||
usbi_err(ctx, "Allocation of IsochronousPacketsArray is failed, %s", windows_error_str(0));
|
||||
return LIBUSB_ERROR_IO;
|
||||
usbi_err(ctx, "Allocation of IsochronousPacketsArray failed");
|
||||
return LIBUSB_ERROR_NO_MEM;
|
||||
}
|
||||
|
||||
transfer_priv->IsochronousResultsArray = malloc(transfer->num_iso_packets * sizeof(USB_DK_ISO_TRANSFER_RESULT));
|
||||
transfer_priv->request.Result.IsochronousResultsArray = (PVOID64)(uintptr_t)transfer_priv->IsochronousResultsArray;
|
||||
transfer_priv->request.Result.IsochronousResultsArray = (PVOID64)transfer_priv->IsochronousResultsArray;
|
||||
if (!transfer_priv->IsochronousResultsArray) {
|
||||
usbi_err(ctx, "Allocation of isochronousResultsArray is failed, %s", windows_error_str(0));
|
||||
free(transfer_priv->IsochronousPacketsArray);
|
||||
return LIBUSB_ERROR_IO;
|
||||
usbi_err(ctx, "Allocation of isochronousResultsArray failed");
|
||||
return LIBUSB_ERROR_NO_MEM;
|
||||
}
|
||||
|
||||
for (i = 0; i < transfer->num_iso_packets; i++)
|
||||
transfer_priv->IsochronousPacketsArray[i] = transfer->iso_packet_desc[i].length;
|
||||
|
||||
transfer_priv->pollable_fd = INVALID_WINFD;
|
||||
|
||||
sysHandle = usbdk_helper.GetRedirectorSystemHandle(priv->redirector_handle);
|
||||
|
||||
wfd = usbi_create_fd(sysHandle, IS_XFERIN(transfer) ? RW_READ : RW_WRITE, NULL, NULL);
|
||||
// Always use the handle returned from usbi_create_fd (wfd.handle)
|
||||
if (wfd.fd < 0) {
|
||||
free(transfer_priv->IsochronousPacketsArray);
|
||||
free(transfer_priv->IsochronousResultsArray);
|
||||
return LIBUSB_ERROR_NO_MEM;
|
||||
}
|
||||
|
||||
if (IS_XFERIN(transfer))
|
||||
transferRes = usbdk_helper.ReadPipe(priv->redirector_handle, &transfer_priv->request, wfd.overlapped);
|
||||
transferRes = usbdk_helper.ReadPipe(priv->redirector_handle, &transfer_priv->request, overlapped);
|
||||
else
|
||||
transferRes = usbdk_helper.WritePipe(priv->redirector_handle, &transfer_priv->request, wfd.overlapped);
|
||||
transferRes = usbdk_helper.WritePipe(priv->redirector_handle, &transfer_priv->request, overlapped);
|
||||
|
||||
switch (transferRes) {
|
||||
case TransferSuccess:
|
||||
wfd.overlapped->Internal = STATUS_COMPLETED_SYNCHRONOUSLY;
|
||||
windows_force_sync_completion(overlapped, (ULONG)transfer_priv->request.Result.GenResult.BytesTransferred);
|
||||
break;
|
||||
case TransferSuccessAsync:
|
||||
break;
|
||||
case TransferFailure:
|
||||
usbi_err(ctx, "ReadPipe/WritePipe failed: %s", windows_error_str(0));
|
||||
usbi_free_fd(&wfd);
|
||||
free(transfer_priv->IsochronousPacketsArray);
|
||||
free(transfer_priv->IsochronousResultsArray);
|
||||
return LIBUSB_ERROR_IO;
|
||||
}
|
||||
|
||||
return LIBUSB_SUCCESS;
|
||||
}
|
||||
|
||||
static int usbdk_do_submit_transfer(struct usbi_transfer *itransfer,
|
||||
short events, int (*transfer_fn)(struct usbi_transfer *))
|
||||
{
|
||||
struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
|
||||
struct libusb_context *ctx = TRANSFER_CTX(transfer);
|
||||
struct usbdk_device_priv *priv = _usbdk_device_priv(transfer->dev_handle->dev);
|
||||
struct usbdk_transfer_priv *transfer_priv = _usbdk_transfer_priv(itransfer);
|
||||
struct winfd wfd;
|
||||
int r;
|
||||
|
||||
wfd = usbi_create_fd();
|
||||
if (wfd.fd < 0)
|
||||
return LIBUSB_ERROR_NO_MEM;
|
||||
|
||||
r = usbi_add_pollfd(ctx, wfd.fd, events);
|
||||
if (r) {
|
||||
usbi_close(wfd.fd);
|
||||
return r;
|
||||
}
|
||||
|
||||
// Use transfer_priv to store data needed for async polling
|
||||
transfer_priv->pollable_fd = wfd;
|
||||
usbi_add_pollfd(ctx, transfer_priv->pollable_fd.fd, IS_XFERIN(transfer) ? POLLIN : POLLOUT);
|
||||
transfer_priv->system_handle = priv->system_handle;
|
||||
|
||||
r = transfer_fn(itransfer);
|
||||
if (r != LIBUSB_SUCCESS) {
|
||||
usbi_remove_pollfd(ctx, wfd.fd);
|
||||
usbdk_clear_transfer_priv(itransfer);
|
||||
return r;
|
||||
}
|
||||
|
||||
return LIBUSB_SUCCESS;
|
||||
}
|
||||
@ -727,34 +670,53 @@ static int usbdk_do_iso_transfer(struct usbi_transfer *itransfer)
|
||||
static int usbdk_submit_transfer(struct usbi_transfer *itransfer)
|
||||
{
|
||||
struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
|
||||
int (*transfer_fn)(struct usbi_transfer *);
|
||||
short events;
|
||||
|
||||
switch (transfer->type) {
|
||||
case LIBUSB_TRANSFER_TYPE_CONTROL:
|
||||
return usbdk_do_control_transfer(itransfer);
|
||||
events = (transfer->buffer[0] & LIBUSB_ENDPOINT_IN) ? POLLIN : POLLOUT;
|
||||
transfer_fn = usbdk_do_control_transfer;
|
||||
break;
|
||||
case LIBUSB_TRANSFER_TYPE_BULK:
|
||||
case LIBUSB_TRANSFER_TYPE_INTERRUPT:
|
||||
if (IS_XFEROUT(transfer) && (transfer->flags & LIBUSB_TRANSFER_ADD_ZERO_PACKET))
|
||||
return LIBUSB_ERROR_NOT_SUPPORTED; //TODO: Check whether we can support this in UsbDk
|
||||
else
|
||||
return usbdk_do_bulk_transfer(itransfer);
|
||||
events = IS_XFERIN(transfer) ? POLLIN : POLLOUT;
|
||||
transfer_fn = usbdk_do_bulk_transfer;
|
||||
break;
|
||||
case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS:
|
||||
return usbdk_do_iso_transfer(itransfer);
|
||||
events = IS_XFERIN(transfer) ? POLLIN : POLLOUT;
|
||||
transfer_fn = usbdk_do_iso_transfer;
|
||||
break;
|
||||
default:
|
||||
usbi_err(TRANSFER_CTX(transfer), "unknown endpoint type %d", transfer->type);
|
||||
return LIBUSB_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
return usbdk_do_submit_transfer(itransfer, events, transfer_fn);
|
||||
}
|
||||
|
||||
static int usbdk_abort_transfers(struct usbi_transfer *itransfer)
|
||||
{
|
||||
struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer);
|
||||
struct libusb_context *ctx = DEVICE_CTX(transfer->dev_handle->dev);
|
||||
struct libusb_context *ctx = TRANSFER_CTX(transfer);
|
||||
struct usbdk_device_priv *priv = _usbdk_device_priv(transfer->dev_handle->dev);
|
||||
struct usbdk_transfer_priv *transfer_priv = _usbdk_transfer_priv(itransfer);
|
||||
struct winfd *pollable_fd = &transfer_priv->pollable_fd;
|
||||
|
||||
if (pCancelIoEx != NULL) {
|
||||
// Use CancelIoEx if available to cancel just a single transfer
|
||||
if (!pCancelIoEx(priv->system_handle, pollable_fd->overlapped)) {
|
||||
usbi_err(ctx, "CancelIoEx failed: %s", windows_error_str(0));
|
||||
return LIBUSB_ERROR_NO_DEVICE;
|
||||
}
|
||||
} else {
|
||||
if (!usbdk_helper.AbortPipe(priv->redirector_handle, transfer->endpoint)) {
|
||||
usbi_err(ctx, "AbortPipe failed: %s", windows_error_str(0));
|
||||
return LIBUSB_ERROR_NO_DEVICE;
|
||||
}
|
||||
}
|
||||
|
||||
return LIBUSB_SUCCESS;
|
||||
}
|
||||
@ -778,16 +740,16 @@ static int usbdk_cancel_transfer(struct usbi_transfer *itransfer)
|
||||
}
|
||||
}
|
||||
|
||||
int windows_copy_transfer_data(struct usbi_transfer *itransfer, uint32_t io_size)
|
||||
static int usbdk_copy_transfer_data(struct usbi_transfer *itransfer, uint32_t io_size)
|
||||
{
|
||||
itransfer->transferred += io_size;
|
||||
return LIBUSB_TRANSFER_COMPLETED;
|
||||
}
|
||||
|
||||
struct winfd *windows_get_fd(struct usbi_transfer *transfer)
|
||||
static int usbdk_get_transfer_fd(struct usbi_transfer *itransfer)
|
||||
{
|
||||
struct usbdk_transfer_priv *transfer_priv = _usbdk_transfer_priv(transfer);
|
||||
return &transfer_priv->pollable_fd;
|
||||
struct usbdk_transfer_priv *transfer_priv = _usbdk_transfer_priv(itransfer);
|
||||
return transfer_priv->pollable_fd.fd;
|
||||
}
|
||||
|
||||
static DWORD usbdk_translate_usbd_status(USBD_STATUS UsbdStatus)
|
||||
@ -796,30 +758,28 @@ static DWORD usbdk_translate_usbd_status(USBD_STATUS UsbdStatus)
|
||||
return NO_ERROR;
|
||||
|
||||
switch (UsbdStatus) {
|
||||
case USBD_STATUS_STALL_PID:
|
||||
case USBD_STATUS_ENDPOINT_HALTED:
|
||||
case USBD_STATUS_BAD_START_FRAME:
|
||||
return ERROR_GEN_FAILURE;
|
||||
case USBD_STATUS_TIMEOUT:
|
||||
return ERROR_SEM_TIMEOUT;
|
||||
case USBD_STATUS_CANCELED:
|
||||
return ERROR_OPERATION_ABORTED;
|
||||
default:
|
||||
return ERROR_FUNCTION_FAILED;
|
||||
return ERROR_GEN_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
void windows_get_overlapped_result(struct usbi_transfer *transfer, struct winfd *pollable_fd, DWORD *io_result, DWORD *io_size)
|
||||
static void usbdk_get_overlapped_result(struct usbi_transfer *itransfer, DWORD *io_result, DWORD *io_size)
|
||||
{
|
||||
if (HasOverlappedIoCompletedSync(pollable_fd->overlapped) // Handle async requests that completed synchronously first
|
||||
|| GetOverlappedResult(pollable_fd->handle, pollable_fd->overlapped, io_size, false)) { // Regular async overlapped
|
||||
struct libusb_transfer *ltransfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer);
|
||||
struct usbdk_transfer_priv *transfer_priv = _usbdk_transfer_priv(transfer);
|
||||
struct usbdk_transfer_priv *transfer_priv = _usbdk_transfer_priv(itransfer);
|
||||
struct winfd *pollable_fd = &transfer_priv->pollable_fd;
|
||||
|
||||
if (ltransfer->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS) {
|
||||
int i;
|
||||
if (HasOverlappedIoCompletedSync(pollable_fd->overlapped) // Handle async requests that completed synchronously first
|
||||
|| 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++) {
|
||||
struct libusb_iso_packet_descriptor *lib_desc = <ransfer->iso_packet_desc[i];
|
||||
struct libusb_iso_packet_descriptor *lib_desc = &transfer->iso_packet_desc[i];
|
||||
|
||||
switch (transfer_priv->IsochronousResultsArray[i].TransferResult) {
|
||||
case STATUS_SUCCESS:
|
||||
@ -838,68 +798,33 @@ void windows_get_overlapped_result(struct usbi_transfer *transfer, struct winfd
|
||||
|
||||
*io_size = (DWORD)transfer_priv->request.Result.GenResult.BytesTransferred;
|
||||
*io_result = usbdk_translate_usbd_status((USBD_STATUS)transfer_priv->request.Result.GenResult.UsbdStatus);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
*io_result = GetLastError();
|
||||
}
|
||||
}
|
||||
|
||||
static int usbdk_clock_gettime(int clk_id, struct timespec *tp)
|
||||
{
|
||||
return windows_clock_gettime(clk_id, tp);
|
||||
}
|
||||
|
||||
const struct usbi_os_backend usbdk_backend = {
|
||||
"Windows",
|
||||
USBI_CAP_HAS_HID_ACCESS,
|
||||
const struct windows_backend usbdk_backend = {
|
||||
usbdk_init,
|
||||
usbdk_exit,
|
||||
|
||||
usbdk_get_device_list,
|
||||
NULL,
|
||||
usbdk_open,
|
||||
usbdk_close,
|
||||
|
||||
usbdk_get_device_descriptor,
|
||||
usbdk_get_active_config_descriptor,
|
||||
usbdk_get_config_descriptor,
|
||||
NULL,
|
||||
|
||||
usbdk_get_config_descriptor_by_value,
|
||||
usbdk_get_configuration,
|
||||
usbdk_set_configuration,
|
||||
usbdk_claim_interface,
|
||||
usbdk_release_interface,
|
||||
|
||||
usbdk_set_interface_altsetting,
|
||||
usbdk_clear_halt,
|
||||
usbdk_reset_device,
|
||||
|
||||
NULL,
|
||||
NULL,
|
||||
|
||||
NULL, // dev_mem_alloc()
|
||||
NULL, // dev_mem_free()
|
||||
|
||||
usbdk_kernel_driver_active,
|
||||
usbdk_detach_kernel_driver,
|
||||
usbdk_attach_kernel_driver,
|
||||
|
||||
usbdk_destroy_device,
|
||||
|
||||
usbdk_submit_transfer,
|
||||
usbdk_cancel_transfer,
|
||||
windows_clear_transfer_priv,
|
||||
|
||||
windows_handle_events,
|
||||
NULL,
|
||||
|
||||
usbdk_clock_gettime,
|
||||
#if defined(USBI_TIMERFD_AVAILABLE)
|
||||
NULL,
|
||||
#endif
|
||||
sizeof(struct usbdk_device_priv),
|
||||
0,
|
||||
sizeof(struct usbdk_transfer_priv),
|
||||
usbdk_clear_transfer_priv,
|
||||
usbdk_copy_transfer_data,
|
||||
usbdk_get_transfer_fd,
|
||||
usbdk_get_overlapped_result,
|
||||
};
|
||||
|
||||
#endif /* USE_USBDK */
|
@ -23,56 +23,13 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
typedef struct tag_USB_DK_DEVICE_ID {
|
||||
WCHAR DeviceID[MAX_DEVICE_ID_LEN];
|
||||
WCHAR InstanceID[MAX_DEVICE_ID_LEN];
|
||||
} USB_DK_DEVICE_ID, *PUSB_DK_DEVICE_ID;
|
||||
#include "windows_nt_common.h"
|
||||
|
||||
static inline void UsbDkFillIDStruct(USB_DK_DEVICE_ID *ID, PCWCHAR DeviceID, PCWCHAR InstanceID)
|
||||
{
|
||||
wcsncpy_s(ID->DeviceID, DeviceID, MAX_DEVICE_ID_LEN);
|
||||
wcsncpy_s(ID->InstanceID, InstanceID, MAX_DEVICE_ID_LEN);
|
||||
}
|
||||
|
||||
typedef struct tag_USB_DK_DEVICE_INFO {
|
||||
USB_DK_DEVICE_ID ID;
|
||||
ULONG64 FilterID;
|
||||
ULONG64 Port;
|
||||
ULONG64 Speed;
|
||||
USB_DEVICE_DESCRIPTOR DeviceDescriptor;
|
||||
} USB_DK_DEVICE_INFO, *PUSB_DK_DEVICE_INFO;
|
||||
|
||||
typedef struct tag_USB_DK_CONFIG_DESCRIPTOR_REQUEST {
|
||||
typedef struct USB_DK_CONFIG_DESCRIPTOR_REQUEST {
|
||||
USB_DK_DEVICE_ID ID;
|
||||
ULONG64 Index;
|
||||
} USB_DK_CONFIG_DESCRIPTOR_REQUEST, *PUSB_DK_CONFIG_DESCRIPTOR_REQUEST;
|
||||
|
||||
typedef struct tag_USB_DK_ISO_TRANSFER_RESULT {
|
||||
ULONG64 ActualLength;
|
||||
ULONG64 TransferResult;
|
||||
} USB_DK_ISO_TRANSFER_RESULT, *PUSB_DK_ISO_TRANSFER_RESULT;
|
||||
|
||||
typedef struct tag_USB_DK_GEN_TRANSFER_RESULT {
|
||||
ULONG64 BytesTransferred;
|
||||
ULONG64 UsbdStatus; // USBD_STATUS code
|
||||
} USB_DK_GEN_TRANSFER_RESULT, *PUSB_DK_GEN_TRANSFER_RESULT;
|
||||
|
||||
typedef struct tag_USB_DK_TRANSFER_RESULT {
|
||||
USB_DK_GEN_TRANSFER_RESULT GenResult;
|
||||
PVOID64 IsochronousResultsArray; // array of USB_DK_ISO_TRANSFER_RESULT
|
||||
} USB_DK_TRANSFER_RESULT, *PUSB_DK_TRANSFER_RESULT;
|
||||
|
||||
typedef struct tag_USB_DK_TRANSFER_REQUEST {
|
||||
ULONG64 EndpointAddress;
|
||||
PVOID64 Buffer;
|
||||
ULONG64 BufferLength;
|
||||
ULONG64 TransferType;
|
||||
ULONG64 IsochronousPacketsArraySize;
|
||||
PVOID64 IsochronousPacketsArray;
|
||||
|
||||
USB_DK_TRANSFER_RESULT Result;
|
||||
} USB_DK_TRANSFER_REQUEST, *PUSB_DK_TRANSFER_REQUEST;
|
||||
|
||||
typedef enum {
|
||||
TransferFailure = 0,
|
||||
TransferSuccess,
|
||||
@ -90,7 +47,7 @@ typedef enum {
|
||||
typedef enum {
|
||||
ControlTransferType,
|
||||
BulkTransferType,
|
||||
IntertuptTransferType,
|
||||
InterruptTransferType,
|
||||
IsochronousTransferType
|
||||
} USB_DK_TRANSFER_TYPE;
|
||||
|
3009
vendor/github.com/karalabe/usb/libusb/libusb/os/windows_winusb.c
generated
vendored
Normal file
3009
vendor/github.com/karalabe/usb/libusb/libusb/os/windows_winusb.c
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@ -36,15 +36,14 @@
|
||||
#endif
|
||||
|
||||
// Missing from MSVC6 setupapi.h
|
||||
#if !defined(SPDRP_ADDRESS)
|
||||
#ifndef SPDRP_ADDRESS
|
||||
#define SPDRP_ADDRESS 28
|
||||
#endif
|
||||
#if !defined(SPDRP_INSTALL_STATE)
|
||||
#ifndef SPDRP_INSTALL_STATE
|
||||
#define SPDRP_INSTALL_STATE 34
|
||||
#endif
|
||||
|
||||
#define MAX_CTRL_BUFFER_LENGTH 4096
|
||||
#define MAX_USB_DEVICES 256
|
||||
#define MAX_USB_STRING_LENGTH 128
|
||||
#define MAX_HID_REPORT_SIZE 1024
|
||||
#define MAX_HID_DESCRIPTOR_SIZE 256
|
||||
@ -61,16 +60,16 @@
|
||||
// http://msdn.microsoft.com/en-us/library/ff545978.aspx
|
||||
// http://msdn.microsoft.com/en-us/library/ff545972.aspx
|
||||
// http://msdn.microsoft.com/en-us/library/ff545982.aspx
|
||||
#if !defined(GUID_DEVINTERFACE_USB_HOST_CONTROLLER)
|
||||
#ifndef GUID_DEVINTERFACE_USB_HOST_CONTROLLER
|
||||
const GUID GUID_DEVINTERFACE_USB_HOST_CONTROLLER = {0x3ABF6F2D, 0x71C4, 0x462A, {0x8A, 0x92, 0x1E, 0x68, 0x61, 0xE6, 0xAF, 0x27}};
|
||||
#endif
|
||||
#if !defined(GUID_DEVINTERFACE_USB_DEVICE)
|
||||
#ifndef GUID_DEVINTERFACE_USB_DEVICE
|
||||
const GUID GUID_DEVINTERFACE_USB_DEVICE = {0xA5DCBF10, 0x6530, 0x11D2, {0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED}};
|
||||
#endif
|
||||
#if !defined(GUID_DEVINTERFACE_USB_HUB)
|
||||
#ifndef GUID_DEVINTERFACE_USB_HUB
|
||||
const GUID GUID_DEVINTERFACE_USB_HUB = {0xF18A0E88, 0xC30C, 0x11D0, {0x88, 0x15, 0x00, 0xA0, 0xC9, 0x06, 0xBE, 0xD8}};
|
||||
#endif
|
||||
#if !defined(GUID_DEVINTERFACE_LIBUSB0_FILTER)
|
||||
#ifndef GUID_DEVINTERFACE_LIBUSB0_FILTER
|
||||
const GUID GUID_DEVINTERFACE_LIBUSB0_FILTER = {0xF9F3FF14, 0xAE21, 0x48A0, {0x8A, 0x25, 0x80, 0x11, 0xA7, 0xA9, 0x31, 0xD9}};
|
||||
#endif
|
||||
|
||||
@ -84,8 +83,6 @@ const GUID GUID_DEVINTERFACE_LIBUSB0_FILTER = { 0xF9F3FF14, 0xAE21, 0x48A0, {0x8
|
||||
#define USB_API_WINUSBX 3
|
||||
#define USB_API_HID 4
|
||||
#define USB_API_MAX 5
|
||||
// The following is used to indicate if the HID or composite extra props have already been set.
|
||||
#define USB_API_SET (1 << USB_API_MAX)
|
||||
|
||||
// Sub-APIs for WinUSB-like driver APIs (WinUSB, libusbK, libusb-win32 through the libusbK DLL)
|
||||
// Must have the same values as the KUSB_DRVID enum from libusbk.h
|
||||
@ -95,15 +92,13 @@ const GUID GUID_DEVINTERFACE_LIBUSB0_FILTER = { 0xF9F3FF14, 0xAE21, 0x48A0, {0x8
|
||||
#define SUB_API_WINUSB 2
|
||||
#define SUB_API_MAX 3
|
||||
|
||||
#define WINUSBX_DRV_NAMES {"libusbK", "libusb0", "WinUSB"}
|
||||
|
||||
struct windows_usb_api_backend {
|
||||
const uint8_t id;
|
||||
const char *designation;
|
||||
const char **driver_name_list; // Driver name, without .sys, e.g. "usbccgp"
|
||||
const char * const designation;
|
||||
const char * const * const driver_name_list; // Driver name, without .sys, e.g. "usbccgp"
|
||||
const uint8_t nb_driver_names;
|
||||
int (*init)(int sub_api, struct libusb_context *ctx);
|
||||
int (*exit)(int sub_api);
|
||||
int (*init)(struct libusb_context *ctx);
|
||||
void (*exit)(void);
|
||||
int (*open)(int sub_api, struct libusb_device_handle *dev_handle);
|
||||
void (*close)(int sub_api, struct libusb_device_handle *dev_handle);
|
||||
int (*configure_endpoints)(int sub_api, struct libusb_device_handle *dev_handle, int iface);
|
||||
@ -123,9 +118,16 @@ struct windows_usb_api_backend {
|
||||
extern const struct windows_usb_api_backend usb_api_backend[USB_API_MAX];
|
||||
|
||||
#define PRINT_UNSUPPORTED_API(fname) \
|
||||
usbi_dbg("unsupported API call for '" \
|
||||
#fname "' (unrecognized device driver)"); \
|
||||
return LIBUSB_ERROR_NOT_SUPPORTED;
|
||||
usbi_dbg("unsupported API call for '%s' " \
|
||||
"(unrecognized device driver)", #fname)
|
||||
|
||||
#define CHECK_SUPPORTED_API(apip, fname) \
|
||||
do { \
|
||||
if ((apip)->fname == NULL) { \
|
||||
PRINT_UNSUPPORTED_API(fname); \
|
||||
return LIBUSB_ERROR_NOT_SUPPORTED; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* private structures definition
|
||||
@ -154,11 +156,12 @@ struct libusb_hid_descriptor {
|
||||
#define LIBUSB_REQ_IN(request_type) ((request_type) & LIBUSB_ENDPOINT_IN)
|
||||
#define LIBUSB_REQ_OUT(request_type) (!LIBUSB_REQ_IN(request_type))
|
||||
|
||||
#ifndef CTL_CODE
|
||||
#define CTL_CODE(DeviceType, Function, Method, Access) \
|
||||
(((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method))
|
||||
#endif
|
||||
|
||||
// The following are used for HID reports IOCTLs
|
||||
#define HID_CTL_CODE(id) \
|
||||
CTL_CODE (FILE_DEVICE_KEYBOARD, (id), METHOD_NEITHER, FILE_ANY_ACCESS)
|
||||
#define HID_BUFFER_CTL_CODE(id) \
|
||||
CTL_CODE (FILE_DEVICE_KEYBOARD, (id), METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define HID_IN_CTL_CODE(id) \
|
||||
CTL_CODE(FILE_DEVICE_KEYBOARD, (id), METHOD_IN_DIRECT, FILE_ANY_ACCESS)
|
||||
#define HID_OUT_CTL_CODE(id) \
|
||||
@ -193,39 +196,20 @@ struct hid_device_priv {
|
||||
uint16_t input_report_size;
|
||||
uint16_t output_report_size;
|
||||
uint16_t feature_report_size;
|
||||
uint16_t usage;
|
||||
uint16_t usagePage;
|
||||
WCHAR string[3][MAX_USB_STRING_LENGTH];
|
||||
uint8_t string_index[3]; // man, prod, ser
|
||||
};
|
||||
|
||||
struct windows_device_priv {
|
||||
uint8_t depth; // distance to HCD
|
||||
uint8_t port; // port number on the hub
|
||||
uint8_t active_config;
|
||||
struct windows_usb_api_backend const *apib;
|
||||
char *path; // device interface path
|
||||
int sub_api; // for WinUSB-like APIs
|
||||
struct {
|
||||
char *path; // each interface needs a device interface path,
|
||||
struct windows_usb_api_backend const *apib; // an API backend (multiple drivers support),
|
||||
int sub_api;
|
||||
int8_t nb_endpoints; // and a set of endpoint addresses (USB_MAXENDPOINTS)
|
||||
uint8_t *endpoint;
|
||||
bool restricted_functionality; // indicates if the interface functionality is restricted
|
||||
// by Windows (eg. HID keyboards or mice cannot do R/W)
|
||||
} usb_interface[USB_MAXINTERFACES];
|
||||
struct hid_device_priv *hid;
|
||||
USB_DEVICE_DESCRIPTOR dev_descriptor;
|
||||
unsigned char **config_descriptor; // list of pointers to the cached config descriptors
|
||||
};
|
||||
|
||||
static inline struct windows_device_priv *_device_priv(struct libusb_device *dev)
|
||||
static inline struct winusb_device_priv *_device_priv(struct libusb_device *dev)
|
||||
{
|
||||
return (struct windows_device_priv *)dev->os_priv;
|
||||
return (struct winusb_device_priv *)dev->os_priv;
|
||||
}
|
||||
|
||||
static inline struct windows_device_priv *windows_device_priv_init(struct libusb_device *dev)
|
||||
static inline struct winusb_device_priv *winusb_device_priv_init(struct libusb_device *dev)
|
||||
{
|
||||
struct windows_device_priv *p = _device_priv(dev);
|
||||
struct winusb_device_priv *p = _device_priv(dev);
|
||||
int i;
|
||||
|
||||
p->apib = &usb_api_backend[USB_API_UNSUPPORTED];
|
||||
@ -238,11 +222,12 @@ static inline struct windows_device_priv *windows_device_priv_init(struct libusb
|
||||
return p;
|
||||
}
|
||||
|
||||
static inline void windows_device_priv_release(struct libusb_device *dev)
|
||||
static inline void winusb_device_priv_release(struct libusb_device *dev)
|
||||
{
|
||||
struct windows_device_priv *p = _device_priv(dev);
|
||||
struct winusb_device_priv *p = _device_priv(dev);
|
||||
int i;
|
||||
|
||||
free(p->dev_id);
|
||||
free(p->path);
|
||||
if ((dev->num_configurations > 0) && (p->config_descriptor != NULL)) {
|
||||
for (i = 0; i < dev->num_configurations; i++)
|
||||
@ -256,32 +241,12 @@ static inline void windows_device_priv_release(struct libusb_device *dev)
|
||||
}
|
||||
}
|
||||
|
||||
struct interface_handle_t {
|
||||
HANDLE dev_handle; // WinUSB needs an extra handle for the file
|
||||
HANDLE api_handle; // used by the API to communicate with the device
|
||||
};
|
||||
|
||||
struct windows_device_handle_priv {
|
||||
int active_interface;
|
||||
struct interface_handle_t interface_handle[USB_MAXINTERFACES];
|
||||
int autoclaim_count[USB_MAXINTERFACES]; // For auto-release
|
||||
};
|
||||
|
||||
static inline struct windows_device_handle_priv *_device_handle_priv(
|
||||
static inline struct winusb_device_handle_priv *_device_handle_priv(
|
||||
struct libusb_device_handle *handle)
|
||||
{
|
||||
return (struct windows_device_handle_priv *)handle->os_priv;
|
||||
return (struct winusb_device_handle_priv *)handle->os_priv;
|
||||
}
|
||||
|
||||
// used for async polling functions
|
||||
struct windows_transfer_priv {
|
||||
struct winfd pollable_fd;
|
||||
uint8_t interface_number;
|
||||
uint8_t *hid_buffer; // 1 byte extended data buffer, required for HID
|
||||
uint8_t *hid_dest; // transfer buffer destination, required for HID
|
||||
size_t hid_expected_size;
|
||||
};
|
||||
|
||||
// used to match a device driver (including filter drivers) against a supported API
|
||||
struct driver_lookup {
|
||||
char list[MAX_KEY_LENGTH + 1]; // REG_MULTI_SZ list of services (driver) names
|
||||
@ -289,34 +254,6 @@ struct driver_lookup {
|
||||
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
|
||||
*/
|
||||
@ -326,57 +263,63 @@ typedef DWORD RETURN_TYPE;
|
||||
typedef RETURN_TYPE CONFIGRET;
|
||||
|
||||
#define CR_SUCCESS 0x00000000
|
||||
#define CR_NO_SUCH_DEVNODE 0x0000000D
|
||||
|
||||
#define USB_DEVICE_DESCRIPTOR_TYPE LIBUSB_DT_DEVICE
|
||||
#define USB_CONFIGURATION_DESCRIPTOR_TYPE LIBUSB_DT_CONFIG
|
||||
#define USB_STRING_DESCRIPTOR_TYPE LIBUSB_DT_STRING
|
||||
#define USB_INTERFACE_DESCRIPTOR_TYPE LIBUSB_DT_INTERFACE
|
||||
#define USB_ENDPOINT_DESCRIPTOR_TYPE LIBUSB_DT_ENDPOINT
|
||||
/* Cfgmgr32 dependencies */
|
||||
DLL_DECLARE_HANDLE(Cfgmgr32);
|
||||
DLL_DECLARE_FUNC(WINAPI, CONFIGRET, CM_Get_Parent, (PDEVINST, DEVINST, ULONG));
|
||||
DLL_DECLARE_FUNC(WINAPI, CONFIGRET, CM_Get_Child, (PDEVINST, DEVINST, ULONG));
|
||||
|
||||
#define USB_REQUEST_GET_STATUS LIBUSB_REQUEST_GET_STATUS
|
||||
#define USB_REQUEST_CLEAR_FEATURE LIBUSB_REQUEST_CLEAR_FEATURE
|
||||
#define USB_REQUEST_SET_FEATURE LIBUSB_REQUEST_SET_FEATURE
|
||||
#define USB_REQUEST_SET_ADDRESS LIBUSB_REQUEST_SET_ADDRESS
|
||||
#define USB_REQUEST_GET_DESCRIPTOR LIBUSB_REQUEST_GET_DESCRIPTOR
|
||||
#define USB_REQUEST_SET_DESCRIPTOR LIBUSB_REQUEST_SET_DESCRIPTOR
|
||||
#define USB_REQUEST_GET_CONFIGURATION LIBUSB_REQUEST_GET_CONFIGURATION
|
||||
#define USB_REQUEST_SET_CONFIGURATION LIBUSB_REQUEST_SET_CONFIGURATION
|
||||
#define USB_REQUEST_GET_INTERFACE LIBUSB_REQUEST_GET_INTERFACE
|
||||
#define USB_REQUEST_SET_INTERFACE LIBUSB_REQUEST_SET_INTERFACE
|
||||
#define USB_REQUEST_SYNC_FRAME LIBUSB_REQUEST_SYNCH_FRAME
|
||||
/* 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));
|
||||
|
||||
#define USB_GET_NODE_INFORMATION 258
|
||||
/* OLE32 dependency */
|
||||
DLL_DECLARE_HANDLE(OLE32);
|
||||
DLL_DECLARE_FUNC_PREFIXED(WINAPI, HRESULT, p, IIDFromString, (LPCOLESTR, LPIID));
|
||||
|
||||
/* SetupAPI dependencies */
|
||||
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
|
||||
#define USB_GET_NODE_CONNECTION_NAME 261
|
||||
#define USB_GET_HUB_CAPABILITIES 271
|
||||
#if !defined(USB_GET_NODE_CONNECTION_INFORMATION_EX)
|
||||
#endif
|
||||
#ifndef USB_GET_NODE_CONNECTION_INFORMATION_EX
|
||||
#define USB_GET_NODE_CONNECTION_INFORMATION_EX 274
|
||||
#endif
|
||||
#if !defined(USB_GET_HUB_CAPABILITIES_EX)
|
||||
#define USB_GET_HUB_CAPABILITIES_EX 276
|
||||
#endif
|
||||
#if !defined(USB_GET_NODE_CONNECTION_INFORMATION_EX_V2)
|
||||
#ifndef USB_GET_NODE_CONNECTION_INFORMATION_EX_V2
|
||||
#define USB_GET_NODE_CONNECTION_INFORMATION_EX_V2 279
|
||||
#endif
|
||||
|
||||
#ifndef METHOD_BUFFERED
|
||||
#define METHOD_BUFFERED 0
|
||||
#endif
|
||||
#ifndef FILE_ANY_ACCESS
|
||||
#define FILE_ANY_ACCESS 0x00000000
|
||||
#endif
|
||||
#ifndef FILE_DEVICE_UNKNOWN
|
||||
#define FILE_DEVICE_UNKNOWN 0x00000022
|
||||
#endif
|
||||
#ifndef FILE_DEVICE_USB
|
||||
#define FILE_DEVICE_USB FILE_DEVICE_UNKNOWN
|
||||
#endif
|
||||
|
||||
#ifndef CTL_CODE
|
||||
#define CTL_CODE(DeviceType, Function, Method, Access) \
|
||||
(((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method))
|
||||
#endif
|
||||
#define USB_CTL_CODE(id) \
|
||||
CTL_CODE(FILE_DEVICE_USB, (id), METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
|
||||
#define IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION \
|
||||
USB_CTL_CODE(USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION)
|
||||
|
||||
#define IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX \
|
||||
USB_CTL_CODE(USB_GET_NODE_CONNECTION_INFORMATION_EX)
|
||||
|
||||
#define IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX_V2 \
|
||||
USB_CTL_CODE(USB_GET_NODE_CONNECTION_INFORMATION_EX_V2)
|
||||
|
||||
typedef enum USB_CONNECTION_STATUS {
|
||||
NoDeviceConnected,
|
||||
@ -395,42 +338,25 @@ typedef enum USB_HUB_NODE {
|
||||
UsbMIParent
|
||||
} USB_HUB_NODE;
|
||||
|
||||
/* Cfgmgr32.dll interface */
|
||||
DLL_DECLARE_HANDLE(Cfgmgr32);
|
||||
DLL_DECLARE_FUNC(WINAPI, CONFIGRET, CM_Get_Parent, (PDEVINST, DEVINST, ULONG));
|
||||
DLL_DECLARE_FUNC(WINAPI, CONFIGRET, CM_Get_Child, (PDEVINST, DEVINST, ULONG));
|
||||
DLL_DECLARE_FUNC(WINAPI, CONFIGRET, CM_Get_Sibling, (PDEVINST, DEVINST, ULONG));
|
||||
DLL_DECLARE_FUNC(WINAPI, CONFIGRET, CM_Get_Device_IDA, (DEVINST, PCHAR, ULONG, ULONG));
|
||||
|
||||
#define IOCTL_USB_GET_HUB_CAPABILITIES_EX \
|
||||
CTL_CODE( FILE_DEVICE_USB, USB_GET_HUB_CAPABILITIES_EX, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
|
||||
#define IOCTL_USB_GET_HUB_CAPABILITIES \
|
||||
CTL_CODE(FILE_DEVICE_USB, USB_GET_HUB_CAPABILITIES, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
|
||||
#define IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION \
|
||||
CTL_CODE(FILE_DEVICE_USB, USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
|
||||
#define IOCTL_USB_GET_ROOT_HUB_NAME \
|
||||
CTL_CODE(FILE_DEVICE_USB, HCD_GET_ROOT_HUB_NAME, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
|
||||
#define IOCTL_USB_GET_NODE_INFORMATION \
|
||||
CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_INFORMATION, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
|
||||
#define IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX \
|
||||
CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_CONNECTION_INFORMATION_EX, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
|
||||
#define IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX_V2 \
|
||||
CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_CONNECTION_INFORMATION_EX_V2, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
|
||||
#define IOCTL_USB_GET_NODE_CONNECTION_ATTRIBUTES \
|
||||
CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_CONNECTION_ATTRIBUTES, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
|
||||
#define IOCTL_USB_GET_NODE_CONNECTION_NAME \
|
||||
CTL_CODE(FILE_DEVICE_USB, USB_GET_NODE_CONNECTION_NAME, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
|
||||
// Most of the structures below need to be packed
|
||||
#pragma pack(push, 1)
|
||||
#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 {
|
||||
UCHAR bLength;
|
||||
@ -444,103 +370,7 @@ typedef struct USB_INTERFACE_DESCRIPTOR {
|
||||
UCHAR iInterface;
|
||||
} USB_INTERFACE_DESCRIPTOR, *PUSB_INTERFACE_DESCRIPTOR;
|
||||
|
||||
typedef struct USB_CONFIGURATION_DESCRIPTOR_SHORT {
|
||||
struct {
|
||||
ULONG ConnectionIndex;
|
||||
struct {
|
||||
UCHAR bmRequest;
|
||||
UCHAR bRequest;
|
||||
USHORT wValue;
|
||||
USHORT wIndex;
|
||||
USHORT wLength;
|
||||
} SetupPacket;
|
||||
} req;
|
||||
USB_CONFIGURATION_DESCRIPTOR data;
|
||||
} USB_CONFIGURATION_DESCRIPTOR_SHORT;
|
||||
|
||||
typedef struct USB_ENDPOINT_DESCRIPTOR {
|
||||
UCHAR bLength;
|
||||
UCHAR bDescriptorType;
|
||||
UCHAR bEndpointAddress;
|
||||
UCHAR bmAttributes;
|
||||
USHORT wMaxPacketSize;
|
||||
UCHAR bInterval;
|
||||
} USB_ENDPOINT_DESCRIPTOR, *PUSB_ENDPOINT_DESCRIPTOR;
|
||||
|
||||
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 {
|
||||
typedef struct _USB_NODE_CONNECTION_INFORMATION_EX {
|
||||
ULONG ConnectionIndex;
|
||||
USB_DEVICE_DESCRIPTOR DeviceDescriptor;
|
||||
UCHAR CurrentConfigurationValue;
|
||||
@ -578,25 +408,7 @@ typedef struct _USB_NODE_CONNECTION_INFORMATION_EX_V2 {
|
||||
USB_NODE_CONNECTION_INFORMATION_EX_V2_FLAGS Flags;
|
||||
} USB_NODE_CONNECTION_INFORMATION_EX_V2, *PUSB_NODE_CONNECTION_INFORMATION_EX_V2;
|
||||
|
||||
typedef struct USB_HUB_CAP_FLAGS {
|
||||
ULONG HubIsHighSpeedCapable:1;
|
||||
ULONG HubIsHighSpeed:1;
|
||||
ULONG HubIsMultiTtCapable:1;
|
||||
ULONG HubIsMultiTt:1;
|
||||
ULONG HubIsRoot:1;
|
||||
ULONG HubIsArmedWakeOnConnect:1;
|
||||
ULONG ReservedMBZ:26;
|
||||
} USB_HUB_CAP_FLAGS, *PUSB_HUB_CAP_FLAGS;
|
||||
|
||||
typedef struct USB_HUB_CAPABILITIES {
|
||||
ULONG HubIs2xCapable:1;
|
||||
} USB_HUB_CAPABILITIES, *PUSB_HUB_CAPABILITIES;
|
||||
|
||||
typedef struct USB_HUB_CAPABILITIES_EX {
|
||||
USB_HUB_CAP_FLAGS CapabilityFlags;
|
||||
} USB_HUB_CAPABILITIES_EX, *PUSB_HUB_CAPABILITIES_EX;
|
||||
|
||||
#pragma pack(pop)
|
||||
#include <poppack.h>
|
||||
|
||||
/* winusb.dll interface */
|
||||
|
||||
@ -608,36 +420,25 @@ typedef struct USB_HUB_CAPABILITIES_EX {
|
||||
#define AUTO_FLUSH 0x06
|
||||
#define RAW_IO 0x07
|
||||
#define MAXIMUM_TRANSFER_SIZE 0x08
|
||||
#define AUTO_SUSPEND 0x81
|
||||
#define SUSPEND_DELAY 0x83
|
||||
#define DEVICE_SPEED 0x01
|
||||
#define LowSpeed 0x01
|
||||
#define FullSpeed 0x02
|
||||
#define HighSpeed 0x03
|
||||
|
||||
typedef enum USBD_PIPE_TYPE {
|
||||
typedef enum _USBD_PIPE_TYPE {
|
||||
UsbdPipeTypeControl,
|
||||
UsbdPipeTypeIsochronous,
|
||||
UsbdPipeTypeBulk,
|
||||
UsbdPipeTypeInterrupt
|
||||
} USBD_PIPE_TYPE;
|
||||
|
||||
typedef struct {
|
||||
USBD_PIPE_TYPE PipeType;
|
||||
UCHAR PipeId;
|
||||
USHORT MaximumPacketSize;
|
||||
UCHAR Interval;
|
||||
} WINUSB_PIPE_INFORMATION, *PWINUSB_PIPE_INFORMATION;
|
||||
#include <pshpack1.h>
|
||||
|
||||
#pragma pack(1)
|
||||
typedef struct {
|
||||
UCHAR request_type;
|
||||
UCHAR request;
|
||||
USHORT value;
|
||||
USHORT index;
|
||||
USHORT length;
|
||||
typedef struct _WINUSB_SETUP_PACKET {
|
||||
UCHAR RequestType;
|
||||
UCHAR Request;
|
||||
USHORT Value;
|
||||
USHORT Index;
|
||||
USHORT Length;
|
||||
} WINUSB_SETUP_PACKET, *PWINUSB_SETUP_PACKET;
|
||||
#pragma pack()
|
||||
|
||||
#include <poppack.h>
|
||||
|
||||
typedef void *WINUSB_INTERFACE_HANDLE, *PWINUSB_INTERFACE_HANDLE;
|
||||
|
||||
@ -665,59 +466,10 @@ typedef BOOL (WINAPI *WinUsb_GetAssociatedInterface_t)(
|
||||
UCHAR AssociatedInterfaceIndex,
|
||||
PWINUSB_INTERFACE_HANDLE AssociatedInterfaceHandle
|
||||
);
|
||||
typedef BOOL (WINAPI *WinUsb_GetCurrentAlternateSetting_t)(
|
||||
WINUSB_INTERFACE_HANDLE InterfaceHandle,
|
||||
PUCHAR AlternateSetting
|
||||
);
|
||||
typedef BOOL (WINAPI *WinUsb_GetDescriptor_t)(
|
||||
WINUSB_INTERFACE_HANDLE InterfaceHandle,
|
||||
UCHAR DescriptorType,
|
||||
UCHAR Index,
|
||||
USHORT LanguageID,
|
||||
PUCHAR Buffer,
|
||||
ULONG BufferLength,
|
||||
PULONG LengthTransferred
|
||||
);
|
||||
typedef BOOL (WINAPI *WinUsb_GetOverlappedResult_t)(
|
||||
WINUSB_INTERFACE_HANDLE InterfaceHandle,
|
||||
LPOVERLAPPED lpOverlapped,
|
||||
LPDWORD lpNumberOfBytesTransferred,
|
||||
BOOL bWait
|
||||
);
|
||||
typedef BOOL (WINAPI *WinUsb_GetPipePolicy_t)(
|
||||
WINUSB_INTERFACE_HANDLE InterfaceHandle,
|
||||
UCHAR PipeID,
|
||||
ULONG PolicyType,
|
||||
PULONG ValueLength,
|
||||
PVOID Value
|
||||
);
|
||||
typedef BOOL (WINAPI *WinUsb_GetPowerPolicy_t)(
|
||||
WINUSB_INTERFACE_HANDLE InterfaceHandle,
|
||||
ULONG PolicyType,
|
||||
PULONG ValueLength,
|
||||
PVOID Value
|
||||
);
|
||||
typedef BOOL (WINAPI *WinUsb_Initialize_t)(
|
||||
HANDLE DeviceHandle,
|
||||
PWINUSB_INTERFACE_HANDLE InterfaceHandle
|
||||
);
|
||||
typedef BOOL (WINAPI *WinUsb_QueryDeviceInformation_t)(
|
||||
WINUSB_INTERFACE_HANDLE InterfaceHandle,
|
||||
ULONG InformationType,
|
||||
PULONG BufferLength,
|
||||
PVOID Buffer
|
||||
);
|
||||
typedef BOOL (WINAPI *WinUsb_QueryInterfaceSettings_t)(
|
||||
WINUSB_INTERFACE_HANDLE InterfaceHandle,
|
||||
UCHAR AlternateSettingNumber,
|
||||
PUSB_INTERFACE_DESCRIPTOR UsbAltInterfaceDescriptor
|
||||
);
|
||||
typedef BOOL (WINAPI *WinUsb_QueryPipe_t)(
|
||||
WINUSB_INTERFACE_HANDLE InterfaceHandle,
|
||||
UCHAR AlternateInterfaceNumber,
|
||||
UCHAR PipeIndex,
|
||||
PWINUSB_PIPE_INFORMATION PipeInformation
|
||||
);
|
||||
typedef BOOL (WINAPI *WinUsb_ReadPipe_t)(
|
||||
WINUSB_INTERFACE_HANDLE InterfaceHandle,
|
||||
UCHAR PipeID,
|
||||
@ -726,6 +478,9 @@ typedef BOOL (WINAPI *WinUsb_ReadPipe_t)(
|
||||
PULONG LengthTransferred,
|
||||
LPOVERLAPPED Overlapped
|
||||
);
|
||||
typedef BOOL (WINAPI *WinUsb_ResetDevice_t)(
|
||||
WINUSB_INTERFACE_HANDLE InterfaceHandle
|
||||
);
|
||||
typedef BOOL (WINAPI *WinUsb_ResetPipe_t)(
|
||||
WINUSB_INTERFACE_HANDLE InterfaceHandle,
|
||||
UCHAR PipeID
|
||||
@ -741,12 +496,6 @@ typedef BOOL (WINAPI *WinUsb_SetPipePolicy_t)(
|
||||
ULONG ValueLength,
|
||||
PVOID Value
|
||||
);
|
||||
typedef BOOL (WINAPI *WinUsb_SetPowerPolicy_t)(
|
||||
WINUSB_INTERFACE_HANDLE InterfaceHandle,
|
||||
ULONG PolicyType,
|
||||
ULONG ValueLength,
|
||||
PVOID Value
|
||||
);
|
||||
typedef BOOL (WINAPI *WinUsb_WritePipe_t)(
|
||||
WINUSB_INTERFACE_HANDLE InterfaceHandle,
|
||||
UCHAR PipeID,
|
||||
@ -755,9 +504,6 @@ typedef BOOL (WINAPI *WinUsb_WritePipe_t)(
|
||||
PULONG LengthTransferred,
|
||||
LPOVERLAPPED Overlapped
|
||||
);
|
||||
typedef BOOL (WINAPI *WinUsb_ResetDevice_t)(
|
||||
WINUSB_INTERFACE_HANDLE InterfaceHandle
|
||||
);
|
||||
|
||||
/* /!\ These must match the ones from the official libusbk.h */
|
||||
typedef enum _KUSB_FNID {
|
||||
@ -803,8 +549,7 @@ typedef struct _KLIB_VERSION {
|
||||
INT Minor;
|
||||
INT Micro;
|
||||
INT Nano;
|
||||
} KLIB_VERSION;
|
||||
typedef KLIB_VERSION* PKLIB_VERSION;
|
||||
} KLIB_VERSION, *PKLIB_VERSION;
|
||||
|
||||
typedef BOOL (WINAPI *LibK_GetProcAddress_t)(
|
||||
PVOID *ProcAddress,
|
||||
@ -816,29 +561,63 @@ typedef VOID (WINAPI *LibK_GetVersion_t)(
|
||||
PKLIB_VERSION Version
|
||||
);
|
||||
|
||||
//KISO_PACKET is equivalent of libusb_iso_packet_descriptor except uses absolute "offset" field instead of sequential Lengths
|
||||
typedef struct _KISO_PACKET {
|
||||
UINT offset;
|
||||
USHORT actual_length; //changed from libusbk_shared.h "Length" for clarity
|
||||
USHORT status;
|
||||
} KISO_PACKET, *PKISO_PACKET;
|
||||
|
||||
typedef enum _KISO_FLAG {
|
||||
KISO_FLAG_NONE = 0,
|
||||
KISO_FLAG_SET_START_FRAME = 0x00000001,
|
||||
} KISO_FLAG;
|
||||
|
||||
//KISO_CONTEXT is the conceptual equivalent of libusb_transfer except is isochronous-specific and must match libusbk's version
|
||||
typedef struct _KISO_CONTEXT {
|
||||
KISO_FLAG Flags;
|
||||
UINT StartFrame;
|
||||
SHORT ErrorCount;
|
||||
SHORT NumberOfPackets;
|
||||
UINT UrbHdrStatus;
|
||||
KISO_PACKET IsoPackets[0];
|
||||
} KISO_CONTEXT, *PKISO_CONTEXT;
|
||||
|
||||
typedef BOOL(WINAPI *WinUsb_IsoReadPipe_t)(
|
||||
WINUSB_INTERFACE_HANDLE InterfaceHandle,
|
||||
UCHAR PipeID,
|
||||
PUCHAR Buffer,
|
||||
ULONG BufferLength,
|
||||
LPOVERLAPPED Overlapped,
|
||||
PKISO_CONTEXT IsoContext
|
||||
);
|
||||
|
||||
typedef BOOL(WINAPI *WinUsb_IsoWritePipe_t)(
|
||||
WINUSB_INTERFACE_HANDLE InterfaceHandle,
|
||||
UCHAR PipeID,
|
||||
PUCHAR Buffer,
|
||||
ULONG BufferLength,
|
||||
LPOVERLAPPED Overlapped,
|
||||
PKISO_CONTEXT IsoContext
|
||||
);
|
||||
|
||||
struct winusb_interface {
|
||||
bool initialized;
|
||||
bool CancelIoEx_supported;
|
||||
WinUsb_AbortPipe_t AbortPipe;
|
||||
WinUsb_ControlTransfer_t ControlTransfer;
|
||||
WinUsb_FlushPipe_t FlushPipe;
|
||||
WinUsb_Free_t Free;
|
||||
WinUsb_GetAssociatedInterface_t GetAssociatedInterface;
|
||||
WinUsb_GetCurrentAlternateSetting_t GetCurrentAlternateSetting;
|
||||
WinUsb_GetDescriptor_t GetDescriptor;
|
||||
WinUsb_GetOverlappedResult_t GetOverlappedResult;
|
||||
WinUsb_GetPipePolicy_t GetPipePolicy;
|
||||
WinUsb_GetPowerPolicy_t GetPowerPolicy;
|
||||
WinUsb_Initialize_t Initialize;
|
||||
WinUsb_QueryDeviceInformation_t QueryDeviceInformation;
|
||||
WinUsb_QueryInterfaceSettings_t QueryInterfaceSettings;
|
||||
WinUsb_QueryPipe_t QueryPipe;
|
||||
WinUsb_ReadPipe_t ReadPipe;
|
||||
WinUsb_ResetDevice_t ResetDevice;
|
||||
WinUsb_ResetPipe_t ResetPipe;
|
||||
WinUsb_SetCurrentAlternateSetting_t SetCurrentAlternateSetting;
|
||||
WinUsb_SetPipePolicy_t SetPipePolicy;
|
||||
WinUsb_SetPowerPolicy_t SetPowerPolicy;
|
||||
WinUsb_WritePipe_t WritePipe;
|
||||
WinUsb_ResetDevice_t ResetDevice;
|
||||
WinUsb_IsoReadPipe_t IsoReadPipe;
|
||||
WinUsb_IsoWritePipe_t IsoWritePipe;
|
||||
};
|
||||
|
||||
/* hid.dll interface */
|
||||
@ -846,34 +625,10 @@ struct winusb_interface {
|
||||
#define HIDP_STATUS_SUCCESS 0x110000
|
||||
typedef void * PHIDP_PREPARSED_DATA;
|
||||
|
||||
#pragma pack(1)
|
||||
typedef struct {
|
||||
ULONG Size;
|
||||
USHORT VendorID;
|
||||
USHORT ProductID;
|
||||
USHORT VersionNumber;
|
||||
} HIDD_ATTRIBUTES, *PHIDD_ATTRIBUTES;
|
||||
#pragma pack()
|
||||
#include <pshpack1.h>
|
||||
#include <poppack.h>
|
||||
|
||||
typedef USHORT USAGE;
|
||||
typedef struct {
|
||||
USAGE Usage;
|
||||
USAGE UsagePage;
|
||||
USHORT InputReportByteLength;
|
||||
USHORT OutputReportByteLength;
|
||||
USHORT FeatureReportByteLength;
|
||||
USHORT Reserved[17];
|
||||
USHORT NumberLinkCollectionNodes;
|
||||
USHORT NumberInputButtonCaps;
|
||||
USHORT NumberInputValueCaps;
|
||||
USHORT NumberInputDataIndices;
|
||||
USHORT NumberOutputButtonCaps;
|
||||
USHORT NumberOutputValueCaps;
|
||||
USHORT NumberOutputDataIndices;
|
||||
USHORT NumberFeatureButtonCaps;
|
||||
USHORT NumberFeatureValueCaps;
|
||||
USHORT NumberFeatureDataIndices;
|
||||
} HIDP_CAPS, *PHIDP_CAPS;
|
||||
|
||||
typedef enum _HIDP_REPORT_TYPE {
|
||||
HidP_Input,
|
||||
@ -919,19 +674,7 @@ typedef struct _HIDP_VALUE_CAPS {
|
||||
} HIDP_VALUE_CAPS, *PHIDP_VALUE_CAPS;
|
||||
|
||||
DLL_DECLARE_HANDLE(hid);
|
||||
DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_GetAttributes, (HANDLE, PHIDD_ATTRIBUTES));
|
||||
DLL_DECLARE_FUNC(WINAPI, VOID, HidD_GetHidGuid, (LPGUID));
|
||||
DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_GetPreparsedData, (HANDLE, PHIDP_PREPARSED_DATA *));
|
||||
DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_FreePreparsedData, (PHIDP_PREPARSED_DATA));
|
||||
DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_GetManufacturerString, (HANDLE, PVOID, ULONG));
|
||||
DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_GetProductString, (HANDLE, PVOID, ULONG));
|
||||
DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_GetSerialNumberString, (HANDLE, PVOID, ULONG));
|
||||
DLL_DECLARE_FUNC(WINAPI, LONG, HidP_GetCaps, (PHIDP_PREPARSED_DATA, PHIDP_CAPS));
|
||||
DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_SetNumInputBuffers, (HANDLE, ULONG));
|
||||
DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_SetFeature, (HANDLE, PVOID, ULONG));
|
||||
DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_GetFeature, (HANDLE, PVOID, ULONG));
|
||||
DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_GetPhysicalDescriptor, (HANDLE, PVOID, ULONG));
|
||||
DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_GetInputReport, (HANDLE, PVOID, ULONG));
|
||||
DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_SetOutputReport, (HANDLE, PVOID, ULONG));
|
||||
DLL_DECLARE_FUNC(WINAPI, BOOL, HidD_FlushQueue, (HANDLE));
|
||||
DLL_DECLARE_FUNC(WINAPI, BOOL, HidP_GetValueCaps, (HIDP_REPORT_TYPE, PHIDP_VALUE_CAPS, PULONG, PHIDP_PREPARSED_DATA));
|
@ -7,7 +7,7 @@
|
||||
#define LIBUSB_MINOR 0
|
||||
#endif
|
||||
#ifndef LIBUSB_MICRO
|
||||
#define LIBUSB_MICRO 21
|
||||
#define LIBUSB_MICRO 22
|
||||
#endif
|
||||
#ifndef LIBUSB_NANO
|
||||
#define LIBUSB_NANO 0
|
1
vendor/github.com/karalabe/usb/libusb/libusb/version_nano.h
generated
vendored
Normal file
1
vendor/github.com/karalabe/usb/libusb/libusb/version_nano.h
generated
vendored
Normal file
@ -0,0 +1 @@
|
||||
#define LIBUSB_NANO 11312
|
42
vendor/github.com/karalabe/usb/raw_disabled.go
generated
vendored
Normal file
42
vendor/github.com/karalabe/usb/raw_disabled.go
generated
vendored
Normal 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
253
vendor/github.com/karalabe/usb/raw_enabled.go
generated
vendored
Normal 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
74
vendor/github.com/karalabe/usb/raw_errors.go
generated
vendored
Normal 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
68
vendor/github.com/karalabe/usb/usb.go
generated
vendored
Normal 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
46
vendor/github.com/karalabe/usb/usb_disabled.go
generated
vendored
Normal 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
98
vendor/github.com/karalabe/usb/usb_enabled.go
generated
vendored
Normal 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)
|
||||
}
|
2
vendor/github.com/karalabe/hid/wchar.go → vendor/github.com/karalabe/usb/wchar.go
generated
vendored
2
vendor/github.com/karalabe/hid/wchar.go → vendor/github.com/karalabe/usb/wchar.go
generated
vendored
@ -9,7 +9,7 @@
|
||||
// +build !ios
|
||||
// +build freebsd linux darwin windows
|
||||
|
||||
package hid
|
||||
package usb
|
||||
|
||||
/*
|
||||
#include <wchar.h>
|
8
vendor/vendor.json
vendored
8
vendor/vendor.json
vendored
@ -267,10 +267,10 @@
|
||||
"revisionTime": "2017-04-30T22:20:11Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "p6UjFsx/1ACWAhsdEOWrXAHptGY=",
|
||||
"path": "github.com/karalabe/hid",
|
||||
"revision": "e40407cce1c217644c09da5415bbfb07d330ea5e",
|
||||
"revisionTime": "2019-05-28T15:16:06Z",
|
||||
"checksumSHA1": "3v8Z4/daUVp9PCcFzEGYVkPadG8=",
|
||||
"path": "github.com/karalabe/usb",
|
||||
"revision": "c012609e094b8a96375fee53cc11f1bcd5cf3aa2",
|
||||
"revisionTime": "2019-06-04T10:57:36Z",
|
||||
"tree": true
|
||||
},
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user